implement GTK4 gmenu item and item list newapi

Sun, 29 Sep 2024 11:29:37 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 29 Sep 2024 11:29:37 +0200
branch
newapi
changeset 311
450a813dc2a5
parent 310
4918f9132552
child 312
3f2b3d15668b

implement GTK4 gmenu item and item list

application/main.c file | annotate | diff | comparison | revisions
ui/common/context.h file | annotate | diff | comparison | revisions
ui/common/menu.c file | annotate | diff | comparison | revisions
ui/common/menu.h file | annotate | diff | comparison | revisions
ui/gtk/headerbar.c file | annotate | diff | comparison | revisions
ui/gtk/headerbar.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/gtk/window.c file | annotate | diff | comparison | revisions
ui/ui/icons.h file | annotate | diff | comparison | revisions
--- 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);
--- 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;
--- 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;
--- 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 {
--- 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
--- 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
 
--- 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 <cx/linked_list.h>
 #include <cx/array_list.h>
 
-#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;i<list->oldcount;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
--- 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 <cx/list.h>
 #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 */
 
--- 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);
     }
--- 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 */
     
     
 

mercurial