This is a strawman proposal for deprecating the public GtkWidget-based menu API in order to remove it for GTK+ 4.0.
The goal of this strawman is to remove the GtkWidget-based menu API from the public GTK+ API, and allow creating menus through the GMenu XML serialization format, and through the GMenu and GAction API.
Deprecating Menu widgets
GTK+ has three widget classes that manage menus:
GtkMenu — the "top level" menu
GtkMenuShell — a container for menu items
GtkMenuItem — a menu item
GtkMenuBar - a menu bar
All of these are real GtkWidget classes, and handle things like menu hierarchy; input events; positioning; visibility. They allow creating menus by building them piece by piece using the GtkWidget and GtkContainer API, as well as creating new implementations inside application code, and overriding the default behaviour in client-side code.
Over the years we also added API that would take descriptions of the menus and build the actual widgets behind the scenes:
GtkUIManager and GtkAction (both deprecated)
GtkBuilder
GMenu and GAction
We also pushed for application developers to use the declarative API (and its serialization formats) instead of building menus by hand.
Sadly, all declarative API have different coverage and features:
GtkUIManager
GtkUIManager allows merging and unmerging of menu hierarchies, which is used by applications to modify their menus in response to contextual UI changes, e.g. plugins
GtkUIManager allows describing toolbars as well as menus
GtkAction exposes the feature of creating real widgets
GtkBuilder
GtkBuilder allows loading the GtkUIManager and GMenu serialization formats
GtkBuilder is mostly meant as a GObject serialization format, which means it's actually easier for generators to build GtkMenu and friends directly
GtkBuilder does not allow merging and unmerging of menu hiearchies
GMenu
GMenu is a generic menu serialization format that has no real UI connection
GMenu is suited for describing menus that can be sent over IPC to other processes, like the compositor
GMenu's API allows merging and unmerging items, but it's overall fairly clunky and requires a lot of state to be kept around in the application
Prior Art
Most platforms provide an API similar to GMenu: a declarative, descriptive API that defines the menu hierarchy and the attributes of each element, as well as an action to perform on activation.
macOS
Windows
Additional issues
- Input handling inside menus and menu shells is complicated by the interdependent nature of the widgets; exposing the event handling to applications increases this complexity
We only have direct API to pop up contextual menus using gtk_menu_popup(), which led to API contortions to handle associating a menu with a widget or an area, as well as negotiating the menu position on the screen — see also bug 315645 for context menus and long press gestures
The existing application API nudges the application developer towards using GMenu but still allows (and, in some cases, forces) building menus out of widgets
Further application requirements
- Add description attributes to menu items
- Tooltips, or other ways to show helpful hints inside the application UI (e.g. in the statusbar)
- Serialization and deserialization of menu and toolbar hierarchies
- Custom menus persistent across sessions
- Key navigation and accelerators
- Scope creep
- Attributes for different menu item types
- Sliders, spin buttons, switches
- Presentation attributes
- Linked buttons
- Grid menus
- Attributes for different menu item types
- Still have a way to encode the information just once for creating both menu items and toolbar items.
- And a library should be able to provide that information (with translations) to applications for common actions.
Have a GtkRecentChooser and be able to show the full file paths inside the application UI (e.g. in the statusbar). It's currently possible with GtkRecentChooserMenu.
Create menu items programmatically, so that a library can provide a generic menu item suitable for all applications. Example: calling g_get_application_name() to add the app name in the menu item description. Example in Tepl.
Review of current GTK api
We have the following GMenu- and GAction-based apis:
gtk_application_set_app_menu
gtk_application_set_menubar
gtk_application_get_menu_by_id
gtk_application_set_accels_for_action
gtk_widget_insert_action_group
gtk_menu_new_from_model
gtk_menu_bar_new_from_model
gtk_menu_shell_bind_model
gtk_menu_button_set_menu_model
gtk_popover_new_from_model
GtkEntry::populate-popup
GtkTextView::populate-popup
Gaps / Shortcomings:
No way to bypass GtkMenu for context menus
No proper context menu api at all - have to use gtk_menu_attach and gtk_menu_popup_*
- No way to associate keyboard shortcuts 'locally'
- Some people really want UI assets (strings, icons, accels) associated with actions
The action group hierarchy created with gtk_widget_insert_action_group is poorly documented and understood
- Toolbars are not covered
Proposal
This strawman proposal is split into three steps
Deprecate GtkMenuShell, GtkMenuItem, and GtkMenu in GTK+ 3.22
Make GtkMenuShell, GtkMenuItem, and GtkMenu private classes in GTK+ 3.9x
Improve GMenu API to make menu merging easier from an application standpoint
Add context menu api, like
gtk_widget_set_context_menu
gtk_entry_get_default_context_menu
The deprecation in GTK+ 3.22 is going to be fairly painful for application developers, and we may opt out of it.
Making menu-related GTK+ widgets private is fairly simple build surgery.
The additional GMenu API needs to be identified and discussed with applications making heavy use of menus, and added to the base class inside GIO. Impact on compositors and other platforms needs also further discussion.
Comments
GTK+ 3 doesn't have a convenient non-deprecated API to still be able to construct a traditional design: menubar + toolbar + statusbar. It is not possible with GMenu to show longer descriptions of menu items in the statusbar, something that I prefer to keep in my applications to make the UI better self-discoverable. Another problem is that there is no high-level API to share information between menu items and toolbar items, if GtkBuilder is used to create a GMenu, the information needs to be duplicated to create the toolbar. Since GtkUIManager is deprecated and application authors are told in the porting guide to not use any deprecated API to be able to port to GTK+ 3.9x, the only solution is to use the low-level widget-based API to create menus and toolbars. Since the widget-based API is not convenient to use, a higher-level API needs to be created, which is exactly what I started to do in the Tepl library. Now if the widget-based API is removed from the public API of GTK+ 4, everything must be redone again. Porting an application with a large menu away from GtkUIManager is a lot of boring work. Application developers will port the menu a first time in GTK+ 3 to not use deprecated APIs, and then they will need to port it a second time for GTK+ 4 because the widget-based API has disappeared? Surely that's not nice for application developers. So, if a higher-level API is created upstream in GTK+, it needs to be available in/for GTK+ 3 as well. — SébastienWilmet
Since GtkUIManager is deprecated since GTK+ 3.10, maybe some application developers have already ported their menu and toolbar with the widget-based API. For large applications it's a lot of work. So please don't force application developers to port their menus to new APIs every 5 years or so. — SébastienWilmet
Inside the Tepl repository, I've created the Amtk shared library (Actions, Menus and Toolbars Kit for GTK+), see this blog post. — SébastienWilmet
Update: Amtk is now a separate project, please read its introduction.
Plotinus is another interesting project which is somewhat relevant to this, might be good for meeting the needs of more complex applications or even for exposing settings usually only accessible through dconf editor — JohnMcHugh