Using GtkPopoverMenu as a GtkMenu replacement

I recently had a look at the Gtk4 docs to figure out what it’d take to port Horizon EDA to Gtk4 once it’s widely available and was quite surprised by the removal of the GtkMenu APIs. If I didn’t overlook something GtkPopoverMenu makes things significantly harder than they were with GtkMenu:

In some places, all I need is a context menu with a few items. I don’t quite see the benefit of using actions rather than connecting directly to the activate signal of a GtkMenuItem.

Since the application uses its own shortcut handling, I pack a GtkBox into the GtkMenuItem to show keybindings. I don’t see that this is possible with the new GtkPopoverMenu.

Menus are also use for clarifying the selection when clicking somewhere matches more than one item:

[https://gitlab.gnome.org/GNOME/gtk/uploads/7e945a5a30c62a8a447ffd5b04ae2479/Peek_2020-07-11_10-06.mp4](Video of how it looks like)

There would be multiple issues doing this with a GtkPopoverMenu :

  • By its very nature, this menu is populated dynamically. Adding new actions on-the-fly just to get items into the menu doesn’t feel like doing it the right way.
  • To make it clear which objects on screen correspond to the items in the menu, I connect to the select/deselect signals of GtkMenuItem and highlight the appropriate item. As far as I can tell, there’s no such thing with GtkPopoverMenu.

So what’s the way to still have these features in Gtk4? Roll a menu on my own based on a popver?

1 Like

The main advantages of removing the menu widget API are:

  • unify the UI description of menus to the GMenu XML format
  • simplify the menu description for menu bars, menu buttons, popovers, and context menus
  • reduce the toolkit complexity, especially when it comes to handing over positioning of menus to the application code

There’s an overall strawman proposal available on the wiki, if you’re interested.

Yes, if you’re trying to build your own, custom menus with custom widgetry, this new API is going to make your life harder; the overall objective to to make common patterns easier, and to increase consistency between GTK applications.

If you want a context menu, you should use GMenu to describe your menu; a GtkPopoverMenu attached to your widget to display the GMenu; and a GtkGestureClick to pop up the popover.

You can create and modify the GMenu instance at run time; and you can use the GtkShortcut API to bind keyboard shortcuts to actions, signals, and callbacks.

I am still on the fence on whether we need a manual creation api for popover menus.

The original strawman had the proposal of a gtk_widget_set_context_menu() API, but as far as I remember, we couldn’t figure out a way to add it while keeping API like gtk_text_set_extra_menu().

I meant api to manually populate a popovermenu with model buttons

As far as I get the docs the only thing to make something happen when a menu item is activated, is to go through the GAction machinery. This however reminds me of the “You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.” thing since this feels unnecessarily complex to just connecting to an (imaginary) activated signal of the GMenuItem.

Any ideas on the replacements for the selected/deselected signals?

Yes, given that everything else is GAction-based—including the accelerators and generic shortcuts.

What are you trying to achieve, with those two signals?

I’m not really sold on that approach; it is a fairly big footgun in terms of UI consistency. People think they can construct menus on the fly when the item is pressed, doing arbitrarily long amount of work in a synchronous fashion, instead of keeping the menu structure around as a model and using menus as a view to represent the model.

See this video https://gitlab.gnome.org/GNOME/gtk/uploads/7e945a5a30c62a8a447ffd5b04ae2479/Peek_2020-07-11_10-06.mp4 linked in the first post. I use this signal to highlight the object in the canvas that’s currently selected in the menu.

Unfortunately, this is exactly what I need for the clarify selection menu.

You can still construct the menus on the fly—after all, that’s what we do for context menus inside GTK. What you cannot do is construct the internal widgetry.

The GtkPopoverMenu already tracks the active item, so we could add a GtkPopoverMenu::item-selected signal that tells you that the selected item changed, and give you the GMenuItem that maps to it. The GMenuItem can also be constructed with additional attributes—including an identifier for the object in your canvas, or any other ancillary data you wish to bind to a menu item.

So how’d you implement such a menu with dynamically generated content with the GMenu api? Create one action and encode the item selected in the target?

That’d be really helpful.

Yes, that’s how I would do it.

I’ll open an issue about it. ETA: https://gitlab.gnome.org/GNOME/gtk/-/issues/2925

Have you tried submenu-action for dynamically populating your submenu ? whats missing in it ?

Acceptable, but still feels like the toolkit is working against me, as I somehow have to pass my callback data through a GVarient. An activated signal on the GMenuItem would be so much easier, especially since actions don’t really have any benefits in this particular application.

Do you have a link to the docs? I don’t quite see how submenus would help.

https://wiki.gnome.org/Projects/GLib/GApplication/GMenuModel

submenu-action
Type: s

Defined on submenu items, this attribute names an action used to signal when the menu is shown or not. The named action is in the same namespace as is normal for action attributes and the usual action-namespace rules apply.

The named action should have a boolean state. When the submenu is shown, the front-end will change the boolean state of the action to true. When the menu is hidden again, it will set it to false. The application signals when the menu is ready to be shown by changing the state to true.

This provides a means by which applications that sync the contents of their menus with some external source can turn off the syncing while the menu is closed (a menu listing wifi hotspots, for example).

[…]

Clearly this requires a different way of thinking we’ll all take a while to get used to, but it’s not too hard to imagine objects on your canvas have some kinda unique id you can pass as an action target

Yes, it’s different, but as an application developer, the whole new menu API causes significant friction without any clear benefit if not already using GActions.

GAction is used for a variety of things in GTK3 already—popovers, menus, application actions—and it is used even more in GTK4, with the introduction of key shortcuts based on actions. It allowed us to make the whole API more consistent, as well as providing a level of introspection on our menus, buttons, and popovers that can be used to generate UI, like the list of shortcuts; or navigation and discovery UIs, like the old Plotinus extension module. Plus, it allows us to move menus out of process on platforms such as macOS, or implement jump lists on Windows 10.

Those are just the immediate benefits of making menus declarative and hiding the widgetry; of course, it’s a change, and thus it has a certain amount of friction. It does have benefits, though, both immediate and down the line.

2 Likes

I do get the benefits for the application you mentioned, but I’m still not quite sure if it’s worth the friction caused in slightly exotic uses of menus.

While we’re at it, there’s another use of GtkMenu in Horizon EDA that might be problematic to port to Gtk4:

The thing on the very right is a menu that opens when right-clicking or dragging to left on an action button that provides more than one action. Then menu items consist of a GtkImage of the right size (to match the buttons) and two labels (one for the action, one for the keybinding). Any ideas how to implement this with Gtk4?

I would use a box of buttons that open a GtkPopover, and populate the popovers with lists of widgets. GMenuItems can also have icons, but in this case I think this maps more to a tool bar than to menus.

I’ve thought about this as well and I see several issues in making it behave like a menu:

  • Selection needs to follow the cursor. As far as I can tell only treeviews support this and treeviews don’t seem to be the right solution for this problem.
  • Need to support activating an item when releasing the mouse cursor. With the current GtkMenu-based implementation, it’s possible to press the mouse button on the button drag to the desired menu item and release the mouse button for activation
  • The popover needs to close when releasing the the mouse button outside of the menu.

Much of this work could be avoided if there was some sort of API to manually pack widgets into menus. Is there a technical reason for omitting this from Gtk4?