# HG changeset patch # User Olaf Wintermann # Date 1732614045 -3600 # Node ID e27526429d856590c43bbb397f879ee7e5b16f95 # Parent 5360027fb2826ae7f266729117ddf41bb854c568 add context menu, implement 'Select All' diff -r 5360027fb282 -r e27526429d85 application/application.c --- a/application/application.c Sun Nov 17 15:19:32 2024 +0100 +++ b/application/application.c Tue Nov 26 10:40:45 2024 +0100 @@ -307,6 +307,14 @@ ui_listselection_free(sel); } +void action_selectall(UiEvent *event, void *data) { + DavBrowser *browser = event->document; + int nres = ui_list_count(browser->resources); + for(int i=0;iresources, i); + } +} + static void newfiledialog_result(UiEvent *event, void *data) { DavBrowser *browser = event->document; char *path = event->eventdata; diff -r 5360027fb282 -r e27526429d85 application/application.h --- a/application/application.h Sun Nov 17 15:19:32 2024 +0100 +++ b/application/application.h Tue Nov 26 10:40:45 2024 +0100 @@ -184,6 +184,8 @@ void action_delete(UiEvent *event, void *data); +void action_selectall(UiEvent *event, void *data); + void action_newfile(UiEvent *event, void *data); void action_mkcol(UiEvent *event, void *data); diff -r 5360027fb282 -r e27526429d85 application/window.c --- a/application/window.c Sun Nov 17 15:19:32 2024 +0100 +++ b/application/window.c Tue Nov 26 10:40:45 2024 +0100 @@ -42,9 +42,29 @@ static UiPathElm* dav_get_pathelm(const char *full_path, size_t len, size_t *ret_nelm, void* data); +static UiMenuBuilder *contextmenu; + void window_init(void) { folder_icon = ui_foldericon(16); file_icon = ui_fileicon(16); + + // initialize the browser context menu + ui_contextmenu(&contextmenu) { + ui_menuitem(.label = "New Folder", .onclick = action_mkcol, .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION)); + ui_menuitem(.label = "New File", .onclick = action_newfile, .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION)); + ui_menuseparator(); + //ui_menuitem(.label = "Cut", .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION, APP_STATE_BROWSER_SELECTION)); + //ui_menuitem(.label = "Copy", .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION, APP_STATE_BROWSER_SELECTION)); + //ui_menuitem(.label = "Paste", .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION, APP_STATE_BROWSER_SELECTION)); + ui_menuitem(.label = "Download", .onclick = action_download, .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION, APP_STATE_BROWSER_SELECTION)); + ui_menuitem(.label = "Delete", .onclick = action_delete, .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION)); + ui_menuitem(.label = "Select All", .onclick = action_selectall, .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION)); + ui_menuseparator(); + ui_menuitem(.label = "Rename", .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION, APP_STATE_BROWSER_SELECTION)); + ui_menuseparator(); + ui_menuitem("Open Properties", .onclick = action_open_properties, .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION, APP_STATE_BROWSER_SELECTION)); + ui_menuitem("Open as Text File", .onclick = action_open_properties, .onclickdata = "text/plain", .groups = UI_GROUPS(APP_STATE_BROWSER_SESSION, APP_STATE_BROWSER_SELECTION)); + } } UiObject* window_create(void) { @@ -75,7 +95,15 @@ model->columnsize[0] = -1; model->columnsize[1] = 150; model->getvalue = (ui_getvaluefunc) window_resource_table_getvalue; - ui_table(obj, .fill = UI_ON, .model = model, .onselection = action_list_selection, .onactivate = action_list_activate, .ondrop = action_dnd_drop, .varname = "reslist", .multiselection = TRUE); + ui_table(obj, + .fill = UI_ON, + .model = model, + .onselection = action_list_selection, + .onactivate = action_list_activate, + .ondrop = action_dnd_drop, + .varname = "reslist", + .multiselection = TRUE, + .contextmenu = contextmenu); // status bar diff -r 5360027fb282 -r e27526429d85 ui/common/context.c --- a/ui/common/context.c Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/common/context.c Tue Nov 26 10:40:45 2024 +0100 @@ -62,7 +62,7 @@ ctx->vars = cxHashMapCreate(mp->allocator, CX_STORE_POINTERS, 16); ctx->documents = cxLinkedListCreate(mp->allocator, cx_cmp_intptr, CX_STORE_POINTERS); - ctx->group_widgets = cxLinkedListCreate(mp->allocator, NULL, sizeof(UiGroupWidget)); + ctx->group_widgets = cxLinkedListCreate(mp->allocator, cx_cmp_ptr, sizeof(UiGroupWidget)); ctx->groups = cxArrayListCreate(mp->allocator, cx_cmp_int, sizeof(int), 32); ctx->attach_document = uic_context_attach_document; @@ -331,6 +331,7 @@ t->obj = f->obj; t->update = f->update; t->getselection = f->getselection; + t->setselection = f->setselection; } UiVar tmp = *from; @@ -556,6 +557,10 @@ cxListAdd(ctx->group_widgets, &gw); } +void uic_remove_group_widget(UiContext *ctx, void *widget) { + (void)cxListFindRemove(ctx->group_widgets, widget); +} + UIEXPORT void *ui_allocator(UiContext *ctx) { return (void*)ctx->allocator; } diff -r 5360027fb282 -r e27526429d85 ui/common/context.h --- a/ui/common/context.h Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/common/context.h Tue Nov 26 10:40:45 2024 +0100 @@ -140,7 +140,7 @@ void uic_check_group_widgets(UiContext *ctx); void uic_add_group_widget(UiContext *ctx, void *widget, ui_enablefunc enable, CxList *groups); void uic_add_group_widget_i(UiContext *ctx, void *widget, ui_enablefunc enable, const int *groups, size_t numgroups); - +void uic_remove_group_widget(UiContext *ctx, void *widget); #ifdef __cplusplus } diff -r 5360027fb282 -r e27526429d85 ui/common/menu.c --- a/ui/common/menu.c Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/common/menu.c Tue Nov 26 10:40:45 2024 +0100 @@ -35,23 +35,27 @@ #include -static UiMenu *menus_begin; -static UiMenu *menus_end; -static CxList *current; +static UiMenuBuilder *current_builder; +static UiMenuBuilder global_builder; static int menu_item_counter = 0; +void uic_menu_init(void) { + global_builder.current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + current_builder = &global_builder; +} + static void add_menu(UiMenu *menu) { cx_linked_list_add( - (void**)&menus_begin, - (void**)&menus_end, + (void**)¤t_builder->menus_begin, + (void**)¤t_builder->menus_end, offsetof(UiMenu, item.prev), offsetof(UiMenu, item.next), menu); } static void add_item(UiMenuItemI *item) { - UiMenu *menu = cxListAt(current, 0); + UiMenu *menu = cxListAt(current_builder->current, 0); cx_linked_list_add( (void**)&menu->items_begin, (void**)&menu->items_end, @@ -86,11 +90,7 @@ return NULL; } -void ui_menu_create(const char *label) { - if(!current) { - current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); - } - +void ui_menu_create(const char *label) { // create menu UiMenu *menu = malloc(sizeof(UiMenu)); mitem_set_id(&menu->item); @@ -105,7 +105,7 @@ menu->end = 0; - if (cxListSize(current) == 0) { + if (cxListSize(current_builder->current) == 0) { add_menu(menu); } else { @@ -115,16 +115,15 @@ } UIEXPORT void ui_menu_end(void) { - cxListRemove(current, 0); + cxListRemove(current_builder->current, 0); + if(cxListSize(current_builder->current) == 0) { + current_builder = &global_builder; + } } void ui_menuitem_create(UiMenuItemArgs args) { - if (!current) { - return; // error? - } - UiMenuItem* item = malloc(sizeof(UiMenuItem)); mitem_set_id(&item->item); item->item.prev = NULL; @@ -142,10 +141,6 @@ } void ui_menuseparator() { - if(!current) { - return; - } - UiMenuItemI *item = malloc(sizeof(UiMenuItemI)); item->id[0] = 0; item->prev = NULL; @@ -156,10 +151,6 @@ } void ui_menu_toggleitem_create(UiMenuToggleItemArgs args) { - if(!current) { - return; - } - UiMenuCheckItem *item = malloc(sizeof(UiMenuCheckItem)); mitem_set_id(&item->item); item->item.prev = NULL; @@ -178,10 +169,6 @@ } void ui_menu_radioitem_create(UiMenuToggleItemArgs args) { - if (!current) { - return; - } - UiMenuCheckItem* item = malloc(sizeof(UiMenuCheckItem)); mitem_set_id(&item->item); item->item.prev = NULL; @@ -200,10 +187,6 @@ } void ui_menu_itemlist_create(UiMenuItemListArgs args) { - if(!current) { - return; - } - UiMenuItemList*item = malloc(sizeof(UiMenuItemList)); mitem_set_id(&item->item); item->item.prev = NULL; @@ -218,10 +201,6 @@ } void ui_menu_checkitemlist_create(UiMenuItemListArgs args) { - if (!current) { - return; - } - UiMenuItemList* item = malloc(sizeof(UiMenuItemList)); mitem_set_id(&item->item); item->item.prev = NULL; @@ -235,10 +214,6 @@ } void ui_menu_radioitemlist_create(UiMenuItemListArgs args) { - if (!current) { - return; - } - UiMenuItemList* item = malloc(sizeof(UiMenuItemList)); mitem_set_id(&item->item); item->item.prev = NULL; @@ -253,27 +228,101 @@ void uic_add_menu_to_stack(UiMenu* menu) { - if (!current) { - current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); - } - - cxListInsert(current, 0, menu); + cxListInsert(current_builder->current, 0, menu); } UiMenu* uic_get_menu_list(void) { - return menus_begin; + return current_builder->menus_begin; } UIEXPORT void ui_menu_close(void) { - UiMenu* menu = cxListAt(current, 0); + UiMenu* menu = cxListAt(current_builder->current, 0); menu->end = 1; } UIEXPORT int ui_menu_is_open(void) { - UiMenu* menu = cxListAt(current, 0); + UiMenu* menu = cxListAt(current_builder->current, 0); if (menu->end) { ui_menu_end(); return 0; } return 1; } + + +void ui_contextmenu_builder(UiMenuBuilder **out_builder) { + UiMenuBuilder *builder = malloc(sizeof(UiMenuBuilder)); + builder->menus_begin = NULL; + builder->menus_end = NULL; + builder->current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); + current_builder = builder; + *out_builder = builder; + + ui_menu_create(NULL); +} + + + +static void free_menuitem(UiMenuItemI *item) { + switch(item->type) { + default: break; + case UI_MENU: { + UiMenu *menu = (UiMenu*)item; + UiMenuItemI *m = menu->items_begin; + while(m) { + UiMenuItemI *next = m->next; + free_menuitem(m); + m = next; + } + break; + } + case UI_MENU_ITEM: { + UiMenuItem *i = (UiMenuItem*)item; + free(i->groups); + free(i->label); + free(i->stockid); + free(i->icon); + break; + } + case UI_MENU_CHECK_ITEM: { + UiMenuCheckItem *i = (UiMenuCheckItem*)item; + free(i->groups); + free(i->label); + free(i->stockid); + free(i->icon); + free(i->varname); + break; + } + case UI_MENU_RADIO_ITEM: { + UiMenuRadioItem *i = (UiMenuRadioItem*)item; + free(i->groups); + free(i->label); + free(i->stockid); + free(i->icon); + //free(i->varname); + break; + } + case UI_MENU_ITEM_LIST: { + break; + } + case UI_MENU_CHECKITEM_LIST: { + break; + } + case UI_MENU_RADIOITEM_LIST: { + break; + } + } + + free(item); +} + +void ui_menubuilder_free(UiMenuBuilder *builder) { + UiMenuItemI *m = &builder->menus_begin->item; + while(m) { + UiMenuItemI *next = m->next; + free_menuitem(m); + m = next; + } + cxListDestroy(builder->current); + free(builder); +} diff -r 5360027fb282 -r e27526429d85 ui/common/menu.h --- a/ui/common/menu.h Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/common/menu.h Tue Nov 26 10:40:45 2024 +0100 @@ -76,9 +76,9 @@ struct UiMenuItem { UiMenuItemI item; ui_callback callback; - const char *label; - const char *stockid; - const char *icon; + char *label; + char *stockid; + char *icon; void *userdata; int *groups; size_t ngroups; @@ -86,10 +86,10 @@ struct UiMenuCheckItem { UiMenuItemI item; - const char* label; - const char* stockid; - const char* icon; - const char* varname; + char *label; + char *stockid; + char *icon; + char *varname; ui_callback callback; void *userdata; int *groups; @@ -98,11 +98,11 @@ struct UiMenuRadioItem { UiMenuItemI item; - const char* label; - const char* stockid; - const char* icon; + char *label; + char *stockid; + char *icon; ui_callback callback; - void* userdata; + void *userdata; int *groups; size_t ngroups; }; @@ -112,10 +112,19 @@ ui_getvaluefunc getvalue; ui_callback callback; void *userdata; - const char *varname; + char *varname; }; + +struct UiMenuBuilder { + UiMenu *menus_begin; + UiMenu *menus_end; + CxList *current; +}; + +void uic_menu_init(void); + UiMenu* uic_get_menu_list(void); void uic_add_menu_to_stack(UiMenu* menu); diff -r 5360027fb282 -r e27526429d85 ui/gtk/image.c --- a/ui/gtk/image.c Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/gtk/image.c Tue Nov 26 10:40:45 2024 +0100 @@ -29,6 +29,7 @@ #include "image.h" #include "container.h" +#include "menu.h" #include "../common/context.h" #include "../common/object.h" @@ -51,6 +52,7 @@ gtk_container_add(GTK_CONTAINER(eventbox), image); #else SCROLLEDWINDOW_SET_CHILD(scrolledwindow, image); + GtkWidget *eventbox = image; #endif UI_APPLY_LAYOUT1(current, args); @@ -70,6 +72,11 @@ } } + if(args.contextmenu) { + UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, eventbox); + ui_widget_set_contextmenu(eventbox, menu); + } + return scrolledwindow; } diff -r 5360027fb282 -r e27526429d85 ui/gtk/list.c --- a/ui/gtk/list.c Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/gtk/list.c Tue Nov 26 10:40:45 2024 +0100 @@ -37,6 +37,7 @@ #include "list.h" #include "icon.h" +#include "menu.h" void* ui_strmodel_getvalue(void *elm, int column) { @@ -235,6 +236,10 @@ G_CALLBACK(ui_listview_selection_event), event); } + if(args.contextmenu) { + UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, view); + ui_widget_set_contextmenu(view, menu); + } // add widget to the current container @@ -370,6 +375,7 @@ // bind var list->update = ui_listview_update; list->getselection = ui_listview_getselection; + list->setselection = ui_listview_setselection; list->obj = tableview; // add callback @@ -396,8 +402,8 @@ event); } // TODO: destroy callback - + GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view)); if(args.multiselection) { gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); @@ -411,6 +417,11 @@ GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS SCROLLEDWINDOW_SET_CHILD(scroll_area, view); + if(args.contextmenu) { + UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, scroll_area); + ui_widget_set_contextmenu(scroll_area, menu); + } + UI_APPLY_LAYOUT1(current, args); current->container->add(current->container, scroll_area, FALSE); diff -r 5360027fb282 -r e27526429d85 ui/gtk/menu.c --- a/ui/gtk/menu.c Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/gtk/menu.c Tue Nov 26 10:40:45 2024 +0100 @@ -355,34 +355,28 @@ * widget menu functions */ +UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, UIWIDGET widget) { + GtkWidget *menu_widget = gtk_menu_new(); + ui_add_menu_items(menu_widget, 0, builder->menus_begin, obj); + return GTK_MENU(menu_widget); +} + static gboolean ui_button_press_event(GtkWidget *widget, GdkEvent *event, GtkMenu *menu) { if(event->type == GDK_BUTTON_PRESS) { GdkEventButton *e = (GdkEventButton*)event; if(e->button == 3) { gtk_widget_show_all(GTK_WIDGET(menu)); - ui_contextmenu_popup(menu); - return TRUE; + ui_contextmenu_popup(menu, widget, 0, 0); } } return FALSE; } -UIMENU ui_contextmenu(UiObject *obj) { - UiContainer *ct = uic_get_current_container(obj); - return ui_contextmenu_w(obj, ct->current); +void ui_widget_set_contextmenu(GtkWidget *widget, GtkMenu *menu) { + g_signal_connect(widget, "button-press-event", (GCallback) ui_button_press_event, menu); } -UIMENU ui_contextmenu_w(UiObject *obj, UIWIDGET widget) { - UiContainer *ct = uic_get_current_container(obj); - - GtkMenu *menu = GTK_MENU(gtk_menu_new()); - g_signal_connect(widget, "button-press-event", (GCallback) ui_button_press_event, menu); - - ct->menu = menu; - return menu; -} - -void ui_contextmenu_popup(UIMENU menu) { +void ui_contextmenu_popup(UIMENU menu, GtkWidget *widget, int x, int y) { #if GTK_MAJOR_VERSION >= 3 && GTK_MINOR_VERSION >= 16 gtk_menu_popup_at_pointer(menu, NULL); #else @@ -390,116 +384,6 @@ #endif } -void ui_widget_menuitem(UiObject *obj, char *label, ui_callback f, void *userdata) { - ui_widget_menuitem_gr(obj, label, f, userdata, -1); -} - -void ui_widget_menuitem_gr(UiObject *obj, char *label, ui_callback f, void *userdata, ...) { - UiContainer *ct = uic_get_current_container(obj); - if(!ct->menu) { - return; - } - - // add groups - CxList *groups = NULL; - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - if(!groups) { - groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16); - } - cxListAdd(groups, &group); - } - va_end(ap); - - // create menuitem - GtkWidget *widget = gtk_menu_item_new_with_mnemonic(label); - gtk_widget_show(widget); - - if(f) { - UiEventData *event = malloc(sizeof(UiEventData)); - event->obj = obj; - event->userdata = userdata; - event->callback = f; - event->value = 0; - event->customdata = NULL; - - g_signal_connect( - widget, - "activate", - G_CALLBACK(ui_menu_event_wrapper), - event); - g_signal_connect( - widget, - "destroy", - G_CALLBACK(ui_destroy_userdata), - event); - } - - gtk_menu_shell_append(GTK_MENU_SHELL(ct->menu), widget); - - if(groups) { - uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups); - cxListDestroy(groups); - } -} - -void ui_widget_menuitem_st(UiObject *obj, char *stockid, ui_callback f, void *userdata) { - ui_widget_menuitem_stgr(obj, stockid, f, userdata, -1); -} - -void ui_widget_menuitem_stgr(UiObject *obj, char *stockid, ui_callback f, void *userdata, ...) { - UiContainer *ct = uic_get_current_container(obj); - if(!ct->menu) { - return; - } - - // add groups - CxList *groups = NULL; - va_list ap; - va_start(ap, userdata); - int group; - while((group = va_arg(ap, int)) != -1) { - if(!groups) { - groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16); - } - cxListAdd(groups, &group); - } - va_end(ap); - - // create menuitem - GtkWidget *widget = gtk_image_menu_item_new_from_stock(stockid, obj->ctx->accel_group); - gtk_widget_show(widget); - - if(f) { - UiEventData *event = malloc(sizeof(UiEventData)); - event->obj = obj; - event->userdata = userdata; - event->callback = f; - event->value = 0; - event->customdata = NULL; - - g_signal_connect( - widget, - "activate", - G_CALLBACK(ui_menu_event_wrapper), - event); - g_signal_connect( - widget, - "destroy", - G_CALLBACK(ui_destroy_userdata), - event); - } - - gtk_menu_shell_append(GTK_MENU_SHELL(ct->menu), widget); - - if(groups) { - uic_add_group_widget(obj->ctx, widget, (ui_enablefunc)ui_set_enabled, groups); - cxListDestroy(groups); - } -} - #endif /* GTK_MAJOR_VERSION <= 3 */ @@ -548,12 +432,23 @@ g_menu_append_submenu(parent, mi->label, G_MENU_MODEL(menu)); } +static void action_enable(GSimpleAction *action, int enabled) { + g_simple_action_set_enabled(action, enabled); +} + void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) { UiMenuItem *i = (UiMenuItem*)item; - + GSimpleAction *action = g_simple_action_new(item->id, NULL); g_action_map_add_action(obj->ctx->action_map, G_ACTION(action)); + if(i->groups) { + CxList *groups = cxArrayListCreateSimple(sizeof(int), i->ngroups); + cxListAddArray(groups, i->groups, i->ngroups); + uic_add_group_widget(obj->ctx, action, (ui_enablefunc)action_enable, groups); + cxListDestroy(groups); + } + if(i->callback != NULL) { UiEventData *event = malloc(sizeof(UiEventData)); event->obj = obj; @@ -702,4 +597,43 @@ list->oldcount = i; } + +/* --------------------- context menu / menubuilder --------------------- */ + +static void remove_popover(GtkWidget *object, GtkPopoverMenu *menu) { + gtk_widget_unparent(GTK_WIDGET(menu)); +} + +UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, GtkWidget *widget) { + GMenu *menu = g_menu_new(); + ui_gmenu_add_menu_items(menu, 0, builder->menus_begin, obj); + GtkWidget *contextmenu = gtk_popover_menu_new_from_model(G_MENU_MODEL(menu)); + gtk_popover_set_has_arrow(GTK_POPOVER(contextmenu), FALSE); + gtk_widget_set_halign(contextmenu, GTK_ALIGN_START); + gtk_widget_set_parent(GTK_WIDGET(contextmenu), widget); + g_signal_connect( + widget, + "destroy", + G_CALLBACK(remove_popover), + contextmenu); + return GTK_POPOVER_MENU(contextmenu); +} + +static void gesture_button_press(GtkGestureClick *gesture, gint n_press, gdouble x, gdouble y, gpointer user_data) { + gtk_popover_set_pointing_to(GTK_POPOVER(user_data), &(GdkRectangle){ x, y, 0, 0 }); + gtk_popover_popup(GTK_POPOVER(user_data)); +} + +void ui_widget_set_contextmenu(GtkWidget *widget, GtkPopoverMenu *menu) { + GtkGesture *gesture = gtk_gesture_click_new(); + gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), 3); + gtk_widget_add_controller(widget, GTK_EVENT_CONTROLLER(gesture)); + g_signal_connect(gesture, "pressed", G_CALLBACK(gesture_button_press), menu); +} + +void ui_contextmenu_popup(UIMENU menu, UIWIDGET widget, int x, int y) { + gtk_popover_set_pointing_to(GTK_POPOVER(menu), &(GdkRectangle){ x, y, 0, 0 }); + gtk_popover_popup(GTK_POPOVER(menu)); +} + #endif diff -r 5360027fb282 -r e27526429d85 ui/gtk/menu.h --- a/ui/gtk/menu.h Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/gtk/menu.h Tue Nov 26 10:40:45 2024 +0100 @@ -39,6 +39,9 @@ extern "C" { #endif +UIMENU ui_create_menu(UiMenuBuilder *builder, UiObject *obj); +void ui_widget_set_contextmenu(GtkWidget *widget, UIMENU menu); + #if GTK_MAJOR_VERSION <= 3 typedef struct UiActiveMenuItemList UiActiveMenuItemList; diff -r 5360027fb282 -r e27526429d85 ui/gtk/toolkit.c --- a/ui/gtk/toolkit.c Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/gtk/toolkit.c Tue Nov 26 10:40:45 2024 +0100 @@ -78,6 +78,7 @@ ui_css_init(); uic_docmgr_init(); + uic_menu_init(); uic_toolbar_init(); ui_image_init(); uic_load_app_properties(); diff -r 5360027fb282 -r e27526429d85 ui/ui/image.h --- a/ui/ui/image.h Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/ui/image.h Tue Nov 26 10:40:45 2024 +0100 @@ -52,6 +52,7 @@ UiBool autoscale; UiGeneric *value; const char *varname; + UiMenuBuilder *contextmenu; } UiImageViewerArgs; #define ui_imageviewer(obj, ...) ui_imageviewer_create(obj, (UiImageViewerArgs){ __VA_ARGS__ } ) diff -r 5360027fb282 -r e27526429d85 ui/ui/menu.h --- a/ui/ui/menu.h Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/ui/menu.h Tue Nov 26 10:40:45 2024 +0100 @@ -86,35 +86,18 @@ UIEXPORT void ui_menu_toggleitemlist_create(UiMenuItemListArgs args); UIEXPORT void ui_menu_radioitemlist_create(UiMenuItemListArgs args); -UIEXPORT void ui_menu_deprecated(char *label); -UIEXPORT void ui_submenu_deprecated(char *label); // deprecated -UIEXPORT void ui_submenu_end_deprecated(); // deprecated - -UIEXPORT void ui_menuitem_deprecated(char *label, ui_callback f, void *userdata); -UIEXPORT void ui_menuitem_st(char *stockid, ui_callback f, void *userdata); -UIEXPORT void ui_menuitem_gr(char *label, ui_callback f, void *userdata, ...); -UIEXPORT void ui_menuitem_stgr(char *stockid, ui_callback f, void *userdata, ...); - - -UIEXPORT void ui_checkitem_deprecated(char *label, ui_callback f, void *userdata); -UIEXPORT void ui_checkitem_nv_deprecated(char *label, char *vname); - -UIEXPORT void ui_menuitem_list_deprecated(UiList *items, ui_callback f, void *userdata); - UIEXPORT void ui_menu_end(void); // TODO: private /* * widget menu functions */ -UIEXPORT UIMENU ui_contextmenu(UiObject *obj); -UIEXPORT UIMENU ui_contextmenu_w(UiObject *obj, UIWIDGET widget); -UIEXPORT void ui_contextmenu_popup(UIMENU menu); + +#define ui_contextmenu(builder) for(ui_contextmenu_builder(builder);ui_menu_is_open();ui_menu_close()) -UIEXPORT void ui_widget_menuitem(UiObject *obj, char *label, ui_callback f, void *userdata); -UIEXPORT void ui_widget_menuitem_st(UiObject *obj, char *stockid, ui_callback f, void *userdata); -UIEXPORT void ui_widget_menuitem_gr(UiObject *obj, char *label, ui_callback f, void *userdata, ...); -UIEXPORT void ui_widget_menuitem_stgr(UiObject *obj, char *stockid, ui_callback f, void *userdata, ...); - +UIEXPORT void ui_contextmenu_builder(UiMenuBuilder **out_builder); +UIEXPORT void ui_menubuilder_free(UiMenuBuilder *builder); +UIEXPORT UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, UIWIDGET widget); +UIEXPORT void ui_contextmenu_popup(UIMENU menu, UIWIDGET widget, int x, int y); // used for macro UIEXPORT void ui_menu_close(void); diff -r 5360027fb282 -r e27526429d85 ui/ui/toolkit.h --- a/ui/ui/toolkit.h Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/ui/toolkit.h Tue Nov 26 10:40:45 2024 +0100 @@ -174,15 +174,16 @@ typedef struct UiListSelection UiListSelection; /* begin opaque types */ -typedef struct UiContext UiContext; -typedef struct UiContainer UiContainer; +typedef struct UiContext UiContext; +typedef struct UiContainer UiContainer; +typedef struct UiMenuBuilder UiMenuBuilder; -typedef struct UiIcon UiIcon; -typedef struct UiImage UiImage; +typedef struct UiIcon UiIcon; +typedef struct UiImage UiImage; -typedef struct UiDnD UiDnD; +typedef struct UiDnD UiDnD; -typedef struct UiThreadpool UiThreadpool; +typedef struct UiThreadpool UiThreadpool; /* end opaque types */ typedef struct UiTabbedPane UiTabbedPane; diff -r 5360027fb282 -r e27526429d85 ui/ui/tree.h --- a/ui/ui/tree.h Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/ui/tree.h Tue Nov 26 10:40:45 2024 +0100 @@ -130,6 +130,7 @@ ui_callback ondrop; void* ondropsdata; UiBool multiselection; + UiMenuBuilder *contextmenu; const int *groups; }; diff -r 5360027fb282 -r e27526429d85 ui/winui/appmenu.cpp --- a/ui/winui/appmenu.cpp Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/winui/appmenu.cpp Tue Nov 26 10:40:45 2024 +0100 @@ -285,3 +285,11 @@ return flyout; } + +UIMENU ui_contextmenu_create(UiMenuBuilder *builder, UiObject *obj, UIWIDGET widget) { + return NULL; +} + +void ui_contextmenu_popup(UIMENU menu, UIWIDGET widget, int x, int y) { + +} diff -r 5360027fb282 -r e27526429d85 ui/winui/toolkit.cpp --- a/ui/winui/toolkit.cpp Sun Nov 17 15:19:32 2024 +0100 +++ b/ui/winui/toolkit.cpp Tue Nov 26 10:40:45 2024 +0100 @@ -166,6 +166,7 @@ uic_init_global_context(); uic_docmgr_init(); + uic_menu_init(); uic_toolbar_init(); uic_load_app_properties();