Tue, 27 Jan 2026 13:08:30 +0100
implement menu itemlist (Win32)
| application/main.c | file | annotate | diff | comparison | revisions | |
| ui/win32/menu.c | file | annotate | diff | comparison | revisions | |
| ui/win32/menu.h | file | annotate | diff | comparison | revisions |
--- a/application/main.c Tue Jan 27 12:23:40 2026 +0100 +++ b/application/main.c Tue Jan 27 13:08:30 2026 +0100 @@ -1307,6 +1307,14 @@ printf("menu item clicked\n"); } +static int nitem = 5; +void action_menu_additem(UiEvent *event, void *userdata) { + char buf[32]; + snprintf(buf, 32, "New Item %d", nitem++); + ui_list_append(menu_list, strdup(buf)); + ui_list_update(menu_list); +} + int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { //int main() { ui_init("app1", 0, NULL); @@ -1330,7 +1338,7 @@ ui_menuitem("Close"); } ui_menu("Test") { - ui_menuitem("Item 0"); + ui_menuitem("Add Item", .onclick = action_menu_additem); ui_menuseparator(); ui_menu_itemlist(.varname = "menulist"); ui_menuseparator();
--- a/ui/win32/menu.c Tue Jan 27 12:23:40 2026 +0100 +++ b/ui/win32/menu.c Tue Jan 27 13:08:30 2026 +0100 @@ -55,7 +55,6 @@ menu = next && next->type == UI_MENU ? (UiMenu*)next : NULL; } - return hMenu; } @@ -259,10 +258,106 @@ } } +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) { }
--- a/ui/win32/menu.h Tue Jan 27 12:23:40 2026 +0100 +++ b/ui/win32/menu.h Tue Jan 27 13:08:30 2026 +0100 @@ -48,6 +48,19 @@ 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 @@ 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