ui/win32/menu.c

changeset 1060
74bed80a0503
parent 1059
226140134594
--- 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) {
 
 }

mercurial