Sun, 13 Sep 2026 14:30:34 +0200
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;