# HG changeset patch # User Olaf Wintermann # Date 1727602177 -7200 # Node ID 450a813dc2a582084a50f184f1f7fa72debd47c7 # Parent 4918f9132552342e0949d469249df18321286b45 implement GTK4 gmenu item and item list diff -r 4918f9132552 -r 450a813dc2a5 application/main.c --- a/application/main.c Thu Sep 26 22:43:13 2024 +0200 +++ b/application/main.c Sun Sep 29 11:29:37 2024 +0200 @@ -39,6 +39,7 @@ UiString *path; UiDouble *progress; UiList *list; + UiList *menulist; UiInteger *radio; } MyDocument; @@ -101,8 +102,31 @@ return col == 0 ? str : "x"; } +static UiList *menu_list; +int new_item_count = 0; + +void action_add_menu_item(UiEvent *event, void *userdata) { + char str[64]; + snprintf(str, 64, "new item %d", new_item_count++); + + ui_list_append(menu_list, strdup(str)); + ui_list_notify(menu_list); +} + +void action_menu_list(UiEvent *event, void *userdata) { + printf("menu list item: %d\n", event->intval); +} + void application_startup(UiEvent *event, void *data) { - + // global list + UiContext *global = ui_global_context(); + menu_list = ui_list_new(global, "menulist"); + ui_list_append(menu_list, "menu list item 1"); + ui_list_append(menu_list, "menu list item 2"); + ui_list_append(menu_list, "menu list item 3"); + + + UiObject *obj = ui_window("Test", NULL); MyDocument *doc = create_doc(); @@ -171,10 +195,12 @@ ui_toolbar_item("Test6", .label = "Test 6", .onclick = action_toolbar_button); ui_toolbar_toggleitem("Toggle", .label = "Toggle", .onchange = action_toolbar_button); ui_toolbar_menu("Menu", .label = "Menu") { - ui_menuitem("Secondary Test", NULL, NULL); + ui_menuitem("Secondary Test", .onclick = action_toolbar_button, NULL); ui_menu("Secondary Sub") { ui_menuitem("Secondary subitem", NULL, NULL); } + ui_menu_itemlist(.varname = "menulist", .onselect=action_menu_list); + ui_menuitem("last", .onclick = action_add_menu_item); } ui_toolbar_add_default("Test", UI_TOOLBAR_LEFT); diff -r 4918f9132552 -r 450a813dc2a5 ui/common/context.h --- a/ui/common/context.h Thu Sep 26 22:43:13 2024 +0200 +++ b/ui/common/context.h Sun Sep 29 11:29:37 2024 +0200 @@ -77,9 +77,15 @@ char *title; -#if UI_GTK2 || UI_GTK3 +#ifdef UI_GTK +#if GTK_CHECK_VERSION(4, 0, 0) + GActionMap *action_map; +#elif UI_GTK2 || UI_GTK3 GtkAccelGroup *accel_group; +#endif #endif + + ui_callback close_callback; void *close_data; diff -r 4918f9132552 -r 450a813dc2a5 ui/common/menu.c --- a/ui/common/menu.c Thu Sep 26 22:43:13 2024 +0200 +++ b/ui/common/menu.c Sun Sep 29 11:29:37 2024 +0200 @@ -38,6 +38,8 @@ static UiMenu *menus_end; static CxList *current; +static int menu_item_counter = 0; + static void add_menu(UiMenu *menu) { cx_linked_list_add( (void**)&menus_begin, @@ -57,6 +59,10 @@ item); } +static void mitem_set_id(UiMenuItemI *item) { + snprintf(item->id, 8, "%x", menu_item_counter++); +} + static char* nl_strdup(const char* s) { return s ? strdup(s) : NULL; } @@ -86,6 +92,7 @@ // create menu UiMenu *menu = malloc(sizeof(UiMenu)); + mitem_set_id(&menu->item); menu->item.prev = NULL; menu->item.next = NULL; menu->item.type = UI_MENU; @@ -118,6 +125,7 @@ } UiMenuItem* item = malloc(sizeof(UiMenuItem)); + mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_ITEM; @@ -138,6 +146,7 @@ } UiMenuItemI *item = malloc(sizeof(UiMenuItemI)); + item->id[0] = 0; item->prev = NULL; item->next = NULL; item->type = UI_MENU_SEPARATOR; @@ -151,6 +160,7 @@ } UiMenuCheckItem *item = malloc(sizeof(UiMenuCheckItem)); + mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_CHECK_ITEM; @@ -172,6 +182,7 @@ } UiMenuCheckItem* item = malloc(sizeof(UiMenuCheckItem)); + mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_CHECK_ITEM; @@ -193,6 +204,7 @@ } UiMenuItemList*item = malloc(sizeof(UiMenuItemList)); + mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_ITEM_LIST; @@ -210,6 +222,7 @@ } UiMenuItemList* item = malloc(sizeof(UiMenuItemList)); + mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_CHECKITEM_LIST; @@ -226,6 +239,7 @@ } UiMenuItemList* item = malloc(sizeof(UiMenuItemList)); + mitem_set_id(&item->item); item->item.prev = NULL; item->item.next = NULL; item->item.type = UI_MENU_RADIOITEM_LIST; diff -r 4918f9132552 -r 450a813dc2a5 ui/common/menu.h --- a/ui/common/menu.h Thu Sep 26 22:43:13 2024 +0200 +++ b/ui/common/menu.h Sun Sep 29 11:29:37 2024 +0200 @@ -61,6 +61,7 @@ UiMenuItemI *prev; UiMenuItemI *next; UiMenuItemType type; + char id[8]; }; struct UiMenu { diff -r 4918f9132552 -r 450a813dc2a5 ui/gtk/headerbar.c --- a/ui/gtk/headerbar.c Thu Sep 26 22:43:13 2024 +0200 +++ b/ui/gtk/headerbar.c Sun Sep 29 11:29:37 2024 +0200 @@ -29,6 +29,7 @@ #include "headerbar.h" #include "button.h" +#include "menu.h" #if GTK_MAJOR_VERSION >= 3 @@ -71,7 +72,7 @@ break; } case UI_TOOLBAR_MENU: { - //add_toolitem_menu_widget(tb, (UiToolbarMenuItem*)i, obj); + ui_add_headerbar_menu(headerbar, box, (UiToolbarMenuItem*)i, obj, pos); break; } default: fprintf(stderr, "toolbar item type unimplemented: %d\n", (int)i->type); @@ -119,17 +120,53 @@ } } -void ui_add_headerbar_item(GtkWidget *headerbar, GtkWidget *box, UiToolbarItem *item, UiObject *obj, enum UiToolbarPos pos) { +void ui_add_headerbar_item( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarItem *item, + UiObject *obj, + enum UiToolbarPos pos) +{ GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.onclick, item->args.onclickdata); WIDGET_ADD_CSS_CLASS(button, "flat"); headerbar_add(headerbar, box, button, pos); } -void ui_add_headerbar_toggleitem(GtkWidget *headerbar, GtkWidget *box, UiToolbarToggleItem *item, UiObject *obj, enum UiToolbarPos pos) { +void ui_add_headerbar_toggleitem( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarToggleItem *item, + UiObject *obj, + enum UiToolbarPos pos) +{ GtkWidget *button = gtk_toggle_button_new(); WIDGET_ADD_CSS_CLASS(button, "flat"); ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.varname, NULL, item->args.onchange, item->args.onchangedata); headerbar_add(headerbar, box, button, pos); } + +void ui_add_headerbar_menu( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarMenuItem *item, + UiObject *obj, + enum UiToolbarPos pos) +{ + GtkWidget *menubutton = gtk_menu_button_new(); + + if(item->args.label) { + gtk_menu_button_set_label(GTK_MENU_BUTTON(menubutton), item->args.label); + } + if(item->args.icon) { + gtk_menu_button_set_icon_name(GTK_MENU_BUTTON(menubutton), item->args.icon); + } + + GMenu *menu = g_menu_new(); + ui_gmenu_add_menu_items(menu, 0, &item->menu, obj); + + gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(menubutton), G_MENU_MODEL(menu)); + + headerbar_add(headerbar, box, menubutton, pos); +} #endif \ No newline at end of file diff -r 4918f9132552 -r 450a813dc2a5 ui/gtk/headerbar.h --- a/ui/gtk/headerbar.h Thu Sep 26 22:43:13 2024 +0200 +++ b/ui/gtk/headerbar.h Sun Sep 29 11:29:37 2024 +0200 @@ -44,9 +44,26 @@ void ui_headerbar_add_items(UiObject *obj, GtkWidget *headerbar, CxList *items, enum UiToolbarPos pos); -void ui_add_headerbar_item(GtkWidget *headerbar, GtkWidget *box, UiToolbarItem *item, UiObject *obj, enum UiToolbarPos pos); +void ui_add_headerbar_item( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarItem *item, + UiObject *obj, + enum UiToolbarPos pos); -void ui_add_headerbar_toggleitem(GtkWidget *headerbar, GtkWidget *box, UiToolbarToggleItem *item, UiObject *obj, enum UiToolbarPos pos); +void ui_add_headerbar_toggleitem( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarToggleItem *item, + UiObject *obj, + enum UiToolbarPos pos); + +void ui_add_headerbar_menu( + GtkWidget *headerbar, + GtkWidget *box, + UiToolbarMenuItem *item, + UiObject *obj, + enum UiToolbarPos pos); #endif diff -r 4918f9132552 -r 450a813dc2a5 ui/gtk/menu.c --- a/ui/gtk/menu.c Thu Sep 26 22:43:13 2024 +0200 +++ b/ui/gtk/menu.c Sun Sep 29 11:29:37 2024 +0200 @@ -42,8 +42,7 @@ #include #include -#if UI_GTK2 || UI_GTK3 - +#if GTK_MAJOR_VERSION <= 3 static ui_menu_add_f createMenuItem[] = { /* UI_MENU */ add_menu_widget, @@ -501,4 +500,177 @@ } } -#endif /* UI_GTK2 || UI_GTK3 */ +#endif /* GTK_MAJOR_VERSION <= 3 */ + + + +#if GTK_MAJOR_VERSION >= 4 + + + +static ui_gmenu_add_f createMenuItem[] = { + /* UI_MENU */ ui_gmenu_add_menu, + /* UI_MENU_ITEM */ ui_gmenu_add_menuitem, + /* UI_MENU_CHECK_ITEM */ ui_gmenu_add_checkitem, + /* UI_MENU_RADIO_ITEM */ ui_gmenu_add_radioitem, + /* UI_MENU_ITEM_LIST */ ui_gmenu_add_menuitem_list, + /* UI_MENU_CHECKITEM_LIST */ ui_gmenu_add_menuitem_list, + /* UI_MENU_RADIOITEM_LIST */ ui_gmenu_add_menuitem_list, + /* UI_MENU_SEPARATOR */ ui_gmenu_add_menuseparator +}; + +void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj) { + UiMenuItemI *it = menu->items_begin; + int index = 0; + while(it) { + createMenuItem[it->type](parent, index, it, obj); + it = it->next; + index++; + } +} + +void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj) { + UiMenu *mi = (UiMenu*)item; + GMenu *menu = g_menu_new(); + ui_gmenu_add_menu_items(menu, 0, mi, obj); + g_menu_append_submenu(parent, mi->label, G_MENU_MODEL(menu)); +} + +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->callback != NULL) { + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = obj; + event->userdata = i->userdata; + event->callback = i->callback; + event->value = 0; + event->customdata = NULL; + + g_signal_connect( + action, + "activate", + G_CALLBACK(ui_activate_event_wrapper), + event); + g_signal_connect( + obj->widget, + "destroy", + G_CALLBACK(ui_destroy_userdata), + event); + } + + char action_name[32]; + snprintf(action_name, 32, "win.%s", item->id); + g_menu_append(parent, i->label, action_name); +} + +void ui_gmenu_add_menuseparator(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { + +} + +void ui_gmenu_add_checkitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { + UiMenuCheckItem *checkitem = (UiMenuCheckItem*)item; + + // TODO +} + +void ui_gmenu_add_radioitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { + +} + +void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject *obj) { + UiMenuItemList *il = (UiMenuItemList*)item; + + const CxAllocator *a = obj->ctx->allocator; + + UiActiveGMenuItemList *ls = cxMalloc( + a, + sizeof(UiActiveGMenuItemList)); + + ls->object = obj; + ls->menu = p; + ls->index = index; + ls->oldcount = 0; + ls->getvalue = il->getvalue; + + UiVar* var = uic_create_var(ui_global_context(), il->varname, UI_VAR_LIST); + ls->list = var->value; + + ls->callback = il->callback; + ls->userdata = il->userdata; + + ls->list->observers = ui_add_observer( + ls->list->observers, + (ui_callback)ui_update_gmenu_item_list, + ls); + + 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); + + + UiEventData *event = malloc(sizeof(UiEventData)); + event->obj = obj; + event->userdata = il->userdata; + event->callback = il->callback; + event->customdata = NULL; + event->value = 0; + + g_signal_connect( + action, + "activate", + G_CALLBACK(ui_activate_event_wrapper), + event); + g_signal_connect( + obj->widget, + "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) { + int intval = event->value; + if(parameter && g_variant_is_of_type(parameter, G_VARIANT_TYPE_INT32)) { + intval = g_variant_get_int32(parameter); + } + + UiEvent evt; + evt.obj = event->obj; + evt.window = event->obj->window; + evt.document = event->obj->ctx->document; + evt.eventdata = event->customdata; + evt.intval = intval; + event->callback(&evt, event->userdata); +} + +void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list) { + // remove old items + for(int i=0;ioldcount;i++) { + g_menu_remove(list->menu, list->index); + } + + // add list items + ui_getvaluefunc getvalue = list->getvalue; + int i = 0; + void* elm = ui_list_first(list->list); + while(elm) { + char *label = (char*) (getvalue ? getvalue(elm, 0) : elm); + + GMenuItem *item = g_menu_item_new(label, NULL); + GVariant *v = g_variant_new("i", i); + g_menu_item_set_action_and_target_value(item, list->action, v); + g_menu_insert_item(list->menu, list->index+i, item); + + elm = ui_list_next(list->list); + i++; + } + + list->oldcount = i; +} + +#endif diff -r 4918f9132552 -r 450a813dc2a5 ui/gtk/menu.h --- a/ui/gtk/menu.h Thu Sep 26 22:43:13 2024 +0200 +++ b/ui/gtk/menu.h Sun Sep 29 11:29:37 2024 +0200 @@ -34,12 +34,12 @@ #include #include "toolkit.h" -#if UI_GTK2 || UI_GTK3 #ifdef __cplusplus extern "C" { #endif +#if GTK_MAJOR_VERSION <= 3 typedef struct UiActiveMenuItemList UiActiveMenuItemList; @@ -75,11 +75,43 @@ int64_t ui_checkitem_get(UiInteger *i); void ui_checkitem_set(UiInteger *i, int64_t value); +#endif /* GTK_MAJOR_VERSION <= 3 */ + +#if GTK_MAJOR_VERSION >= 4 + +typedef void(*ui_gmenu_add_f)(GMenu *, int, UiMenuItemI*, UiObject*); + +typedef struct UiActiveGMenuItemList UiActiveGMenuItemList; +struct UiActiveGMenuItemList { + UiObject *object; + GMenu *menu; + char action[32]; + int index; + int oldcount; + UiList *list; + ui_getvaluefunc getvalue; + ui_callback callback; + void *userdata; +}; + +void ui_gmenu_add_menu_items(GMenu *parent, int i, UiMenu *menu, UiObject *obj); + +void ui_gmenu_add_menu(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_menuitem(GMenu *parent, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_menuseparator(GMenu *p, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_checkitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_radioitem(GMenu *p, int index, UiMenuItemI *item, UiObject *obj); +void ui_gmenu_add_menuitem_list(GMenu *p, int index, UiMenuItemI *item, UiObject *obj); + +void ui_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event); +void ui_update_gmenu_item_list(UiEvent *event, UiActiveGMenuItemList *list); + +#endif + + #ifdef __cplusplus } #endif -#endif /* UI_GTK2 || UI_GTK3 */ - #endif /* MENU_H */ diff -r 4918f9132552 -r 450a813dc2a5 ui/gtk/window.c --- a/ui/gtk/window.c Thu Sep 26 22:43:13 2024 +0200 +++ b/ui/gtk/window.c Sun Sep 29 11:29:37 2024 +0200 @@ -86,6 +86,10 @@ obj->ctx = uic_context(obj, mp); obj->window = window_data; +#if GTK_CHECK_VERSION(4, 0, 0) + obj->ctx->action_map = G_ACTION_MAP(obj->widget); +#endif + if(title != NULL) { gtk_window_set_title(GTK_WINDOW(obj->widget), title); } diff -r 4918f9132552 -r 450a813dc2a5 ui/ui/icons.h --- a/ui/ui/icons.h Thu Sep 26 22:43:13 2024 +0200 +++ b/ui/ui/icons.h Sun Sep 29 11:29:37 2024 +0200 @@ -35,7 +35,7 @@ extern "C" { #endif -#ifdef UI_GTK3 +#ifdef UI_GTK #define UI_ICON_HOME "go-home" #define UI_ICON_NEW_WINDOW "list-add" @@ -50,7 +50,7 @@ #define UI_ICON_GO_BACK "go-previous" #define UI_ICON_GO_FORWARD "go-next" -#endif /* UI_GTK3 */ +#endif /* UI_GTK */