From 855edff504971434320402eb21fdf2c559a33c45 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Thu, 13 Mar 2025 20:59:18 +0100 Subject: [PATCH] implement text style changes via toolbar buttons --- application/application.h | 5 + application/editor.c | 2 + application/editor.h | 37 ++++--- application/gtk-text.c | 201 +++++++++++++++++++++++++++++++++++++- application/gtk-text.h | 2 + application/note.c | 44 +++++++++ application/note.h | 10 ++ application/window.c | 52 +++++++++- application/window.h | 5 +- ui/common/types.c | 20 ++++ ui/gtk/button.c | 9 +- ui/ui/toolkit.h | 4 + 12 files changed, 369 insertions(+), 22 deletions(-) diff --git a/application/application.h b/application/application.h index 1c6d1e2..ac28c5c 100644 --- a/application/application.h +++ b/application/application.h @@ -137,6 +137,11 @@ struct NoteModel { */ UiList *textnote_para; + UiInteger *textnote_strong; + UiInteger *textnote_emphasis; + UiInteger *textnote_underline; + UiInteger *textnote_code; + bool modified; }; diff --git a/application/editor.c b/application/editor.c index 179d4ea..93289af 100644 --- a/application/editor.c +++ b/application/editor.c @@ -273,6 +273,7 @@ static const char* node_style(MDNode *n) { case MD_SPAN_EM: return EDITOR_STYLE_EMPHASIS; case MD_SPAN_STRONG: return EDITOR_STYLE_STRONG; case MD_SPAN_A: return EDITOR_STYLE_LINK; + case MD_SPAN_CODE: return EDITOR_STYLE_CODE; } return NULL; } @@ -316,6 +317,7 @@ static const char* paragraph_style(MDPara *p) { case 6: return EDITOR_STYLE_HEADING6; } } + case MD_BLOCK_CODE: return EDITOR_STYLE_CODE_BLOCK; default: return EDITOR_STYLE_PARAGRAPH; } } diff --git a/application/editor.h b/application/editor.h index 15dc2ab..11c798f 100644 --- a/application/editor.h +++ b/application/editor.h @@ -38,18 +38,19 @@ extern "C" { #endif -#define EDITOR_STYLE_PARAGRAPH "para" -#define EDITOR_STYLE_HEADING1 "heading1" -#define EDITOR_STYLE_HEADING2 "heading2" -#define EDITOR_STYLE_HEADING3 "heading3" -#define EDITOR_STYLE_HEADING4 "heading4" -#define EDITOR_STYLE_HEADING5 "heading5" -#define EDITOR_STYLE_HEADING6 "heading6" -#define EDITOR_STYLE_QUOTE "quote" -#define EDITOR_STYLE_CODE "code" -#define EDITOR_STYLE_EMPHASIS "emphasis" -#define EDITOR_STYLE_STRONG "strong" -#define EDITOR_STYLE_LINK "link" +#define EDITOR_STYLE_PARAGRAPH "para" +#define EDITOR_STYLE_HEADING1 "heading1" +#define EDITOR_STYLE_HEADING2 "heading2" +#define EDITOR_STYLE_HEADING3 "heading3" +#define EDITOR_STYLE_HEADING4 "heading4" +#define EDITOR_STYLE_HEADING5 "heading5" +#define EDITOR_STYLE_HEADING6 "heading6" +#define EDITOR_STYLE_QUOTE "quote" +#define EDITOR_STYLE_CODE_BLOCK "code_block" +#define EDITOR_STYLE_EMPHASIS "emphasis" +#define EDITOR_STYLE_STRONG "strong" +#define EDITOR_STYLE_CODE "code" +#define EDITOR_STYLE_LINK "link" #define MD_MAX_DEPTH 50 @@ -60,6 +61,8 @@ typedef struct MDNode MDNode; typedef struct MDDocLinear MDDocLinear; typedef struct MDDocStyleSection MDDocStyleSection; +typedef struct MDActiveStyles MDActiveStyles; + struct MDPara { MD_BLOCKTYPE type; MDNode *content; @@ -95,6 +98,14 @@ struct MDDocStyleSection { const char *style; const char *link; }; + +struct MDActiveStyles { + const char *paragraph; + int paragraph_index; + UiBool emphasis; + UiBool strong; + UiBool code; +}; void editor_init(UiText *text); @@ -116,6 +127,8 @@ void editor_init_textbuf(UiText *text); void editor_apply_styles(UiText *text, CxList /* MDDocStyleSection */ *styles); cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a); +UiBool editor_set_style(UiText *text, const char *style, UiBool enabled); + #ifdef __cplusplus } #endif diff --git a/application/gtk-text.c b/application/gtk-text.c index e0b7f5c..4702038 100644 --- a/application/gtk-text.c +++ b/application/gtk-text.c @@ -28,10 +28,17 @@ #include "gtk-text.h" #include "editor.h" +#include "note.h" #include #include +// workaround for netbeans bug +#define g_object_set g_object_set +#define g_object_get_property g_object_get_property +#define g_object_set_data g_object_set_data +#define g_object_get_data g_object_get_data + static CxMap *markdown_tags; static void editor_button_released_cb( @@ -41,6 +48,12 @@ static void editor_button_released_cb( double y, NoteEditor *editor); +static void editor_set_cursor_cb( + GtkTextBuffer *buffer, + const GtkTextIter *location, + GtkTextMark *mark, + NoteEditor *editor); + void editor_global_init() { markdown_tags = cxHashMapCreateSimple(sizeof(MDTag)); @@ -52,9 +65,10 @@ void editor_global_init() { cxMapPut(markdown_tags, EDITOR_STYLE_HEADING5, &((MDTag){"##### ", NULL})); cxMapPut(markdown_tags, EDITOR_STYLE_HEADING6, &((MDTag){"###### ", NULL})); cxMapPut(markdown_tags, EDITOR_STYLE_STRONG, &((MDTag){"**", "**"})); + cxMapPut(markdown_tags, EDITOR_STYLE_EMPHASIS, &((MDTag){"*", "*"})); + cxMapPut(markdown_tags, EDITOR_STYLE_CODE, &((MDTag){"`", "`"})); } - void editor_init_textview(UiObject *obj, UIWIDGET textview) { gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_WORD_CHAR); gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 16); @@ -67,11 +81,15 @@ void editor_init_textview(UiObject *obj, UIWIDGET textview) { g_object_set_data(G_OBJECT(textview), "editor", editor); + // gesture event controller is used for handling clicks on links GtkEventController *controller = GTK_EVENT_CONTROLLER(gtk_gesture_click_new()); - g_signal_connect(controller, "released", G_CALLBACK (editor_button_released_cb), editor); + g_signal_connect(controller, "released", G_CALLBACK(editor_button_released_cb), editor); gtk_widget_add_controller(textview, controller); } +/* + * handle clicks inside the textview and follow links when possible + */ static void editor_button_released_cb( GtkGestureClick *gesture, guint n_press, @@ -105,6 +123,46 @@ static void editor_button_released_cb( } } +static void editor_set_cursor_cb( + GtkTextBuffer *buffer, + const GtkTextIter *location, + GtkTextMark *mark, + NoteEditor *editor) +{ + const char *mark_name = gtk_text_mark_get_name(mark); + if(!mark_name || strcmp(mark_name, "insert")) { + return; + } + + GtkTextIter pos; + // get current tags + // when the buffer has a selection, get the tags from the selection start + // without a selection, get the tags at the cursor position + if(gtk_text_buffer_get_has_selection(buffer)) { + GtkTextIter end; + gtk_text_buffer_get_selection_bounds(buffer, &pos, &end); + } else { + GtkTextMark *mark = gtk_text_buffer_get_insert(buffer); + gtk_text_buffer_get_iter_at_mark(buffer, &pos, mark); + } + + MDActiveStyles styles = { 0 }; + GSList *tags = gtk_text_iter_get_tags(&pos); + while(tags) { + GtkTextTag *tag = tags->data; + set_style_cb set_style = (set_style_cb)g_object_get_data(G_OBJECT(tag), "set_style"); + if(set_style) { + set_style(&styles, tag); + } + tags = tags->next; + } + + NoteModel *note = notemodel_current(editor->obj); + if(note) { + note_update_current_style(note, &styles); + } +} + void editor_try_follow_link(NoteEditor *editor, GtkTextIter *pos) { GSList *tags = gtk_text_iter_get_tags(pos); while(tags) { @@ -134,6 +192,15 @@ void editor_init_textbuf(UiText *text) { init_textbuf(buf); g_object_set_data(G_OBJECT(buf), "md", text); } + + GtkTextView *textview = text->obj; + if(!textview) { + return; + } + NoteEditor *editor = g_object_get_data(G_OBJECT(textview), "editor"); + if(editor) { + g_signal_connect(buf, "mark-set", G_CALLBACK(editor_set_cursor_cb), editor); + } } void init_textbuf(GtkTextBuffer *buf) { @@ -143,58 +210,140 @@ void init_textbuf(GtkTextBuffer *buf) { } + +static void tagstyle_paragraph(MDActiveStyles *style, GtkTextTag *tag) { + style->paragraph = EDITOR_STYLE_PARAGRAPH; + style->paragraph_index = 0; +} + +static void tagstyle_heading1(MDActiveStyles *style, GtkTextTag *tag) { + style->paragraph = EDITOR_STYLE_HEADING1; + style->paragraph_index = 3; +} + +static void tagstyle_heading2(MDActiveStyles *style, GtkTextTag *tag) { + style->paragraph = EDITOR_STYLE_HEADING2; + style->paragraph_index = 4; +} + +static void tagstyle_heading3(MDActiveStyles *style, GtkTextTag *tag) { + style->paragraph = EDITOR_STYLE_HEADING3; + style->paragraph_index = 5; +} + +static void tagstyle_heading4(MDActiveStyles *style, GtkTextTag *tag) { + style->paragraph = EDITOR_STYLE_HEADING4; + style->paragraph_index = 6; +} + +static void tagstyle_heading5(MDActiveStyles *style, GtkTextTag *tag) { + style->paragraph = EDITOR_STYLE_HEADING5; + style->paragraph_index = 7; +} + +static void tagstyle_heading6(MDActiveStyles *style, GtkTextTag *tag) { + style->paragraph = EDITOR_STYLE_HEADING6; + style->paragraph_index = 8; +} + +static void tagstyle_quote(MDActiveStyles *style, GtkTextTag *tag) { + style->paragraph = EDITOR_STYLE_QUOTE; + style->paragraph_index = 2; +} + +static void tagstyle_codeblock(MDActiveStyles *style, GtkTextTag *tag) { + style->paragraph = EDITOR_STYLE_CODE_BLOCK; + style->paragraph_index = 1; +} + +static void tagstyle_emphasis(MDActiveStyles *style, GtkTextTag *tag) { + style->emphasis = TRUE; +} + +static void tagstyle_strong(MDActiveStyles *style, GtkTextTag *tag) { + style->strong = TRUE; +} + +static void tagstyle_code(MDActiveStyles *style, GtkTextTag *tag) { + style->code = TRUE; +} + +static void tagstyle_link(MDActiveStyles *style, GtkTextTag *tag) { + +} + void init_tagtable(GtkTextTagTable *table) { printf("init_tagtable\n"); GtkTextTag *tag; tag = gtk_text_tag_new(EDITOR_STYLE_PARAGRAPH); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_paragraph); gtk_text_tag_table_add(table, tag); tag = gtk_text_tag_new(EDITOR_STYLE_HEADING1); g_object_set(tag, "scale", 1.5, "weight", PANGO_WEIGHT_BOLD, NULL); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_heading1); gtk_text_tag_table_add(table, tag); tag = gtk_text_tag_new(EDITOR_STYLE_HEADING2); g_object_set(tag, "scale", 1.4, "weight", PANGO_WEIGHT_BOLD, NULL); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_heading2); gtk_text_tag_table_add(table, tag); tag = gtk_text_tag_new(EDITOR_STYLE_HEADING3); g_object_set(tag, "scale", 1.3, "weight", PANGO_WEIGHT_BOLD, NULL); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_heading3); gtk_text_tag_table_add(table, tag); tag = gtk_text_tag_new(EDITOR_STYLE_HEADING4); g_object_set(tag, "scale", 1.2, "weight", PANGO_WEIGHT_BOLD, NULL); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_heading4); gtk_text_tag_table_add(table, tag); tag = gtk_text_tag_new(EDITOR_STYLE_HEADING5); g_object_set(tag, "scale", 1.1, "weight", PANGO_WEIGHT_BOLD, NULL); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_heading5); gtk_text_tag_table_add(table, tag); tag = gtk_text_tag_new(EDITOR_STYLE_HEADING6); g_object_set(tag, "scale", 1.1, "weight", PANGO_WEIGHT_BOLD, NULL); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_heading6); gtk_text_tag_table_add(table, tag); tag = gtk_text_tag_new(EDITOR_STYLE_QUOTE); g_object_set(tag, "left-margin", 20, NULL); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_quote); gtk_text_tag_table_add(table, tag); - tag = gtk_text_tag_new(EDITOR_STYLE_CODE); + tag = gtk_text_tag_new(EDITOR_STYLE_CODE_BLOCK); g_object_set(tag, "family", "Monospace", "paragraph-background", "#080808", NULL); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_codeblock); gtk_text_tag_table_add(table, tag); tag = gtk_text_tag_new(EDITOR_STYLE_EMPHASIS); g_object_set(tag, "style", PANGO_STYLE_ITALIC, NULL); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_emphasis); gtk_text_tag_table_add(table, tag); tag = gtk_text_tag_new(EDITOR_STYLE_STRONG); g_object_set(tag, "weight", PANGO_WEIGHT_BOLD, NULL); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_strong); + gtk_text_tag_table_add(table, tag); + + tag = gtk_text_tag_new(EDITOR_STYLE_CODE); + g_object_set(tag, "family", "Monospace", "background", "#efefef", NULL); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_code); gtk_text_tag_table_add(table, tag); tag = gtk_text_tag_new(EDITOR_STYLE_LINK); g_object_set(tag, "foreground", "blue", NULL); + g_object_set_data(G_OBJECT(tag), "set_style", (void*)tagstyle_link); gtk_text_tag_table_add(table, tag); } +/* + * Applies all styles from the MDDocStyleSection list to the text buffer + */ void editor_apply_styles(UiText *text, CxList /* MDDocStyleSection */ *styles) { GtkTextBuffer *buffer = text->data1; GtkTextTagTable *tagtable = gtk_text_buffer_get_tag_table(buffer); @@ -220,7 +369,11 @@ void editor_apply_styles(UiText *text, CxList /* MDDocStyleSection */ *styles) { } } - +/* + * Creates a map from a GtkTextTag list + * key: tag name + * value: GtkTextTag* + */ static CxMap* tags2map(GSList *tags) { CxMap *map = cxHashMapCreateSimple(CX_STORE_POINTERS); while(tags) { @@ -236,6 +389,10 @@ static CxMap* tags2map(GSList *tags) { return map; } +/* + * Clones a map (that stores pointers) + * The values are not cloned + */ static CxMap* map_clone(CxMap *map) { CxMap *newmap = cxHashMapCreateSimple(CX_STORE_POINTERS); CxMapIterator i = cxMapIterator(map); @@ -245,6 +402,9 @@ static CxMap* map_clone(CxMap *map) { return newmap; } +/* + * Removes all sub entries from map + */ static void map_subtract(CxMap *map, CxMap *sub) { CxMapIterator i = cxMapIterator(sub); cx_foreach(CxMapEntry *, entry, i) { @@ -252,6 +412,9 @@ static void map_subtract(CxMap *map, CxMap *sub) { } } +/* + * Converts the UiText text buffer to markdown + */ cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a) { CxBuffer out; cxBufferInit(&out, NULL, 1024, a, CX_BUFFER_AUTO_EXTEND); @@ -321,3 +484,33 @@ cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a) { cxmutstr ret = cx_mutstrn(out.space, out.size); return ret; } + +/* + * Apply a non-paragraph style to the current selection + */ +UiBool editor_set_style(UiText *text, const char *style, UiBool enabled) { + GtkTextBuffer *buffer = text->data1; + if(!buffer) { + fprintf(stderr, "Error: editor_set_style: no text buffer\n"); + return FALSE; + } + + if(!gtk_text_buffer_get_has_selection(buffer)) { + return FALSE; + } + GtkTextIter begin, end; + gtk_text_buffer_get_selection_bounds(buffer, &begin, &end); + + if(enabled) { + gtk_text_buffer_apply_tag_by_name(buffer, style, &begin, &end); + } else { + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), style); + if(tag) { + gtk_text_buffer_remove_tag(buffer, tag, &begin, &end); + } else { + fprintf(stderr, "Error: tag %s not found\n", style); + } + } + + return TRUE; +} diff --git a/application/gtk-text.h b/application/gtk-text.h index 57e69a9..3f25b25 100644 --- a/application/gtk-text.h +++ b/application/gtk-text.h @@ -50,6 +50,8 @@ typedef struct TextLink { int type; } TextLink; +typedef void (*set_style_cb)(MDActiveStyles *style, GtkTextTag *tag); + void editor_try_follow_link(NoteEditor *editor, GtkTextIter *pos); void init_textbuf(GtkTextBuffer *buf); diff --git a/application/note.c b/application/note.c index 165c77a..4f3bd3c 100644 --- a/application/note.c +++ b/application/note.c @@ -31,6 +31,14 @@ #include "store.h" #include "editor.h" +NoteModel *notemodel_current(UiObject *obj) { + MainWindow *win = obj->window; + if(win->current_notebook && win->current_notebook->current_note) { + return win->current_notebook->current_note->model; + } + return NULL; +} + NoteModel* notemodel_create(const CxAllocator *note_allocator) { NoteModel *model = ui_document_new(sizeof(NoteModel)); model->ctx = ui_document_context(model); @@ -57,6 +65,11 @@ NoteModel* notemodel_create(const CxAllocator *note_allocator) { ui_list_append(model->textnote_para, "Heading 5"); ui_list_append(model->textnote_para, "Heading 6"); + model->textnote_strong = ui_int_new(model->ctx, "note_textnote_strong"); + model->textnote_emphasis = ui_int_new(model->ctx, "note_textnote_emphasis"); + model->textnote_underline = ui_int_new(model->ctx, "note_textnote_underline"); + model->textnote_code = ui_int_new(model->ctx, "note_textnote_code"); + return model; } @@ -114,3 +127,34 @@ void note_save(UiObject *obj, NotebookModel *notebook, Note *note) { note_store_save_note_async(obj, note, NULL, NULL); } } + +void note_update_current_style(NoteModel *note, MDActiveStyles *style) { + ui_list_setselection(note->textnote_para, style->paragraph_index); + + ui_set(note->textnote_strong, style->strong); + ui_set(note->textnote_emphasis, style->emphasis); + //ui_set(note->textnote_underline = style->underline); + ui_set(note->textnote_code, style->code); +} + +void note_text_style_set_strong(NoteModel *note, UiBool enabled) { + if(!editor_set_style(note->text, EDITOR_STYLE_STRONG, enabled)) { + ui_set(note->textnote_strong, !enabled); + } +} + +void note_text_style_set_emphasis(NoteModel *note, UiBool enabled) { + if(!editor_set_style(note->text, EDITOR_STYLE_EMPHASIS, enabled)) { + ui_set(note->textnote_emphasis, !enabled); + } +} + +void note_text_style_set_underline(NoteModel *note, UiBool enabled) { + +} + +void note_text_style_set_code(NoteModel *note, UiBool enabled) { + if(!editor_set_style(note->text, EDITOR_STYLE_CODE, enabled)) { + ui_set(note->textnote_code, !enabled); + } +} diff --git a/application/note.h b/application/note.h index 30a003b..8e2bce8 100644 --- a/application/note.h +++ b/application/note.h @@ -30,11 +30,14 @@ #define NOTE_H #include "application.h" +#include "editor.h" #ifdef __cplusplus extern "C" { #endif +NoteModel *notemodel_current(UiObject *obj); + NoteModel* notemodel_create(const CxAllocator *note_allocator); void notemodel_set_note(NoteModel *model, Note *note); @@ -43,6 +46,13 @@ void note_load_content(UiObject *obj, NotebookModel *notebook, Note *note); void note_save(UiObject *obj, NotebookModel *notebook, Note *note); +void note_update_current_style(NoteModel *note, MDActiveStyles *style); + +void note_text_style_set_strong(NoteModel *note, UiBool enabled); +void note_text_style_set_emphasis(NoteModel *note, UiBool enabled); +void note_text_style_set_underline(NoteModel *note, UiBool enabled); +void note_text_style_set_code(NoteModel *note, UiBool enabled); + #ifdef __cplusplus } diff --git a/application/window.c b/application/window.c index fec346b..6a4d0e3 100644 --- a/application/window.c +++ b/application/window.c @@ -31,6 +31,7 @@ #include "store.h" #include "notebook.h" #include "editor.h" +#include "note.h" #include #include @@ -75,9 +76,10 @@ void window_create() { } ui_hbox(obj, .style_class = "note_toolbar", .margin = 10, .spacing = 4, .fill = UI_OFF) { ui_combobox(obj, .varname = "note_textnote_para"); - ui_button(obj, .icon = "format-text-bold"); - ui_button(obj, .icon = "format-text-italic"); - ui_button(obj, .icon = "format-text-underline"); + ui_togglebutton(obj, .icon = "format-text-bold", .varname = "note_textnote_strong", .onchange = action_textnote_style_strong); + ui_togglebutton(obj, .icon = "format-text-italic", .varname = "note_textnote_emphasis", .onchange = action_textnote_style_emphasis); + ui_togglebutton(obj, .icon = "format-text-underline", .varname = "note_textnote_underline", .onchange = action_textnote_style_underline); + ui_togglebutton(obj, .label = "code", .varname = "note_textnote_code", .onchange = action_textnote_style_code); ui_button(obj, .icon = "view-list-bullet"); ui_button(obj, .icon = "view-list-ordered"); ui_button(obj, .icon = "insert-image"); @@ -297,3 +299,47 @@ void action_note_activated(UiEvent *event, void *userdata) { window_notelist_setvisible(window, TRUE); } } + +void action_textnote_style_strong(UiEvent *event, void *userdata) { + if(event->set) { + return; // only handle user interactions, not events triggered by ui_set + } + MainWindow *window = event->window; + NotebookModel *notebook = window->current_notebook; + if(notebook && notebook->current_note && notebook->current_note->model) { + note_text_style_set_strong(notebook->current_note->model, event->intval); + } +} + +void action_textnote_style_emphasis(UiEvent *event, void *userdata) { + if(event->set) { + return; + } + MainWindow *window = event->window; + NotebookModel *notebook = window->current_notebook; + if(notebook && notebook->current_note && notebook->current_note->model) { + note_text_style_set_emphasis(notebook->current_note->model, event->intval); + } +} + +void action_textnote_style_underline(UiEvent *event, void *userdata) { + if(event->set) { + return; + } + MainWindow *window = event->window; + NotebookModel *notebook = window->current_notebook; + if(notebook && notebook->current_note && notebook->current_note->model) { + note_text_style_set_underline(notebook->current_note->model, event->intval); + } +} + +void action_textnote_style_code(UiEvent *event, void *userdata) { + if(event->set) { + return; + } + MainWindow *window = event->window; + NotebookModel *notebook = window->current_notebook; + if(notebook && notebook->current_note && notebook->current_note->model) { + note_text_style_set_code(notebook->current_note->model, event->intval); + } +} diff --git a/application/window.h b/application/window.h index 2f8f42f..ed470ac 100644 --- a/application/window.h +++ b/application/window.h @@ -56,7 +56,10 @@ void action_notebook_selected(UiEvent *event, void *userdata); void action_note_selected(UiEvent *event, void *userdata); void action_note_activated(UiEvent *event, void *userdata); - +void action_textnote_style_strong(UiEvent *event, void *userdata); +void action_textnote_style_emphasis(UiEvent *event, void *userdata); +void action_textnote_style_underline(UiEvent *event, void *userdata); +void action_textnote_style_code(UiEvent *event, void *userdata); diff --git a/ui/common/types.c b/ui/common/types.c index fe12110..1fdb1b9 100644 --- a/ui/common/types.c +++ b/ui/common/types.c @@ -306,7 +306,9 @@ UIEXPORT UiGeneric* ui_generic_new(UiContext *ctx, char *name) { void ui_int_set(UiInteger* i, int64_t value) { if (i) { if (i->set) { + ui_setop_enable(TRUE); i->set(i, value); + ui_setop_enable(FALSE); } else { i->value = value; } @@ -324,7 +326,9 @@ int64_t ui_int_get(UiInteger* i) { void ui_double_set(UiDouble* d, double value) { if (d) { if (d->set) { + ui_setop_enable(TRUE); d->set(d, value); + ui_setop_enable(FALSE); } else { d->value = value; } @@ -343,7 +347,9 @@ double ui_double_get(UiDouble* d) { void ui_string_set(UiString* s, const char* value) { if (s) { if (s->set) { + ui_setop_enable(TRUE); s->set(s, value); + ui_setop_enable(FALSE); } else { if(s->value.free) { s->value.free(s->value.ptr); @@ -371,7 +377,9 @@ char* ui_string_get(UiString* s) { void ui_text_set(UiText* s, const char* value) { if (s) { if (s->set) { + ui_setop_enable(TRUE); s->set(s, value); + ui_setop_enable(FALSE); } else { if(s->value.free) { s->value.free(s->value.ptr); @@ -616,3 +624,15 @@ void uic_list_register_observer_destructor(UiContext *ctx, UiList *list, UiObser destr->observer = observer; cxMempoolSetDestructor(destr, (cx_destructor_func)observer_destructor); } + + +static int ui_set_op = 0; + +void ui_setop_enable(int set) { + ui_set_op = set; +} + +int ui_get_setop(void) { + return ui_set_op; +} + diff --git a/ui/gtk/button.c b/ui/gtk/button.c index 5123e27..b26a130 100644 --- a/ui/gtk/button.c +++ b/ui/gtk/button.c @@ -114,6 +114,7 @@ void ui_button_clicked(GtkWidget *widget, UiEventData *event) { e.document = event->obj->ctx->document; e.eventdata = NULL; e.intval = event->value; + e.set = ui_get_setop(); event->callback(&e, event->userdata); } @@ -136,7 +137,8 @@ void ui_toggled_obs(void *widget, UiVarEventData *event) { e.window = event->obj->window; e.document = event->obj->ctx->document; e.eventdata = event->var->value; - e.intval = i->get(i); + e.intval = i->get(i); + e.set = ui_get_setop(); ui_notify_evt(i->observers, &e); } @@ -148,6 +150,7 @@ static void ui_toggled_callback(GtkToggleButton *widget, UiEventData *event) { e.document = event->obj->ctx->document; e.eventdata = NULL; e.intval = gtk_toggle_button_get_active(widget); + e.set = ui_get_setop(); event->callback(&e, event->userdata); } @@ -275,7 +278,7 @@ static UIWIDGET togglebutton_create(UiObject *obj, GtkWidget *widget, UiToggleAr UiObject* current = uic_current_obj(obj); ui_setup_togglebutton( - current, + obj, widget, args.label, args.icon, @@ -318,6 +321,7 @@ static void ui_checkbox_callback(GtkCheckButton *widget, UiEventData *event) { e.document = event->obj->ctx->document; e.eventdata = NULL; e.intval = gtk_check_button_get_active(widget); + e.set = ui_get_setop(); event->callback(&e, event->userdata); } @@ -388,6 +392,7 @@ static void radiobutton_toggled(void *widget, UiEventData *event) { e.document = event->obj->ctx->document; e.eventdata = NULL; e.intval = RADIOBUTTON_GET_ACTIVE(widget); + e.set = ui_get_setop(); event->callback(&e, event->userdata); } diff --git a/ui/ui/toolkit.h b/ui/ui/toolkit.h index f3d3c62..732473c 100644 --- a/ui/ui/toolkit.h +++ b/ui/ui/toolkit.h @@ -288,6 +288,7 @@ struct UiEvent { void *window; void *eventdata; int intval; + int set; }; struct UiMouseEvent { @@ -594,6 +595,9 @@ UIEXPORT void ui_condvar_wait(UiCondVar *var); UIEXPORT void ui_condvar_signal(UiCondVar *var, void *data, int intdata); UIEXPORT void ui_condvar_destroy(UiCondVar *var); +UIEXPORT void ui_setop_enable(int set); +UIEXPORT int ui_get_setop(void); + #ifdef __cplusplus } #endif -- 2.43.5