Gtk4 - migrating from Gtk3 - help with menus

Hello everyone,
I apologize in advance if this is not the suitable place to ask for help,
but I find it close to impossible to ask question in any other place on the web,
at least a place where I can find help from the Gtk dev. team.
As the topic resumes it I am migrating a Gtk3 app to Gtk4 and I am facing some issues,
issues that on line search can not help to me to face, so far.

My troubles come from the changes related to the pop-up menus,
In many part of my app I used ’ gtk_menu_new ()’ and ‘gtk_menu_shell_append()’ to add item
to this menu, and not such thing as a ‘ui’ file to describe the menu, actually I found it way more
convenient to simply write to code the way I see fit … and now all this seems to be gone …
Of course I browse the (great) new web site that presents the new API, but the help it provides
for that particular feature is rather elusive on the details, I found out that I could pop-up a menu
using ‘gtk_popover_menu_new_from_mode l(NULL)’ and then add element to this menu using :

gboolean
  gtk_popover_menu_add_child (
  GtkPopoverMenu* popover,
  GtkWidget* child,
  const char* id)

So far I was not able to find a working example for this function on the web, in particular because I have
no idea what that ‘char * id’ is …

Thank you in advance for any information on the matter.
Best regards, and thanks for the great work with GTK+.

Sébastien

No, that doesn’t work. This API is about allowing placeholder items in the menu model that are then filled by an externally constructed widget.

gtk4-widget-factory has an example:

  • gear_menu_model defines an item with a “custom” attribute:

    <attribute name="custom">transition-speed</attribute>

  • that placeholder is then filled in by adding a child with

    <child type="transition-speed">

The second step could be done in code with

gtk_popover_menu_add_child (popover, scale, "transition-speed");

Here I was going to mention a regular GtkPopover with a box child filled with GtkModelButtons as a more direct replacement for manually constructed menus, but it turns out that GtkModelButton is no longer public. It still works — I only just noticed that I should replace it in Polari’s GTK4 port — but I wouldn’t recommend poking at internals like that.

Dear Florian,
first of all thank you for your answer and sorry not to reply sooner,
actually I got pretty busy migrating the part of my code that were not as tricky to migrate as the menus,
that really seem to be more complicated to handle, on my point of view I mean, so I will be happy to have your point of view on how to do this for my program.
At runtime I append/insert/remove many menu elements (including entire submenus) the program handles
a workspace in which many projects can be opened, each of them can have each own custom menus,
so I need to take time to think about the best way to handle this with GTK4, your advise will be more than welcome.

Dear @fmuellner,
I am looking for a list of possible <attribute name="something"> for the menus, and so far I am not able to find any, so that I could know in details what could be done and how, that is the meaning of the something and how to use them, I though that maybe you would be able to point me in the right direction …

Thanks in advance for your help !

Sébastien

Sorry to bother you again,
but I do have another question, is the XML file (that I am trying to write for my program as you read this)
an interface to the GMenu APIs, if yes can I use the GMenu API directly in my code (like: g_menu_new () and so on …) ?
Considering that all my menus, and there is a lot of information, where already coded and not prepared using the GtkBuilder/glade assistant, it seems way easier to adapt them like this, what do you think about it ?

Again thanks in advance for your input, best regards.

Sébastien

Not quite how I’d phrase it but yes: What matters is you end up with a GMenuModel, doesn’t really matter where that comes from (in theory you could even implement a custom one)

Somewhat confusingly this is documented on consumers of GMenuModel rather than GMenuModel itself - i.e. GtkPopoverMenu

The reasoning being that different “renderers” are, well, different and support different things - even label has different meaning in some circumstances. A GMenuModel is essentially just a structured set of dictionaries.

Dear @zbrown thank you for your answer,
sorry to bother you again but I must confess that the new implementation of the menus is really troubling me,
to be honest it seemed so simple to me for GTK3 than it seems to be now, and the examples the I find in gtk4-demo, or in gtk4-widget-factory are just not enough for me to understand what to do, If you allow me I will walk you through my process and hope that you will correct me.

I prepared an XML file containing my menu model:

   <? xml version="1.0" encoding="UTF-8"? >
     <interface>
     < !-- Main Window Workspace Menu -- >
      < menu id="workspace_menu">
        < section>
            < item>
              < attribute name="label" translatable="yes">Open _Workspace</attribute>
              < attribute name="target">work.open_worspace</attribute>
            </item>
         </section>
      </menu>
    </interface>

