From cae37189df90785fc629b95c332c23772768305f Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Fri, 7 Mar 2025 22:32:49 +0100 Subject: [PATCH] add note toolbar --- application/application.h | 5 ++ application/note.c | 13 ++++++ application/window.c | 13 +++++- ui/common/context.c | 5 +- ui/gtk/container.c | 8 ++-- ui/gtk/list.c | 98 +++++++++++++++++++++++++++++++++++---- ui/gtk/list.h | 7 ++- ui/gtk/toolkit.c | 25 ++++++---- ui/gtk/toolkit.h | 4 ++ ui/ui/toolkit.h | 2 + ui/ui/tree.h | 2 + 11 files changed, 158 insertions(+), 24 deletions(-) diff --git a/application/application.h b/application/application.h index 22cd07d..d95216c 100644 --- a/application/application.h +++ b/application/application.h @@ -131,6 +131,11 @@ struct NoteModel { UiText *text; UiGeneric *html; + /* + * text note paragraph types + */ + UiList *textnote_para; + bool modified; }; diff --git a/application/note.c b/application/note.c index b342865..1d8fdb3 100644 --- a/application/note.c +++ b/application/note.c @@ -43,6 +43,19 @@ NoteModel* notemodel_create(const CxAllocator *note_allocator) { ui_set(model->type, 1); //ui_set_group(model->ctx, APP_STATE_NOTE_SELECTED); + model->textnote_para = ui_list_new(model->ctx, "note_textnote_para"); + // currently only one type of textnote exists, therefore the + // paragraph types are static + ui_list_append(model->textnote_para, "Paragraph"); + ui_list_append(model->textnote_para, "Code"); + ui_list_append(model->textnote_para, "Blockquote"); + ui_list_append(model->textnote_para, "Heading 1"); + ui_list_append(model->textnote_para, "Heading 2"); + ui_list_append(model->textnote_para, "Heading 3"); + ui_list_append(model->textnote_para, "Heading 4"); + ui_list_append(model->textnote_para, "Heading 5"); + ui_list_append(model->textnote_para, "Heading 6"); + return model; } diff --git a/application/window.c b/application/window.c index 4ab3bc9..d6faaf3 100644 --- a/application/window.c +++ b/application/window.c @@ -37,6 +37,7 @@ void window_create() { UiObject *obj = ui_sidebar_window("note", NULL); + ui_window_size(obj, 1600, 1200); MainWindow *wdata = window_init_data(obj); /* @@ -67,10 +68,20 @@ void window_create() { ui_tab(obj, "textnote") { ui_vbox0(obj) { ui_grid(obj, .margin = 10, .columnspacing = 10, .rowspacing = 10, .def_vfill = TRUE, .fill = UI_OFF) { - ui_label(obj, .label = "Title", .vfill = TRUE); + //ui_label(obj, .label = "Title", .vfill = TRUE); ui_textfield(obj, .varname = "note_title", .hexpand = TRUE, .groups = UI_GROUPS(APP_STATE_NOTE_SELECTED)); ui_newline(obj); } + 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_button(obj, .icon = "view-list-bullet"); + ui_button(obj, .icon = "view-list-ordered"); + ui_button(obj, .icon = "insert-image"); + ui_button(obj, .icon = "insert-link"); + } ui_textarea(obj, .varname = "note_text", .vfill = TRUE, .hfill = TRUE, .hexpand = TRUE, .vexpand = TRUE, .colspan = 2, .groups = UI_GROUPS(APP_STATE_NOTE_SELECTED), .fill = UI_ON); } } diff --git a/ui/common/context.c b/ui/common/context.c index 3cc7abc..5857194 100644 --- a/ui/common/context.c +++ b/ui/common/context.c @@ -347,7 +347,10 @@ void uic_copy_binding(UiVar *from, UiVar *to, UiBool copytodoc) { *to = tmp; UiList* t2 = to->value; - ui_notify(t2->observers, NULL); + if(t->update) { + t->update(t, -1); + } + ui_notify(t2->observers, NULL); // TODO: why not t? break; } diff --git a/ui/gtk/container.c b/ui/gtk/container.c index 9341b8d..74d1f6b 100644 --- a/ui/gtk/container.c +++ b/ui/gtk/container.c @@ -1068,16 +1068,16 @@ void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget, UiBool fill) cxListAdd(s->children, widget); if(s->pos == 0) { - gtk_paned_set_start_child(GTK_PANED(s->current_pane), widget); + PANED_SET_CHILD1(s->current_pane, widget); s->pos++; s->nchildren++; } else { if(s->nchildren+1 == s->max) { - gtk_paned_set_end_child(GTK_PANED(s->current_pane), widget); + PANED_SET_CHILD2(s->current_pane, widget); } else { GtkWidget *pane = create_paned(s->orientation); - gtk_paned_set_start_child(GTK_PANED(pane), widget); - gtk_paned_set_end_child(GTK_PANED(s->current_pane), pane); + PANED_SET_CHILD1(pane, widget); + PANED_SET_CHILD2(s->current_pane, pane); s->current_pane = pane; } diff --git a/ui/gtk/list.c b/ui/gtk/list.c index dc0c479..5d7f519 100644 --- a/ui/gtk/list.c +++ b/ui/gtk/list.c @@ -57,6 +57,14 @@ static GtkTargetEntry targetentries[] = }; */ +static void listview_copy_static_elements(UiListView *listview, char **elm, size_t nelm) { + listview->elements = calloc(nelm, sizeof(char*)); + listview->nelm = nelm; + for(int i=0;ielements[i] = strdup(elm[i]); + } +} + #if GTK_CHECK_VERSION(4, 10, 0) @@ -248,6 +256,10 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) { list->setselection = ui_listview_setselection2; ui_update_liststore(ls, list); + } else if (args.static_elements && args.static_nelm > 0) { + listview_copy_static_elements(listview, args.static_elements, args.static_nelm); + listview->model->getvalue = ui_strmodel_getvalue; // force strmodel + ui_update_liststore_static(ls, listview->elements, listview->nelm); } // event handling @@ -323,11 +335,15 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) { list->setselection = ui_combobox_setselection; ui_update_liststore(ls, list); + } else if (args.static_elements && args.static_nelm > 0) { + listview_copy_static_elements(listview, args.static_elements, args.static_nelm); + listview->model->getvalue = ui_strmodel_getvalue; // force strmodel + ui_update_liststore_static(ls, listview->elements, listview->nelm); } // event handling if(args.onactivate) { - g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), listview); + g_signal_connect(view, "notify::selected", G_CALLBACK(ui_dropdown_notify), listview); } // add widget to parent @@ -478,6 +494,23 @@ static void listview_update_selection(UiListView *view) { free(newselection); } } + +void ui_dropdown_notify(GtkWidget *dropdown, GObject *pspec, gpointer userdata) { + UiListView *view = userdata; + guint index = gtk_drop_down_get_selected(GTK_DROP_DOWN(dropdown)); + GObject *item = gtk_drop_down_get_selected_item(GTK_DROP_DOWN(dropdown)); + if(item && view->onactivate) { + ObjWrapper *eventdata = (ObjWrapper*)item; + UiEvent event; + event.obj = view->obj; + event.document = event.obj->ctx->document; + event.window = event.obj->window; + event.intval = index; + event.eventdata = eventdata->data; + view->onactivate(&event, view->onactivatedata); + } +} + void ui_columnview_activate(void *ignore, guint position, gpointer userdata) { UiListView *view = userdata; @@ -524,6 +557,14 @@ void ui_update_liststore(GListStore *liststore, UiList *list) { } } +void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm) { + g_list_store_remove_all(liststore); + for(int i=0;iobj; ui_update_liststore(view->liststore, list); @@ -729,6 +770,7 @@ UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) { g_object_unref(listmodel); UiListView *listview = malloc(sizeof(UiListView)); + memset(listview, 0, sizeof(UiListView)); listview->obj = obj; listview->widget = view; listview->var = var; @@ -876,6 +918,7 @@ UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { // add TreeView as observer to the UiList to update the TreeView if the // data changes UiListView *tableview = malloc(sizeof(UiListView)); + memset(tableview, 0, sizeof(UiListView)); tableview->obj = obj; tableview->widget = view; tableview->var = var; @@ -1002,7 +1045,7 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) { UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); - GtkWidget *combobox = ui_create_combobox(obj, model, var, args.onactivate, args.onactivatedata); + GtkWidget *combobox = ui_create_combobox(obj, model, var, args.static_elements, args.static_nelm, args.onactivate, args.onactivatedata); ui_set_name_and_style(combobox, args.name, args.style_class); ui_set_widget_groups(obj->ctx, combobox, args.groups); UI_APPLY_LAYOUT1(current, args); @@ -1011,16 +1054,29 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) { return combobox; } -GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_callback f, void *udata) { +GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char **elm, size_t nelm, ui_callback f, void *udata) { GtkWidget *combobox = gtk_combo_box_new(); UiListView *uicbox = malloc(sizeof(UiListView)); + memset(uicbox, 0, sizeof(UiListView)); uicbox->obj = obj; uicbox->widget = combobox; UiList *list = var ? var->value : NULL; GtkListStore *listmodel = create_list_store(list, model); + if(!list && elm && nelm > 0) { + listview_copy_static_elements(uicbox, elm, nelm); + for(int i=0;ielements[i]); + gtk_list_store_set_value(listmodel, &iter, 0, &value); + } + } + if(listmodel) { gtk_combo_box_set_model(GTK_COMBO_BOX(combobox), GTK_TREE_MODEL(listmodel)); g_object_unref(listmodel); @@ -1060,7 +1116,7 @@ GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_call event->userdata = udata; event->callback = f; event->value = 0; - event->customdata = NULL; + event->customdata = uicbox; g_signal_connect( combobox, @@ -1073,12 +1129,22 @@ GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_call } void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) { + int index = gtk_combo_box_get_active(widget); + UiListView *listview = e->customdata; + void *eventdata = NULL; + if(listview->var && listview->var->value) { + UiList *list = listview->var->value; + eventdata = ui_list_get(list, index); + } else if(listview->elements && listview->nelm > index) { + eventdata = listview->elements[index]; + } + UiEvent event; event.obj = e->obj; event.window = event.obj->window; event.document = event.obj->ctx->document; - event.eventdata = NULL; - event.intval = gtk_combo_box_get_active(widget); + event.eventdata = eventdata; + event.intval = index; e->callback(&event, e->userdata); } @@ -1496,7 +1562,15 @@ void ui_table_dragdest_a(UIWIDGET tablewidget, int actions, char **targets, int void ui_listview_destroy(GtkWidget *w, UiListView *v) { //gtk_tree_view_set_model(GTK_TREE_VIEW(w), NULL); - ui_destroy_boundvar(v->obj->ctx, v->var); + if(v->var) { + ui_destroy_boundvar(v->obj->ctx, v->var); + } + if(v->elements) { + for(int i=0;inelm;i++) { + free(v->elements[i]); + } + free(v->elements); + } #if GTK_CHECK_VERSION(4, 10, 0) free(v->columns); #endif @@ -1505,7 +1579,15 @@ void ui_listview_destroy(GtkWidget *w, UiListView *v) { } void ui_combobox_destroy(GtkWidget *w, UiListView *v) { - ui_destroy_boundvar(v->obj->ctx, v->var); + if(v->var) { + ui_destroy_boundvar(v->obj->ctx, v->var); + } + if(v->elements) { + for(int i=0;inelm;i++) { + free(v->elements[i]); + } + free(v->elements); + } free(v); } diff --git a/ui/gtk/list.h b/ui/gtk/list.h index 1ff4029..784e644 100644 --- a/ui/gtk/list.h +++ b/ui/gtk/list.h @@ -45,6 +45,8 @@ typedef struct UiListView { GtkWidget *widget; UiVar *var; UiModel *model; + char **elements; + size_t nelm; #if GTK_CHECK_VERSION(4, 10, 0) GListStore *liststore; GtkSelectionModel *selectionmodel; @@ -100,7 +102,6 @@ struct UiListBox { void *onactivatedata; ui_callback onbuttonclick; void *onbuttonclickdata; - GtkListBoxRow *first_row; }; @@ -108,11 +109,13 @@ struct UiListBox { #if GTK_CHECK_VERSION(4, 10, 0) void ui_update_liststore(GListStore *liststore, UiList *list); +void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm); void ui_listview_update2(UiList *list, int i); UiListSelection ui_listview_getselection2(UiList *list); void ui_listview_setselection2(UiList *list, UiListSelection selection); +void ui_dropdown_notify(GtkWidget *dropdown, GObject *pspec, gpointer userdata); void ui_columnview_activate(void *ignore, guint position, gpointer userdata); void ui_listview_selection_changed(GtkSelectionModel* self, guint position, guint n_items, gpointer user_data); @@ -155,7 +158,7 @@ void ui_listview_add_dnd(UiListView *listview, UiListArgs *args); void ui_listview_enable_drop(UiListView *listview, UiListArgs *args); UIWIDGET ui_combobox_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata); -GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_callback f, void *udata); +GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char **elm, size_t nelm, ui_callback f, void *udata); void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e); void ui_combobox_modelupdate(UiList *list, int i); UiListSelection ui_combobox_getselection(UiList *list); diff --git a/ui/gtk/toolkit.c b/ui/gtk/toolkit.c index 19a0990..2f28c2b 100644 --- a/ui/gtk/toolkit.c +++ b/ui/gtk/toolkit.c @@ -327,8 +327,6 @@ UiObject *ui_get_active_window() { #if GTK_MAJOR_VERSION >= 3 -static GtkCssProvider* ui_gtk_css_provider; - #if GTK_MAJOR_VERSION == 4 static const char *ui_gtk_css = "#path-textfield-box {\n" @@ -415,15 +413,19 @@ static const char *ui_gtk_css = #endif void ui_css_init(void) { - ui_gtk_css_provider = gtk_css_provider_new(); + ui_add_styledata(ui_gtk_css, -1); +} + +void ui_add_styledata(const char *styledata, int len) { + GtkCssProvider *css = gtk_css_provider_new(); #ifdef UI_GTK3 - gtk_css_provider_load_from_data(ui_gtk_css_provider, ui_gtk_css, -1, NULL); + gtk_css_provider_load_from_data(css, styledata, len, NULL); GdkScreen *screen = gdk_screen_get_default(); gtk_style_context_add_provider_for_screen( screen, - GTK_STYLE_PROVIDER(ui_gtk_css_provider), + GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_USER); #endif /* UI_GTK3 */ @@ -431,13 +433,20 @@ void ui_css_init(void) { #if GTK_MINOR_VERSION < 12 - gtk_css_provider_load_from_data(ui_gtk_css_provider, ui_gtk_css, -1); + gtk_css_provider_load_from_data(css, styledata, len); #else - gtk_css_provider_load_from_string(ui_gtk_css_provider, ui_gtk_css); + if(len < 0) { + gtk_css_provider_load_from_string(css, ui_gtk_css); + } else { + GBytes *style_data = g_bytes_new(styledata, len); + gtk_css_provider_load_from_bytes(css, style_data); + g_bytes_unref(style_data); + + } #endif /* GTK_MINOR_VERSION < 12 */ GdkDisplay *display = gdk_display_get_default(); - gtk_style_context_add_provider_for_display(display, GTK_STYLE_PROVIDER(ui_gtk_css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + gtk_style_context_add_provider_for_display(display, GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); #endif /* UI_GTK4 */ } diff --git a/ui/gtk/toolkit.h b/ui/gtk/toolkit.h index 62e82bc..b98bb25 100644 --- a/ui/gtk/toolkit.h +++ b/ui/gtk/toolkit.h @@ -74,6 +74,8 @@ extern "C" { #define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon) #define LISTBOX_REMOVE(listbox, row) gtk_list_box_remove(GTK_LIST_BOX(listbox), row) #define LISTBOX_ROW_SET_CHILD(row, child) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), child) +#define PANED_SET_CHILD1(paned, child) gtk_paned_set_start_child(GTK_PANED(paned), child) +#define PANED_SET_CHILD2(paned, child) gtk_paned_set_end_child(GTK_PANED(paned), child) #else #define WINDOW_SHOW(window) gtk_widget_show_all(window) #define WINDOW_DESTROY(window) gtk_widget_destroy(window) @@ -94,6 +96,8 @@ extern "C" { #define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_BUTTON) #define LISTBOX_REMOVE(listbox, row) gtk_container_remove(GTK_CONTAINER(listbox), row) #define LISTBOX_ROW_SET_CHILD(row, child) gtk_container_add(GTK_CONTAINER(row), child) +#define PANED_SET_CHILD1(paned, child) gtk_paned_pack1(GTK_PANED(paned), child, TRUE, TRUE) +#define PANED_SET_CHILD2(paned, child) gtk_paned_pack2(GTK_PANED(paned), child, TRUE, TRUE) #endif #ifdef UI_GTK2 diff --git a/ui/ui/toolkit.h b/ui/ui/toolkit.h index 437e360..f3d3c62 100644 --- a/ui/ui/toolkit.h +++ b/ui/ui/toolkit.h @@ -447,6 +447,8 @@ typedef struct UiCondVar { UIEXPORT void ui_init(const char *appname, int argc, char **argv); UIEXPORT const char* ui_appname(); +UIEXPORT void ui_add_styledata(const char *styledata, int len); + UIEXPORT UiContext* ui_global_context(void); UIEXPORT void ui_context_closefunc(UiContext *ctx, ui_callback fnc, void *udata); diff --git a/ui/ui/tree.h b/ui/ui/tree.h index 589d2a1..9d8ebba 100644 --- a/ui/ui/tree.h +++ b/ui/ui/tree.h @@ -118,6 +118,8 @@ struct UiListArgs { UiList* list; const char* varname; UiModel* model; + char **static_elements; + size_t static_nelm; ui_getvaluefunc getvalue; ui_callback onactivate; void* onactivatedata; -- 2.43.5