GTK4: gtk_popover_menu_bar_add_child

Dear all,
I am in the process of re-opening old wounds these days … sorry about that :wink:
This message follows these one:

I am still trying figure out how to attach a widget to a menu bar in GTK4.
No issue in setting the custom attribute, and checking afterwards that it is set to the proper value,
however the gtk_popover_menu_bar_add_child always return an error.

Any idea to solve this, or maybe as I was asking for in my previous threads, a working example ?

Sébastien

What is the error? Can you show the relevant code and/or UI definition?

Hello and thank you for looking into this,
well there is no error per say, my bad on using this terminology, sorry.
The function simply send back the FALSE value, which means that the widget was not added to the menu:

To be honest I have no idea how to retrieve more information about this result,
and I will be happy to follow any suggestion you may have.

Given that:

For this to work, the menu model of bar must have an item with a custom attribute that matches id.

Then again, you should show how you populate the menu model, and show your call to gtk_popover_menu_bar_add_child(). Otherwise people will just be guessing…

I was just working on an example code when you appropriately ask for it.
And well the test example I prepared just works as it should … so I am trying to figure out
the difference(s) with my main program … and so far I am not able to find any …

any way to retrieve more information on the FALSE return
of the gtk_popover_menu_bar_add_child() function to know what is going wrong ?

I can confirm that the proper menu item with the appropriate custom attribute is here,
but no way to insert the widget anyway … I reduced my main code to be a simple, and close as possible
to the working example … still no way to insert the widget …

I would use a debugger.

For anything more, I think we need to see the code.

Well I would appreciate your eyes on the matter, if you have time an energy to look into it of course.

You can access the latest version of the sources here:

Once you cloned the repo, you need to build the program using make debug which will trigger most, if not all, flags to debug it properly.

Atomes (my program) contains both GTK3 and GTK4 parts, delimited using #ifdef GTK3 or #ifdef GTK4 preprocessor instructions.

Few key informations:

  • src/gui/gui.c
    Every menu item elements in Atomes is created in src/gui/gui.c in the create_gmenu_item() function, this is where I add custom attribute if required.

  • src/opengl/win/menu_bar.c
    The place where I create the menu bar for the OpenGL window, contains the append_opengl_item() function to create menu elements for the menu bar.
    The append_opengl_item() calls create_gmenu_item() with proper arguments.
    This is also where I try to insert an example custom widget using gtk_popover_menu_bar_add_child at the custom set-col-H[C]-c.1 on line 164.

  • src/opengl/win/m_coord.c
    The place where I insert the menu element and the custom attribute, through calls to append_opengl_item ()
    (and then create_gmenu_item), I set the custom on the call line 352.

  • To test the insertion:

  1. You might need to install few libs that are required (see the pkg-config tests in Makefile for more information)
  2. Build atomes: make debug
  3. cd bin
  4. export G_MESSAGES_DEBUG=all
  5. ./atomes
  6. Create a new project Ctrl+N
  7. On the new (and empty) OpenGL window: right click : insert -> library -> CH4

You will see in the terminal that each time a custom menu item is created the value is displayed,
and that when using the gtk_popover_menu_bar_add_child()function it fails.

I hope this is clear enough, since the program can have many OpenGL win opened,
and that each of them contain a bunch of different custom items,
I had to settle for a unique example (CH4 molecule) to test the insertion.

Please let me know if you need anything more.

Sorry, I’m unlikely to have much time to clone and build an entire project just for this. I would suggest that you try debugging it yourself wrt the function and why it returns false, and/or continue trying to make a minimal sample exhibit the problem, which might reveal the culprit.

Of course I understand,
I wish I could do it myself actually, but I have no clue how to debug a function that is not in the program it-self, namely gtk_popover_menu_bar_add_child() … I would really appreciate if you can educate me :wink:

Well, I can only tell you about using gdb as that’s all I use. What platform are you on, and what debugger(s) do you have access to?