By the way I am not sure if I have to use target or action for the keyword …
otherwise I kept it simple for the purpose of my example, after that I understand how to read this file and build the popover using it, expect for the callbacks, precisely how to set up GVariant data to send the callback function, even more interesting, how to send a data structure as GVariant so that I could write something like this:

static void  action_from_popover (GtkWidget  *widget,  
                                  const char *action_name,  
                                  GVariant   *parameter)
{
   my_struct * data = (my_struct *)parameter;
   perform_action_based_on_parameter (data);
}

Again I kept it simple, imagine that in my program there is a bunch of projects that can be opened, each in a dedicated window, the menus for each of them being generated similarly I need to be able to understand which is which in the callback function, hence using the pointer data.

Here the Gtk3 version of the program I am migrating to illustrate my purpose:

Thanks in advance for your help.
Best regards.

Sébastien

You may want to further familiarise yourself with GAction terms: action is the name of an action, target is something passed to that action (the GVariant *parameter in your function).

Thus you always need action but you may occasionally, additionally, need a target (e.g. as when adding radio items)

Unfortunately you can’t send a pointer through a GVariant (variants are designed for sending data between processes so really not pointer friendly).

Looking at your code I assume your using gtk_widget_class_install_action? In which case widget is actually an instance of your custom type so you probably don’t need an additional my_struct

Hope that helps

Don’t worry about it, happy to help

You have to use action, and you may use target.

The former is the name of the action that is activated, the latter is the (optional) parameter. See the

relevant docs section:

It really depends. In code, you can use any of the g_variant_new_* functions, GVariantBuilder for complex types or g_variant_parse() to create a GVariant from a string representation. The last one is also how you use the “target” attribute in .ui files.

You can’t. GVariant is primarily used for serializing data, potentially between different processes. You just can’t throw random memory at it and expect it to come out fine on the other side, you’ll need dedicated (de)serialization functions like my_struct_serialize/my_struct_deserialize.

If you use GtkApplicationWindow for your windows, then you can use window-specific actions (“win.save-project”) instead of application-wide actions (“app.new-window”).

For more information about actions and menus:

Dear @fmueller, and @ebassi,
thank you very much for your answers, between my question and your answers I stumbled
on the documentation that @ebassi pointed me towards, that helped me, I also found clues on Stackoverflow here, and I achieved what I was looking for :wink:
I used a xml ‘ui’ file to describe the menu/actions and combined that with:

static void atomes_menu_bar_action (GSimpleAction * action, GVariant * parameter, gpointer data)
{
  gchar * name = g_strdup_printf ("%s", g_action_get_name(G_ACTION(action)));
  if (g_strcmp0 (name, "connect") == 0)
  {
    do_something_with_my_struct (data);
  }
}

GtkWidget * create_main_window (GApplication * my_app)
{
  GSimpleAction * act_connect    = g_simple_action_new ("connect",  NULL);
  g_action_map_add_action (G_ACTION_MAP(my_app), G_ACTION(act_connect));
  my_struct * user_data = create_struct (); // to prepare my data structure
  g_signal_connect (act_connect, "activate", G_CALLBACK(do_simple_action),  (gpointer)user_data);
 
  GtkWidget * window = gtk_application_window_new (GTK_APPLICATION(my_app));
  GtkBuilder * builder = gtk_builder_new_from_file ("menus/main.ui");
  GMenuModel * model = G_MENU_MODEL (gtk_builder_get_object (builder, "menu_bar")); 
  gtk_application_set_menubar (GTK_APPLICATION(my_app), model);
  gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW(window), TRUE);
  return window;
}

Thanks for the help :wink:
All the best and have a good day !

Sébastien

1 Like

Here I come again, but, but, but …
… this is likely to be my last question :wink:

What about icons in the menus, that was easy to use gtk icons, but what if I want to use
custom icons, I noticed that I could do it with a complete path for the location:

<attribue name='icon'>/home/user/files/codeblocks-files/my-program/bin/pixmaps/icon.png</attribute>

I think it is rather complicated to provide a static path to my icon, is there any way around this,
so that icons can be look for in the bin directory and I would only have to provide ‘pixmaps/icon.png’ in the XML file ?

Thanks in advance for your help.

A quick recommendation: instead of constantly attaching new replies, use a new topic.

Don’t use icons in menus: they are definitely not recommended.

At most, you can use iconic menu items, which mean menu items composed of just the icon.

Well icons don’t belong in /bin of course (they are data) - You’ll want to look at GResource, a method for embedding resources (such as icons) directly into your executable. GtkApplication automatically adds your applications resource path to the icon search path.