fix sourcelist2 contextmenu on gtk4

Sun, 13 Sep 2026 14:30:34 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 13 Sep 2026 14:30:34 +0200
changeset 1256
da0899b1699d
parent 1255
4681df3739f7
child 1257
e201fe56f890

fix sourcelist2 contextmenu on gtk4

application/main.c file | annotate | diff | comparison | revisions
ui/gtk/list.c file | annotate | diff | comparison | revisions
ui/gtk/list.h file | annotate | diff | comparison | revisions
ui/gtk/menu.c file | annotate | diff | comparison | revisions
ui/gtk/menu.h file | annotate | diff | comparison | revisions
ui/ui/list.h file | annotate | diff | comparison | revisions
--- a/application/main.c	Sun Sep 13 11:50:14 2026 +0200
+++ b/application/main.c	Sun Sep 13 14:30:34 2026 +0200
@@ -759,6 +759,10 @@
     printf("content toggled: %d : %d\n", i, event->intval);
 }
 
+static void sourcelist_contextmenu_popup(UiEvent *event, void *userdata) {
+    printf("contextmenu popup at item: %d\n", event->intval);
+}
+
 void application_startup(UiEvent *event, void *data) {
     // test window destruction
     UiObject *testobj = ui_window("testwindow");
@@ -985,7 +989,7 @@
             //UiModel *model = ui_model(obj->ctx, UI_STRING, "test", -1);
             //ui_treeview(obj, .fill = TRUE, .model = model, .show_header = TRUE, .getvalue = tree_getvalue, .getdepth = tree_getdepth, .varname = "tree");
             
-            ui_sourcelist2(obj, .fill = TRUE, .getvalue = sourcelist2_getvalue, .varname = "tree", .onactivate = sourcelist2_activate);
+            ui_sourcelist2(obj, .fill = TRUE, .getvalue = sourcelist2_getvalue, .varname = "tree", .onactivate = sourcelist2_activate, .contextmenu = sourcelist_menu, .oncontextmenupopup = sourcelist_contextmenu_popup);
         }
     }
     
--- a/ui/gtk/list.c	Sun Sep 13 11:50:14 2026 +0200
+++ b/ui/gtk/list.c	Sun Sep 13 14:30:34 2026 +0200
@@ -3084,6 +3084,10 @@
     cxListFree(v->rows);
     free(v->onactivate_action);
     free(v->onbuttonclick_action);
+    free(v->oncontextmenupopup_action);
+    if(v->contextmenu) {
+        ui_menubuilder_unref(v->contextmenu);
+    }
     free(v);
 }
 
@@ -3126,6 +3130,15 @@
     uilistbox->onbuttonclick = args->onbuttonclick;
     uilistbox->onbuttonclickdata = args->onbuttonclickdata;
     uilistbox->onbuttonclick_action = args->onbuttonclick_action ? strdup(args->onbuttonclick_action) : NULL;
+    uilistbox->oncontextmenupopup = args->oncontextmenupopup;
+    uilistbox->oncontextmenupopupdata = args->oncontextmenupopupdata;
+    uilistbox->oncontextmenupopup_action = args->oncontextmenupopup_action ? strdup(args->oncontextmenupopup_action) : NULL;
+    uilistbox->contextmenu = NULL;
+    
+    if(args->contextmenu) {
+        uilistbox->contextmenu = args->contextmenu;
+        ui_menubuilder_ref(args->contextmenu);
+    }
     
     UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST);
     uilistbox->var = var;
@@ -3143,11 +3156,6 @@
     g_object_set_data(G_OBJECT(scroll_area), "ui_listbox", uilistbox);
     g_object_set_data(G_OBJECT(listbox), "ui_listbox", uilistbox);
     