Regardless of debugger, you’ll want to ensure you have installed the debug symbols for GTK. How to do this depends on your Linux distribution or other OS - which I don’t think you’ve specified? :wink: - but info on which you should be able to find that info by searching wrt the relevant distro/OS/etc.

Next, for gdb, I would run the program like gdb ./your-exe, then in the GDB prompt set a breakpoint like so: break gtk_popover_menu_bar_add_child. Then run and ‘steer’ your program to the point where you try to add that. Now GDB will break at the start of the function, and you can use its commands like step or next or others to move through the function, print to inspect variables, etc. Hopefully doing so will help you see where things are going wrong.

The steps will be different in specifics but the same in generalities, if you’re using a different debugger. There might be better guides out there online than I can write here in a few minutes, but maybe this gets you started…

Of course I am using GDB, and working on Linux (Ubuntu 22.04 LTS), my point was how to inspect inside the gtk_popover_menu_bar_add_child function to know why its results is FALSE.
If I add a breakpoint on the function call, and then step, it does not enter it, it only keeps following the next sequence of instructions in my program.

I might not be fluent enough with GBD to understand what you are trying to tell me,
anyway thanks for your help, and your time so far.
I will keep looking for the answer :wink:

I don’t mean you should add a breakpoint on your call of the function, in your source; I mean you should add a breakpoint on the function itself. break gtk_popover_menu_bar_add_child should do that. Does it not? Or did you type something else?

I did just that yes, but whenever I enter next or step afterwards it only keeps inspecting what’s next in my program and not the function itself.

Have you installed the debug symbols for GTK? I would try that. If you’re still having issues, sorry, I don’t know off-hand; try checking some GDB guides.

To add to that, you may consider building GTK in debug mode:

  1. git clone https://gitlab.gnome.org/GNOME/gtk.git --branch=gtk-4-10
  2. cd gtk && mkdir build && meson setup build --buildtype=debug -Dintrospection=disabled -Dmedia-gstreamer=disabled -Dbuild-tests=false
  3. ninja -C build
  4. Open your application under GDB with the built GTK library: LD_PRELOAD=build/gtk/libgtk-4.so gdb /path/to/your/app
  5. Run one time to make gdb aware of all symbols: run
  6. Close the application
  7. Now set the breakpoint: break gtk_popover_menu_bar_add_child
  8. Run again with the command run. This time you’ll be able to step into the function
2 Likes

lb90 is better at explaining than me :slight_smile:

Another option is to use jhbuild, makes all this pretty nice and easy, and I believe includes debug info by default.

1 Like

Ok,
I was working on understanding the issue, indeed with the debug symbols installed GDB is working as you describe @dboles, so thanks for the idea !
And I figured that it could actually be a GTK bug.
I noticed that the gtk_popover_menu_bar_add_child() function was calling the gtk_popover_menu_add_child() function as many times as the number of menu items in the upper (top) level of the menu bar. Then I realised that it might not call it for lower levels of the menu. And that is actually the case.

What do you think ?

Here is an example code that demonstrates the behaviour:

#include <gtk/gtk.h>

GtkApplication * TestApp = NULL;

void 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, "quit") == 0)
  {
    g_application_quit (G_APPLICATION(TestApp));
  }
}

GMenuItem * create_gmenu_item (const gchar * label, const gchar * action, const gchar * custom)
{
  GMenuItem * item;
  item = g_menu_item_new (label, action);
  g_menu_item_set_attribute (item, "use-markup", "s", "TRUE", NULL);
  // Setting custom here:
  if (custom)
  {
    g_menu_item_set_attribute (item, "custom", "s", custom, NULL);
    /* Checking custom value: */
    GVariant * cust = g_menu_item_get_attribute_value (item, "custom",  g_variant_type_new("s"));
    if (cust) g_print ("item is:: %s, custom is:: %s\n", label, g_variant_get_string  (cust, NULL));
  }
  return item;
}

void append_menu_item (GMenu * menu, const gchar * label, const gchar * action, const gchar * custom)
{
  GMenuItem * item = create_gmenu_item (label, action, custom);
  g_menu_append_item (menu, item);
  g_object_unref (item);
}

