From: Olaf Wintermann Date: Wed, 28 Jan 2026 19:17:48 +0000 (+0100) Subject: add searchbar UI X-Git-Url: https://uap-core.de/gitweb/?a=commitdiff_plain;h=HEAD;p=note.git add searchbar UI --- diff --git a/application/application.h b/application/application.h index 81b5ed4..c727542 100644 --- a/application/application.h +++ b/application/application.h @@ -42,6 +42,7 @@ extern "C" { #define APP_STATE_NOTEBOOK_SELECTED 100 #define APP_STATE_NOTE_SELECTED 110 #define APP_STATE_NOTE_HAS_ATTACHMENTS 201 +#define APP_STATE_NOTE_SEARCHBAR 202 #define VIEW_FLAGS_NO_BROWSER 1 diff --git a/application/window.c b/application/window.c index ba35fb9..0bac192 100644 --- a/application/window.c +++ b/application/window.c @@ -103,7 +103,7 @@ void window_create() { ui_button(obj, .icon = "insert-image", .onclick = action_textnote_insertimg); ui_button(obj, .icon = "insert-link"); } - ui_hbox_w(obj, wdata->attachments, .margin = 10) { + ui_hbox_w(obj, wdata->attachments, .margin = 10, .visibility_states = UI_GROUPS(APP_STATE_NOTE_HAS_ATTACHMENTS)) { UIWIDGET sw; ui_scrolledwindow_w(obj, sw, .name = "note_attachments_sw") { ui_itemlist(obj, .varname = "note_attachments", .container = UI_CONTAINER_HBOX, .create_ui = attachment_item, .userdata = wdata); @@ -115,10 +115,23 @@ void window_create() { #if GTK_MAJOR_VERSION >= 4 ui_customwidget(obj, editor_gtk4_workaround, wdata, .hfill = TRUE); #endif - ui_set_visible(wdata->attachments, FALSE); - ui_widget_set_states(obj->ctx, wdata->attachments, (ui_enablefunc)ui_set_visible, APP_STATE_NOTE_HAS_ATTACHMENTS, -1); wdata->textview = ui_textarea(obj, .varname = "note_text", .vfill = TRUE, .hfill = TRUE, .hexpand = TRUE, .vexpand = TRUE, .colspan = 2, .states = UI_GROUPS(APP_STATE_NOTE_SELECTED), .fill = UI_ON); editor_init_textview(obj, ui_textarea_gettextwidget(wdata->textview)); + + ui_grid(obj, .margin = 4, .columnspacing = 4, .rowspacing = 4, .def_hfill = TRUE, .def_vfill = TRUE, .visibility_states = UI_GROUPS(APP_STATE_NOTE_SEARCHBAR)) { + ui_rlabel(obj, .label = "Find"); + ui_textfield(obj, .hexpand = TRUE); + ui_button(obj, .label = "Previous"); + ui_button(obj, .label = "Next"); + ui_checkbox(obj, .label = "Case Sensitive"); + ui_checkbox(obj, .label = "Regex"); + + ui_newline(obj); + ui_rlabel(obj, .label = "Replace"); + ui_textfield(obj, .hexpand = TRUE); + ui_button(obj, .label = "Replace"); + ui_button(obj, .label = "Replace All"); + } } } } diff --git a/ui/gtk/container.c b/ui/gtk/container.c index ad5297a..6b8b736 100644 --- a/ui/gtk/container.c +++ b/ui/gtk/container.c @@ -393,6 +393,11 @@ UIWIDGET ui_box_create(UiObject *obj, UiContainerArgs *args, UiSubContainerType UiContainerX *container = ui_box_container(obj, box, type); uic_object_push_container(obj, container); + if(args->visibility_states) { + size_t nstates = uic_state_array_size(args->visibility_states); + ui_widget_set_visibility_states(obj->ctx, box, args->visibility_states, (int)nstates); + } + return box; } @@ -429,6 +434,11 @@ UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) { UiContainerX *container = ui_grid_container(obj, grid, args->def_hexpand, args->def_vexpand, args->def_hfill, args->def_vfill); uic_object_push_container(obj, container); + if(args->visibility_states) { + size_t nstates = uic_state_array_size(args->visibility_states); + ui_widget_set_visibility_states(obj->ctx, grid, args->visibility_states, (int)nstates); + } + return grid; } @@ -476,6 +486,11 @@ UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) { uic_object_push_container(obj, container); } + if(args->visibility_states) { + size_t nstates = uic_state_array_size(args->visibility_states); + ui_widget_set_visibility_states(obj->ctx, frame, args->visibility_states, (int)nstates); + } + return frame; } @@ -500,6 +515,11 @@ UIEXPORT UIWIDGET ui_expander_create(UiObject *obj, UiFrameArgs *args) { uic_object_push_container(obj, container); } + if(args->visibility_states) { + size_t nstates = uic_state_array_size(args->visibility_states); + ui_widget_set_visibility_states(obj->ctx, expander, args->visibility_states, (int)nstates); + } + return expander; } @@ -525,6 +545,11 @@ UIWIDGET ui_scrolledwindow_create(UiObject* obj, UiFrameArgs *args) { uic_object_push_container(obj, container); } + if(args->visibility_states) { + size_t nstates = uic_state_array_size(args->visibility_states); + ui_widget_set_visibility_states(obj->ctx, sw, args->visibility_states, (int)nstates); + } + return sw; } @@ -814,6 +839,11 @@ UIWIDGET ui_tabview_create(UiObject* obj, UiTabViewArgs *args) { UiContainerX *container = ui_tabview_container(obj, widget); uic_object_push_container(obj, container); + if(args->visibility_states) { + size_t nstates = uic_state_array_size(args->visibility_states); + ui_widget_set_visibility_states(obj->ctx, widget, args->visibility_states, (int)nstates); + } + return widget; } diff --git a/ui/motif/container.c b/ui/motif/container.c index c5c7c4d..d1e0599 100644 --- a/ui/motif/container.c +++ b/ui/motif/container.c @@ -90,6 +90,11 @@ static UIWIDGET box_create(UiObject *obj, UiContainerArgs *args, UiBoxOrientatio UiContainerX *container = ui_box_container(obj, grid, orientation); uic_object_push_container(obj, container); + if(args->visibility_states) { + size_t nstates = uic_state_array_size(args->visibility_states); + ui_widget_set_visibility_states(obj->ctx, grid, args->visibility_states, (int)nstates); + } + return grid; } @@ -171,6 +176,11 @@ UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) { UiContainerX *container = ui_grid_container(obj, grid, args->def_hexpand, args->def_vexpand, args->def_hfill, args->def_vfill); uic_object_push_container(obj, container); + if(args->visibility_states) { + size_t nstates = uic_state_array_size(args->visibility_states); + ui_widget_set_visibility_states(obj->ctx, grid, args->visibility_states, (int)nstates); + } + return grid; } @@ -299,6 +309,10 @@ UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) { } } + if(args->visibility_states) { + size_t nstates = uic_state_array_size(args->visibility_states); + ui_widget_set_visibility_states(obj->ctx, frame, args->visibility_states, (int)nstates); + } return frame; } @@ -469,6 +483,11 @@ UIWIDGET ui_tabview_create(UiObject *obj, UiTabViewArgs *args) { uic_object_push_container(obj, (UiContainerX*)ct); + if(args->visibility_states) { + size_t nstates = uic_state_array_size(args->visibility_states); + ui_widget_set_visibility_states(obj->ctx, form, args->visibility_states, (int)nstates); + } + return form; } diff --git a/ui/ui/container.h b/ui/ui/container.h index dc2aa29..e9f3d3d 100644 --- a/ui/ui/container.h +++ b/ui/ui/container.h @@ -82,6 +82,8 @@ typedef struct UiContainerArgs { UiBool def_vfill; UiBool def_hexpand; UiBool def_vexpand; + + const int *visibility_states; } UiContainerArgs; typedef struct UiFrameArgs { @@ -112,6 +114,8 @@ typedef struct UiFrameArgs { const char* label; UiBool isexpanded; + + const int *visibility_states; } UiFrameArgs; typedef struct UiTabViewArgs { @@ -144,6 +148,8 @@ typedef struct UiTabViewArgs { int spacing; int columnspacing; int rowspacing; + + const int *visibility_states; } UiTabViewArgs; typedef struct UiHeaderbarArgs { diff --git a/ui/win32/menu.c b/ui/win32/menu.c index 82bcf2b..16171de 100644 --- a/ui/win32/menu.c +++ b/ui/win32/menu.c @@ -49,9 +49,11 @@ HMENU ui_create_main_menu(UiObject *obj) { } HMENU hMenu = CreateMenu(); - ui_add_menu(hMenu, 0, &menu->item, obj); - - + while (menu) { + ui_add_menu(hMenu, 0, &menu->item, obj); + UiMenuItemI *next = menu->item.next; + menu = next && next->type == UI_MENU ? (UiMenu*)next : NULL; + } return hMenu; } @@ -256,10 +258,106 @@ void ui_radioitem_set(UiInteger *i, int64_t value) { } } +static void menuitem_list_remove_binding(void *obj) { + UiActiveMenuItemList *ls = obj; + UiList *list = ls->var->value; + CxList *bindings = list->obj; + if(bindings) { + (void)cxListFindRemove(bindings, obj); + if(cxListSize(bindings) == 0) { + cxListFree(bindings); + list->obj = NULL; + list->update = NULL; + } + } +} + void ui_add_menu_list(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) { + UiMenuItemList *il = (UiMenuItemList*)item; + const CxAllocator *a = obj->ctx->allocator; + UiVar *var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); + if (!var) { + return; + } + + UiActiveMenuItemList *ls = cxMalloc( + a, + sizeof(UiActiveMenuItemList)); + ls->object = obj; + ls->menu = parent; + ls->command_ids = cxArrayListCreate(a, sizeof(uint64_t), 16); + ls->index = pos; + ls->getvalue = il->getvalue; + ls->callback = il->callback; + ls->userdata = il->userdata; + ls->addseparator = il->addseparator; + ls->var = var; + + UiList *list = ls->var->value; + list->update = ui_menulist_update; + list->getselection = NULL; + list->setselection = NULL; + + // It is possible, that the UiVar is from a global shared context, + // used by multiple windows. To support this usecase, the list->obj + // binding object is a list of all connected UiActiveMenuItemList. + CxList *bindings = list->obj; + if(!bindings) { + bindings = cxLinkedListCreate(ls->var->from_ctx->mp->allocator, CX_STORE_POINTERS); + list->obj = bindings; + } + cxListAdd(bindings, ls); + + // The destruction of the toplevel obj must remove the menulist binding + uic_context_add_destructor(obj->ctx, menuitem_list_remove_binding, ls); + + ui_update_menuitem_list(ls); } +void ui_menulist_update(UiList *list, int ignored) { + CxList *bindings = list->obj; + CxIterator i = cxListIterator(bindings); + cx_foreach(UiActiveMenuItemList *, ls, i) { + ui_update_menuitem_list(ls); + } +} + +void ui_update_menuitem_list(UiActiveMenuItemList *list) { + UiObject *obj = list->object; + + UiList *ls; + if(list->var && list->var->value) { + ls = list->var->value; + } else { + return; + } + + CxIterator i = cxListIterator(list->command_ids); + cx_foreach(uint64_t *, id, i) { + DeleteMenu(list->menu, *id, MF_BYCOMMAND); + } + cxListClear(list->command_ids); // TODO: we could reuse some of the ids + + ui_getvaluefunc getvalue = list->getvalue; + void* elm = ui_list_first(ls); + + int pos = list->index; + while(elm) { + char *label = (char*) (getvalue ? getvalue(elm, 0) : elm); + if (!label) { + label = ""; + } + + uint64_t id = ++obj->ctx->command_id_counter; + InsertMenu(list->menu, pos++, MF_STRING, id, label); + cxListAdd(list->command_ids, &id); + + elm = ui_list_next(ls); + } +} + + void ui_add_menu_checklist(HMENU parent, int pos, UiMenuItemI *item, UiObject *obj) { } diff --git a/ui/win32/menu.h b/ui/win32/menu.h index 48b0c5f..efb5d48 100644 --- a/ui/win32/menu.h +++ b/ui/win32/menu.h @@ -48,6 +48,19 @@ typedef struct UiStateMenuItem { UiBool state; } UiStateMenuItem; +typedef struct UiActiveMenuItemList UiActiveMenuItemList; +struct UiActiveMenuItemList { + UiObject *object; + HMENU menu; + CxList *command_ids; + int index; + UiVar *var; + ui_getvaluefunc getvalue; + ui_callback callback; + void *userdata; + bool addseparator; +}; + typedef void(*ui_menu_add_f)(HMENU, int, UiMenuItemI*, UiObject*); HMENU ui_create_main_menu(UiObject *obj); @@ -67,6 +80,9 @@ void ui_checkitem_set(UiInteger *i, int64_t value); int64_t ui_radioitem_get(UiInteger *i); void ui_radioitem_set(UiInteger *i, int64_t value); +void ui_menulist_update(UiList *list, int ignored); +void ui_update_menuitem_list(UiActiveMenuItemList *list); + #ifdef __cplusplus } #endif