ui/gtk/menu.c

changeset 553
90e38db0c755
parent 440
7c4b9cba09ca
child 558
62aefa91ffc5
--- a/ui/gtk/menu.c	Sun Apr 06 08:50:27 2025 +0200
+++ b/ui/gtk/menu.c	Sun Apr 06 13:28:35 2025 +0200
@@ -233,6 +233,20 @@
 }
 */
 
+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 add_menuitem_list_widget(GtkWidget *p, int index, UiMenuItemI *item, UiObject *obj) {
     UiMenuItemList *il = (UiMenuItemList*)item;
     const CxAllocator *a = obj->ctx->allocator;
@@ -247,21 +261,45 @@
     ls->oldcount = 0;
     ls->getvalue = il->getvalue;
     
-    UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
-    ls->list = var->value;
+    //UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+    UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
+    ls->var = var;
+    if(var) {
+        UiList *list = 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(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+            list->obj = bindings;
+        }
+        cxListAdd(bindings, ls);
+        
+        // The destruction of the toplevel obj must remove the menulist binding
+        cxMempoolRegister(obj->ctx->mp, ls, menuitem_list_remove_binding);
+        
+        ui_update_menuitem_list(ls);
+    }
     
     ls->callback = il->callback;
     ls->userdata = il->userdata;
-    
-    UiObserver *observer = ui_observer_new((ui_callback)ui_update_menuitem_list, ls);
-    ls->list->observers = ui_obsvlist_add(ls->list->observers, observer);
-    uic_list_register_observer_destructor(obj->ctx, ls->list, observer);
-    
-    ui_update_menuitem_list(NULL, 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(UiEvent *event, UiActiveMenuItemList *list) {    
+void ui_update_menuitem_list(UiActiveMenuItemList *list) {    
     // remove old items
     if(list->oldcount > 0) {
         int i = 0;
@@ -276,7 +314,9 @@
         }
     }
     
-    void* elm = ui_list_first(list->list);
+    UiList *ls = list->var->value;
+    
+    void* elm = ui_list_first(ls);
     if(elm) {
         GtkWidget *widget = gtk_separator_menu_item_new();
         gtk_menu_shell_insert(list->menu, widget, list->index);
@@ -312,7 +352,7 @@
                 event);
         }
         
-        elm = ui_list_next(list->list);
+        elm = ui_list_next(ls);
         i++;
     }
     
@@ -506,6 +546,20 @@
     
 }
 
+static void menuitem_list_remove_binding(void *obj) {
+    UiActiveGMenuItemList *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_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) {
     UiMenuItemList *il = (UiMenuItemList*)item; 
     
@@ -521,17 +575,34 @@
     ls->oldcount = 0;
     ls->getvalue = il->getvalue;
     
-    UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+    //UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST);
+    UiVar* var = uic_create_var(obj->ctx, il->varname, UI_VAR_LIST);
     ls->var = var;
-    UiList *list = var->value;
+    if(var) {
+        UiList *list = 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(var->from_ctx->mp->allocator, NULL, CX_STORE_POINTERS);
+            list->obj = bindings;
+        }
+        cxListAdd(bindings, ls);
+        
+        // The destruction of the toplevel obj must remove the menulist binding
+        cxMempoolRegister(obj->ctx->mp, ls, menuitem_list_remove_binding);
+        
+        ui_update_gmenu_item_list(ls);
+    }
     
     ls->callback = il->callback;
     ls->userdata = il->userdata;
     
-    UiObserver *observer = ui_observer_new((ui_callback)ui_update_gmenu_item_list, ls);
-    list->observers = ui_obsvlist_add(list->observers, observer);
-    uic_list_register_observer_destructor(obj->ctx, list, observer);
-    
     GSimpleAction *action = g_simple_action_new(item->id, g_variant_type_new("i"));
     g_action_map_add_action(obj->ctx->action_map, G_ACTION(action));
     snprintf(ls->action, 32, "win.%s", item->id);
@@ -554,8 +625,6 @@
             "destroy",
             G_CALLBACK(ui_destroy_userdata),
             event);
-    
-    ui_update_gmenu_item_list(NULL, ls);
 }
 
 void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) {
@@ -588,7 +657,15 @@
     
 }
 
-void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list) {
+void ui_menulist_update(UiList *list, int ignored) {
+    CxList *bindings = list->obj;
+    CxIterator i = cxListIterator(bindings);
+    cx_foreach(UiActiveGMenuItemList *, ls, i) {
+        ui_update_gmenu_item_list(ls);
+    }
+}
+
+void ui_update_gmenu_item_list(UiActiveGMenuItemList *list) {
     // remove old items
     for(int i=0;i<list->oldcount;i++) {
         g_menu_remove(list->menu, list->index);

mercurial