GMenu * second_level_menu ()
{
  GMenu * menu = g_menu_new ();
  append_menu_item (menu, "Level 2: C(H)<sub>2</sub>", "None", "CH2-2");
  append_menu_item (menu, "Level 2: C(H)<sub>3</sub>", "None", NULL);
  append_menu_item (menu, "Level 2: C(H)<sub>4</sub>", "None", NULL);
  return menu;
}

void run_program (GApplication * app, gpointer data)
{
  GtkWidget * window = gtk_application_window_new (GTK_APPLICATION(app));
  gtk_window_set_title (GTK_WINDOW(window), "Test");
  gtk_window_set_resizable (GTK_WINDOW(window), TRUE);
  gtk_widget_set_size_request (window, 900, 450);


  GMenu * menubar = g_menu_new ();
  GMenu * menu    = g_menu_new ();
  // Trying different things just in case:
  append_menu_item (menu, "C(H)<sub>2</sub>", "None", "CH2");
  append_menu_item (menu, "C(H)<sub>3</sub>", "None", NULL);
  append_menu_item (menu, "C(H)<sub>4</sub>", "None", NULL);
  g_menu_append_submenu (menu, "Second level", (GMenuModel*)second_level_menu());

  append_menu_item (menu, "Quit", "app.quit", NULL);
    
  g_menu_append_submenu (menubar, "First level", G_MENU_MODEL (menu));
  g_object_unref (menu);
  
  GtkWidget * menu_bar = gtk_popover_menu_bar_new_from_model (G_MENU_MODEL(menubar));
  // Adding a widget at custom 'CH2', this is working
  gtk_popover_menu_bar_add_child ((GtkPopoverMenuBar *)menu_bar, gtk_label_new("Level 1: Custom CH2"), "CH2");
  // Adding a widget at custom 'CH2-2' second level, this is not working
  gtk_popover_menu_bar_add_child ((GtkPopoverMenuBar *)menu_bar, gtk_label_new("Level 2: Custom CH2"), "CH2-2");
  
  GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
  gtk_window_set_child ((GtkWindow *)window, vbox);
  GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
  gtk_box_append (GTK_BOX(vbox), hbox);
  gtk_box_append (GTK_BOX(hbox), menu_bar);
  gtk_widget_show (menu_bar);

  
  GSimpleAction * act = g_simple_action_new ("quit", NULL);
  g_signal_connect (act, "activate", G_CALLBACK(menu_bar_action), NULL);
  g_action_map_add_action (G_ACTION_MAP(GTK_APPLICATION(app)), G_ACTION(act));
  
  //gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW(window), TRUE);
  gtk_window_present (GTK_WINDOW(window)); 
}

int main (int argc, char *argv[])
{
  // setlocale(LC_ALL,"en_US");
  gtk_disable_setlocale ();
#if GLIB_MINOR_VERSION < 74
   TestApp = gtk_application_new (g_strdup_printf ("test._%d.gtk", (int)clock()), G_APPLICATION_FLAGS_NONE);
#else
    TestApp = gtk_application_new (g_strdup_printf ("test._%d.gtk", (int)clock()), G_APPLICATION_DEFAULT_FLAGS);
#endif
  g_signal_connect (G_OBJECT(TestApp), "activate", G_CALLBACK(run_program), NULL);
  int status = g_application_run (G_APPLICATION (TestApp), 0, NULL);
  g_object_unref (TestApp);
  return status;
}
1 Like

Thank you for suggestions and the explanations !

Yes, it seems intended that gtk_popover_menu_bar_add_child() can only add custom widgets to its own children, not children of submenus; it iterates its own children only, does not recurse. For the latter I would think you’d have to use gtk_popover_menu_add_child() on the ‘submenu’ popover menu, before adding that to the menu bar. (I didn’t look into whether that’s possible specifically, but hopefully it is.)

=> gtk_popover_menu_bar_add_child: works only for the top level of the menu bar (#5955) · Issues · GNOME / gtk · GitLab