-    if(args->contextmenu) {
-        UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, listbox);
-        ui_widget_set_contextmenu(listbox, menu);
-    }
-    
     // signals
     g_signal_connect(
                 listbox,
@@ -3231,10 +3239,34 @@
 
 #endif
 
+static void listbox2_on_contextmenu_popup(UiEvent *ununsed, void *userdata) {
+    UiListBox2Row *row = userdata;
+    
+    UiListSelection sel;
+    sel.count = 1;
+    sel.rows = &row->index;
+    
+    UiEvent event;
+    event.obj = row->listbox->obj;
+    event.window = event.obj->window;
+    event.document = event.obj->ctx->document;
+    event.eventdata = &sel;
+    event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION;
+    event.intval = row->index;
+    event.set = 0;
+    
+    if(row->listbox->oncontextmenupopup) {
+        row->listbox->oncontextmenupopup(&event, row->listbox->oncontextmenupopupdata);
+    }
+    if(row->listbox->oncontextmenupopup_action) {
+        uic_action_callback(&event, row->listbox->oncontextmenupopup_action);
+    }
+}
+
 #define LISTBOX2_ITEM_HBOX_SPACING 10
 static void listbox2_update_item(UiListBox2Row *rowdata, GtkWidget *row, UiSourceListItem *item, int identation) { 
     GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, LISTBOX2_ITEM_HBOX_SPACING);
-    
+       
     rowdata->expanded = 1;
     GtkWidget *expander = gtk_image_new();
     GtkWidget *expander0 = expander;
@@ -3370,6 +3402,14 @@
         UiListBox2Row *row_data_ptr = cxListLast(listbox->rows);
         listbox2_update_item(row_data_ptr, row0, &item, item.depth - (item.depth > 0 ? heading : 0));
         
+        if(listbox->contextmenu) {
+            row_data_ptr->contextmenu_popup_handler.callback = listbox2_on_contextmenu_popup;
+            row_data_ptr->contextmenu_popup_handler.userdata = row_data_ptr;
+            g_object_set_data(G_OBJECT(row), "ui_contextmenu_handler", &row_data_ptr->contextmenu_popup_handler);
+            UIMENU menu = ui_contextmenu_create(listbox->contextmenu, listbox->obj, row);
+            ui_widget_set_contextmenu(row, menu);
+        }
+        
         if(item.depth >= stack_size) {
             stack_size += 8;
             stack = realloc(stack, stack_size * sizeof(UiListBox2Row*));
--- a/ui/gtk/list.h	Sun Sep 13 11:50:14 2026 +0200
+++ b/ui/gtk/list.h	Sun Sep 13 14:30:34 2026 +0200
@@ -32,6 +32,7 @@
 #include "../ui/list.h"
 #include "../ui/tree.h"
 #include "toolkit.h"
+#include "menu.h"
 
 #include <cx/array_list.h>
 
@@ -152,6 +153,7 @@
     CxList                       *rows;
     ui_sourcelist_getvalue_func  getvalue;
     void                         *getvaluedata;
+    UiMenuBuilder                *contextmenu;
     
     ui_callback                  onactivate;
     void                         *onactivatedata;
@@ -159,6 +161,9 @@
     ui_callback                  onbuttonclick;
     void                         *onbuttonclickdata;
     char                         *onbuttonclick_action;
+    ui_callback                  oncontextmenupopup;
+    void                         *oncontextmenupopupdata;
+    char                         *oncontextmenupopup_action;
 };
 
 typedef struct UiListBox2Row {
@@ -170,6 +175,7 @@
     int           depth;
     UiBool        expanded;
     UiBool        heading;
+    UiContextMenuPopupCallback contextmenu_popup_handler;
 } UiListBox2Row;
 
 void* ui_null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult);
--- a/ui/gtk/menu.c	Sun Sep 13 11:50:14 2026 +0200
+++ b/ui/gtk/menu.c	Sun Sep 13 14:30:34 2026 +0200
@@ -46,6 +46,13 @@
 #include <cx/array_list.h>
 #include <cx/printf.h>
 
+static void contextmenu_handler(GtkWidget *widget) {
+    UiContextMenuPopupCallback *cb = g_object_get_data(G_OBJECT(widget), "ui_contextmenu_handler");
+    if(cb && cb->callback) {
+        cb->callback(NULL, cb->userdata);
+    }
+}
+
 #if GTK_MAJOR_VERSION <= 3
 
 static ui_menu_add_f createMenuItem[] = {
@@ -502,6 +509,7 @@
 }
 
 void ui_contextmenu_popup(UIMENU menu, GtkWidget *widget, int x, int y) {
+    contextmenu_handler(widget);
     gtk_widget_show_all(GTK_WIDGET(menu));
 #if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 16
     gtk_menu_popup_at_pointer(menu, NULL);
@@ -1015,6 +1023,7 @@
 }
 
 void ui_contextmenu_popup(UIMENU menu, UIWIDGET widget, int x, int y) {
+    contextmenu_handler(widget);
     gtk_popover_set_pointing_to(GTK_POPOVER(menu), &(GdkRectangle){ x, y, 0, 0 });
     gtk_popover_popup(GTK_POPOVER(menu));
 }
--- a/ui/gtk/menu.h	Sun Sep 13 11:50:14 2026 +0200
+++ b/ui/gtk/menu.h	Sun Sep 13 14:30:34 2026 +0200
@@ -44,6 +44,12 @@
     
 GtkWidget *ui_create_menubar(UiObject *obj);
 
+typedef struct UiContextMenuPopupCallback {
+    /* this callback will be called without an event ptr */
+    ui_callback callback;
+    void *userdata;
+} UiContextMenuPopupCallback;
+
 #if GTK_MAJOR_VERSION <= 3
 
 typedef struct UiActiveMenuItemList UiActiveMenuItemList;
--- a/ui/ui/list.h	Sun Sep 13 11:50:14 2026 +0200
+++ b/ui/ui/list.h	Sun Sep 13 14:30:34 2026 +0200
@@ -359,6 +359,14 @@
     void        *onbuttonclickdata;
     const char  *onbuttonclick_action;
     
+    /*
+     * called before the contextmenu popups and contains informations
+     * about the row, where the menu was activated
+     */
+    ui_callback oncontextmenupopup;
+    void        *oncontextmenupopupdata;
+    const char  *oncontextmenupopup_action;
+    
     UiMenuBuilder *contextmenu;
     
     const int *states;  

mercurial