use sea_orm::sea_query::prelude::Utc;
use sea_orm::{NotSet, Set};
use entity::note::NoteType;
-use ui_rs::{ui_actions, UiModel};
+use ui_rs::{action, ui_actions, UiModel};
use ui_rs::ui::*;
use crate::backend::BackendHandle;
use crate::window::NoteTypeTabView;
pub kind: NoteType,
pub created: DateTimeWithTimeZone,
+ title_start: i32,
+ title_end: i32,
+
#[bind("note_type")]
pub note_type: UiInteger,
pub fn init_content(&mut self, content: &entity::notecontent::Model) {
self.content_id = content.id;
self.text.set(content.content.as_str());
+ self.update_title(content.content.as_str());
+ }
+
+ pub fn update_title(&mut self, s: &str) {
+ match generate_title(s) {
+ Some(result) => {
+ let title = result.0;
+ self.title_start = result.1 as i32;
+ self.title_end = result.1 as i32 + title.len() as i32;
+
+ // TODO: notify notebook that the title has changed
+ },
+ None => {
+ self.title_start = -1;
+ self.title_end = -1;
+ }
+ }
}
pub fn save(&self, collection_id: i32, backend: &BackendHandle) {
let content = self.text.get();
let title = match generate_title(content.as_str()) {
- Some(title) => title.to_string(),
+ Some(title) => title.0.to_string(),
None => "Note".to_string()
};
}
});
}
+
+ #[action]
+ pub fn note_text_changed(&mut self, event: &ActionEvent) {
+ if event.set {
+ return;
+ }
+
+ // check if any text in the title range has changed
+ let pos = match &event.event_type {
+ EventType::TextInsert(t) => t.pos,
+ EventType::TextDelete(t) => t.begin,
+ _ => {
+ return;
+ }
+ };
+ if self.title_end != -1 && pos > self.title_end + 1 {
+ return; // this text edit can not change the title
+ }
+
+ // TODO: we don't need the full text
+ self.update_title(self.text.get().as_str());
+ }
}
-fn generate_title(s: &str) -> Option<&str> {
+fn generate_title(s: &str) -> Option<(&str, usize)> {
for line in s.lines() {
if !line.trim().is_empty() {
- return Some(line);
+ let start = line.as_ptr() as usize - s.as_ptr() as usize;
+ return Some((line, start));
}
}
+
None
}
\ No newline at end of file
obj.textarea(|b|{
b.fill(true);
b.varname("note_text");
+ b.action("note_text_changed");
});
});
});
#![allow(dead_code)]
use std::ffi::{c_char, c_void, CStr, CString};
+use std::slice;
use crate::ui::{event, ffi, UiDouble, UiInteger, UiString, UiText, UiRange, ListSelection, UiObject, NoAppData, UiSourceList};
-use crate::ui::ffi::{UiEvent, UiSubListEventData};
+use crate::ui::ffi::{UiEvent, UiSubListEventData, UiTextChangeEventData};
+use crate::ui::text::{TextInsert, TextDelete};
pub struct Event<'a, T> {
pub obj: &'a mut UiObject<T>,
TextValue(&'a mut UiText),
DoubleValue(&'a mut UiDouble),
RangeValue(&'a mut UiRange),
+ TextInsert(&'a mut TextInsert),
+ TextDelete(&'a mut TextDelete),
ListSelection(&'a ListSelection),
ListElement,
Dnd,
TextValue(UiText),
DoubleValue(UiDouble),
RangeValue(UiRange),
+ TextInsert(TextInsert),
+ TextDelete(TextDelete),
ListSelection(ListSelection),
ListElement,
Dnd,
EventTypeData::RangeValue( UiRange { ptr: d } )
}
8 => {
+ let d: *mut ffi::UiTextChangeEventData = ptr.cast();
+ let text_change_type = unsafe { ui_text_change_event_get_type(d) };
+ match text_change_type {
+ 0 => {
+ let begin = unsafe {
+ ui_text_change_event_get_begin(d)
+ };
+ let str = unsafe {
+ let cstr: *const u8 = ui_text_change_event_get_text(d).cast();
+ let length = ui_text_change_event_get_length(d);
+ let str = str::from_utf8(slice::from_raw_parts(cstr, length as usize));
+ str.unwrap_or_else(|_| "")
+ };
+ let insert = TextInsert {
+ pos: begin,
+ text: str.to_string()
+ };
+ EventTypeData::TextInsert(insert)
+ },
+ 1 => {
+ unsafe {
+ let delete = TextDelete {
+ begin: ui_text_change_event_get_begin(d),
+ end: ui_text_change_event_get_end(d)
+ };
+ EventTypeData::TextDelete(delete)
+ }
+ },
+ _ => EventTypeData::Null
+ }
+ },
+ 9 => {
EventTypeData::ListSelection( ListSelection::from_ptr(ptr.cast()) )
}
- 9 => {
+ 10 => {
// list elm
EventTypeData::Null
}
- 10 => {
+ 11 => {
// dnd
EventTypeData::Null
}
- 11 => {
+ 12 => {
EventTypeData::SubList(SubListEvent::from_ptr(ptr.cast()))
}
- 12 => {
+ 13 => {
// filelist
EventTypeData::Null
}
EventTypeData::TextValue(t) => EventType::TextValue( t ),
EventTypeData::DoubleValue(d) => EventType::DoubleValue( d ),
EventTypeData::RangeValue(r) => EventType::RangeValue( r ),
+ EventTypeData::TextInsert(c) => EventType::TextInsert( c ),
+ EventTypeData::TextDelete(c) => EventType::TextDelete( c ),
EventTypeData::ListSelection(s) => EventType::ListSelection( s ),
EventTypeData::ListElement => EventType::ListElement,
EventTypeData::Dnd => EventType::Dnd,
fn ui_event_get_int(event: *const UiEvent) -> i32;
fn ui_event_get_set(event: *const UiEvent) -> i32;
+ fn ui_text_change_event_get_type(data: *const UiTextChangeEventData) -> i32;
+ fn ui_text_change_event_get_begin(data: *const UiTextChangeEventData) -> i32;
+ fn ui_text_change_event_get_end(data: *const UiTextChangeEventData) -> i32;
+ fn ui_text_change_event_get_text(data: *const UiTextChangeEventData) -> *const c_char;
+ fn ui_text_change_event_get_length(data: *const UiTextChangeEventData) -> i32;
+
fn ui_sublist_event_get_sublist_index(event: *const UiSubListEventData) -> i32;
fn ui_sublist_event_get_row_index(event: *const UiSubListEventData) -> i32;
}
\ No newline at end of file
_private: [u8; 0],
}
+#[repr(C)]
+pub struct UiTextChangeEventData {
+ _private: [u8; 0],
+}
+
#[repr(C)]
pub struct UiText {
_private: [u8; 0],
self
}
+ pub fn action(&mut self, action: &str) -> &mut Self {
+ let cstr = CString::new(action).unwrap();
+ unsafe {
+ ui_textarea_args_set_action(self.args, cstr.as_ptr());
+ }
+ self
+ }
+
pub fn visibility_states(&mut self, states: &[i32]) -> &mut Self {
unsafe {
ui_textarea_args_set_visibility_states(self.args, states.as_ptr(), states.len() as c_int);
}
}
+pub struct TextInsert {
+ pub pos: i32,
+ pub text: String,
+}
+
+pub struct TextDelete {
+ pub begin: i32,
+ pub end: i32
+}
/* -------------------------------- TextField -------------------------------- */
fn ui_textarea_args_set_style_class(args: *mut UiTextAreaArgs, classname: *const c_char);
fn ui_textarea_args_set_onchange(args: *mut UiTextAreaArgs, callback: UiCallback);
fn ui_textarea_args_set_onchangedata(args: *mut UiTextAreaArgs, data: *mut c_void);
+ fn ui_textarea_args_set_action(args: *mut UiTextAreaArgs, action: *const c_char);
fn ui_textarea_args_set_varname(args: *mut UiTextAreaArgs, varname: *const c_char);
fn ui_textarea_args_set_value(args: *mut UiTextAreaArgs, ivalue: *mut UiText);
fn ui_textarea_args_set_states(args: *mut UiTextAreaArgs, states: *const c_int, numstates: c_int);
static int app_argc;
static const char **app_argv;
-static UiBool exit_on_shutdown;
+static UiBool exit_on_shutdown;
+
+static char *main_thread_error_msg;
+
+// This function is only used by language bindings, to improve error messages
+// for example, when using java bindings, this can provide infos how to fix
+// this (-XstartOnFirstThread)
+void ui_set_main_thread_error_msg(const char *msg) {
+ main_thread_error_msg = msg ? strdup(msg) : NULL;
+}
/* ------------------- App Init / Event Loop functions ------------------- */
static AppDelegate *app_delegate;
+static void main_thr_check(const char *func) {
+ if(![NSThread isMainThread]) {
+ fprintf(stderr, "Error: %s must run on the main thread.\n", func);
+ if(main_thread_error_msg) {
+ fprintf(stderr, "%s\n", main_thread_error_msg);
+ }
+ exit(1);
+ }
+}
+
void ui_init(const char *appname, int argc, char **argv) {
+ main_thr_check("ui_init");
+
application_name = appname ? strdup(appname) : NULL;
app_argc = argc;
app_argv = (const char**)argv;
}
void ui_main(void) {
+ main_thr_check("ui_main");
+
NSApplicationMain(app_argc, app_argv);
//[NSApp finishLaunching];
//[NSApp activateIgnoringOtherApps:YES];
args->onchangedata = onchangedata;
}
+void ui_textarea_args_set_action(UiTextAreaArgs *args, const char *action) {
+ args->action = strdup(action);
+}
+
void ui_textarea_args_set_varname(UiTextAreaArgs *args, const char *varname) {
args->varname = strdup(varname);
}
free((void*)args->name);
free((void*)args->style_class);
free((void*)args->varname);
+ free((void*)args->action);
free((void*)args->states);
free((void*)args->visibility_states);
free(args);
free(sel);
}
+/* -------------------------- UiTextChangedEvent -------------------------- */
+
+int ui_text_change_event_get_type(UiTextChangeEventData *event) {
+ return event->type;
+}
+
+int ui_text_change_event_get_begin(UiTextChangeEventData *event) {
+ return event->begin;
+}
+
+int ui_text_change_event_get_end(UiTextChangeEventData *event) {
+ return event->end;
+}
+
+const char* ui_text_change_event_get_text(UiTextChangeEventData *event) {
+ return event->text;
+}
+
+int ui_text_change_event_get_length(UiTextChangeEventData *event) {
+ return event->length;
+}
+
/* ---------------------------- UiFileList ---------------------------- */
int ui_filelist_count(UiFileList *flist) {
#include "../ui/toolkit.h"
#include "../ui/list.h"
+#include "../ui/text.h"
#ifdef __cplusplus
extern "C" {
UIEXPORT void ui_list_set_selected_indices(UiList *list, int *indices, int num);
UIEXPORT void ui_list_selection_free(UiListSelection *sel);
+UIEXPORT int ui_text_change_event_get_type(UiTextChangeEventData *event);
+UIEXPORT int ui_text_change_event_get_begin(UiTextChangeEventData *event);
+UIEXPORT int ui_text_change_event_get_end(UiTextChangeEventData *event);
+UIEXPORT const char* ui_text_change_event_get_text(UiTextChangeEventData *event);
+UIEXPORT int ui_text_change_event_get_length(UiTextChangeEventData *event);
+
UIEXPORT int ui_filelist_count(UiFileList *flist);
UIEXPORT char* ui_filelist_get(UiFileList *flist, int index);
static GtkTextBuffer* create_textbuffer(UiTextArea *textarea) {
GtkTextBuffer *buf = gtk_text_buffer_new(NULL);
if(textarea) {
+ /*
g_signal_connect(
buf,
"changed",
G_CALLBACK(ui_textbuf_changed),
textarea);
+ */
+
+ g_signal_connect(
+ buf,
+ "insert-text",
+ G_CALLBACK(ui_textbuf_changed_insert),
+ textarea);
+ g_signal_connect(
+ buf,
+ "delete-range",
+ G_CALLBACK(ui_textbuf_changed_delete),
+ textarea);
} else {
fprintf(stderr, "Error: create_textbuffer: textarea == NULL\n");
}
}
+void ui_textbuf_changed_insert(
+ GtkTextBuffer *textbuffer,
+ GtkTextIter *location,
+ char *text,
+ int length,
+ UiTextArea *textarea)
+{
+ UiTextChangeEventData event;
+ event.type = UI_TEXT_INSERT;
+ event.begin = gtk_text_iter_get_offset(location);
+ event.end = event.begin + length;
+ event.text = text;
+ event.length = length;
+ ui_textbuf_changed(textarea, &event);
+}
+
+void ui_textbuf_changed_delete(
+ GtkTextBuffer *self,
+ const GtkTextIter *start,
+ const GtkTextIter *end,
+ UiTextArea *textarea)
+{
+ UiTextChangeEventData event;
+ event.type = UI_TEXT_DELETE;
+ event.begin = gtk_text_iter_get_offset(start);
+ event.end = gtk_text_iter_get_offset(end);
+ event.text = NULL;
+ event.length = 0;
+ ui_textbuf_changed(textarea, &event);
+}
+
+
+// void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea)
-void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea) {
+void ui_textbuf_changed(UiTextArea *textarea, UiTextChangeEventData *data) {
if(!ui_onchange_events_is_enabled()) {
return;
}
e.obj = textarea->obj;
e.window = e.obj->window;
e.document = textarea->ctx->document;
- e.eventdata = value;
- e.eventdatatype = UI_EVENT_DATA_TEXT_VALUE;
+ e.eventdata = data;
+ e.eventdatatype = UI_EVENT_DATA_TEXT_CHANGED;
e.intval = 0;
e.set = ui_get_setop();
void ui_textarea_remove(UiText *text, int begin, int end);
void ui_textarea_realize_event(GtkWidget *widget, gpointer data);
-void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea);
+//void ui_textbuf_changed(GtkTextBuffer *textbuffer, UiTextArea *textarea);
+void ui_textbuf_changed_insert(
+ GtkTextBuffer *textbuffer,
+ GtkTextIter *location,
+ char *text,
+ int length,
+ UiTextArea *textarea);
+void ui_textbuf_changed_delete(
+ GtkTextBuffer *self,
+ const GtkTextIter *start,
+ const GtkTextIter *end,
+ UiTextArea *textarea);
+void ui_textbuf_changed(UiTextArea *textarea, UiTextChangeEventData *data);
void ui_textbuf_insert(
GtkTextBuffer *textbuffer,
static UiBool exit_on_shutdown;
+// NOOP on most platforms, expect macos
+void ui_set_main_thread_error_msg(const char *msg) {}
+
UIEXPORT void ui_init(const char *appname, int argc, char **argv) {
application_name = appname ? strdup(appname) : NULL;
uic_init_global_context();
fallback_resources = fallbackres;
}
+// NOOP on most platforms, expect macos
+void ui_set_main_thread_error_msg(const char *msg) {}
+
void ui_init(const char *appname, int argc, char **argv) {
application_name = appname ? strdup(appname) : NULL;
uic_init_global_context();
static UiBool exit_on_shutdown;
+// NOOP on most platforms, expect macos
+extern "C" UIEXPORT void ui_set_main_thread_error_msg(const char *msg) {}
+
void ui_init(const char *appname, int argc, char **argv) {
application_name = appname ? strdup(appname) : NULL;
static CxMap *srv_obj_map;
static uint64_t srv_obj_id_counter = 0;
+// NOOP on most platforms, expect macos
+void ui_set_main_thread_error_msg(const char *msg) {}
+
void ui_init(const char *appname, int argc, char **argv) {
ui_app_name = appname ? strdup(appname) : NULL;
const int *states;
const int *visibility_states;
} UiTextAreaArgs;
+
+typedef enum UiTextChangedEventType {
+ UI_TEXT_INSERT = 0,
+ UI_TEXT_DELETE
+} UiTextChangedEventType;
+
+typedef struct UiTextChangeEventData {
+ UiTextChangedEventType type;
+ int begin;
+ int end;
+ const char *text;
+ int length;
+} UiTextChangeEventData;
typedef struct UiTextFieldArgs {
UiBool fill;
UI_EVENT_DATA_TEXT_VALUE,
UI_EVENT_DATA_DOUBLE_VALUE,
UI_EVENT_DATA_RANGE_VALUE,
+ UI_EVENT_DATA_TEXT_CHANGED,
UI_EVENT_DATA_LIST_SELECTION,
UI_EVENT_DATA_LIST_ELM,
UI_EVENT_DATA_DND,
static HFONT ui_font = NULL;
+// NOOP on most platforms, expect macos
+UIEXPORT void ui_set_main_thread_error_msg(const char *msg) {}
+
void ui_init(const char *appname, int argc, char **argv) {
application_name = appname ? strdup(appname) : NULL;
}\r
}\r
\r
+// NOOP on most platforms, expect macos\r
+UIEXPORT extern "C" void ui_set_main_thread_error_msg(const char *msg) {}\r
+\r
void ui_init(const char* appname, int argc, char** argv) {\r
application_name = appname ? strdup(appname) : NULL;\r
\r