Alternate menubar layout

/!\ This is currently a proposal (and incomplete, at that). This is not an official recommendation for GNOME applications.

Use cases

These are the different types of situations that an application may find itself in with respect to the user's desired menu layout:

  • GNOME 3
  • "new" GNOME 2/Windows/KDE style (no way to display an appmenu, but still prefer menu buttons to menubars)
  • "oldschool" GNOME 2/MATE/Windows/KDE style (ie: prefer menubars to menu buttons)
  • Mac OS
  • Unity (prefer oldschool menubar at the top)

"Old school" here means the traditional File/Edit/View/Help type of menubar layout.

The instructions below are only helpful for the case of an app that wants to work in GNOME 3 but wants to use the toplevel menubar when in Mac OS. They are also partially applicable to Unity, but Unity tends to prefer that applications use an "oldschool" menu layout, and skip the appmenu entirely (which is not handled here).

Current instructions

This page attempts to roughly document a proposal for how applications should deal with providing an alternate menubar layout for running under Unity or Mac OS.

Both of these platforms have a menubar at the top of the screen that users expect to contain the menus of the running application. GtkMenuButton-in-GtkHeaderBar menus don't make much sense in this context.

The approach is fairly simple:

  • ensure all GtkMenuButton menus are defined via GMenuModel

  • define an alternate menubar layout
  • prevent the alternate menubar from ever being shown locally
  • hide headerbar content when the alternate menubar is shown on Unity or Mac OS

Use GMenuModel for all GtkMenuButton menus

This is pretty straightforward. If you're not already doing so, probably you want to construct the GMenu with GtkBuilder.

The reason that you want to do this is because all functionality that can be hit from your GtkMenuButton menus should be in the form of GAction so that these actions can also be hit from the equivalent items in the alternative menubar layout.

Define an alternate menubar

You should define a menubar layout using GMenu-in-GtkBuilder. In many cases, this will already exist in older versions of the app in question (before it was changed to use GtkMenuButton).

You must (manually) ensure that the content of the alternative menubar is kept to feature parity with the menus attached to GtkMenuButtons that you will hide in order that all features of the application are available on all platforms. This is perhaps the weakest point of this proposal -- having to remember to update menus in two places.

Because all functionality that would have been accessible from the GtkHeaderBar menus is in the form of actions, you simply have to use the same action names to hit that functionality.

Set this menubar on your GtkApplication using gtk_application_set_menubar().

Hide the local menubar

Normally, when using gtk_application_set_menubar() the menubar would be shown locally inside of each window. Because the functionality is already provided by the GtkHeaderBar, we want to suppress this. This menu should only ever be shown outside of the window.

The way to accomplish that is via the "show-menubar" property on GtkApplicationWindow. Call gtk_application_window_set_show_menubar() to set that to FALSE (or pass an initial value for the property to your g_object_new() call, if subclassing).

Conditionally hide headerbar content

Since the menu is never shown locally, we only want to hide the headerbar content in the case that the menu is shown remotely by a desktop environment like Unity or Mac OS.

Fortunately, we have a relatively easy way to determine when this is the case -- the "gtk-shell-shows-menubar" property on GtkSettings. GObject property bindings are your friend here.

Assuming you have a widget like "menu_button", do something like this:

   GtkSettings *settings;
   GtkWidget *menu_button;

   menu_button = (GtkWidget *) gtk_builder_get_object (builder, "menu-button");
   settings = gtk_settings_get_default ();
   g_object_bind_property (settings, "gtk-shell-shows-menubar",
                           menu_button, "visible",
                           G_BINDING_INVERT_BOOLEAN);

We invert the boolean because we want the menu button to be visible when the menubar is not visible.