Re: GMenu, GAction and GtkBuilder

On Tue, 2013-10-01 at 12:15 +0200, Murray Cumming wrote:
Here is a description of the gtkmm menu API that should replace
GtkUIManager. I'll reply with some opinions about the API and reasons
why I don't think we should actually deprecate UIManager yet.

For a fairly complete example, just look here:

* Actions:

We can replace Gtk::Action/Gtk::ToggleAction/Gtk::RadioAction instances
with Gio::Action. You'll actually want to use Gio::SimpleAction in most
cases, because it has useful API.

We replace Gtk::ActionGroup with Gio::ActionGroup, though you'll want to
actually use Gio::SimpleActionGroup because you can add actions to it.

I find it annoying that the classes we actually use in applications have
this "Simple" in their name, cluttering up our application code. It
feels like GAction should be GActionBase and GSimpleAction should be
GAction. GSimpleAction feel like "GGoodEnoughToActuallyUseAction".

We replace calls to
  Gtk::ActionGroup::add("action-name", "Action Title", signal_handler)
  Gio::SimpleActionGroup::add_action("action-name", signal_handler)


The Action Title now becomes just a menu item title and is specified in
the XML.

Notice that this means you'll need to put that XML in an external file
so that it can be translated by intltools (now yelp-tools). That's not
so bad though. You probably already have external .glade (or .ui) files
that you can add the <menu> to.

However, watch out. Glade doesn't yet support the <menu> syntax. If you
open and save your .glade file in Glade then it will silently remove
your <menu> nodes.
This Glade bug might track that:

We replace GtkUIManager with GtkBuilder, using its <menu> syntax instead
of the old <ui> syntax.

* Toggle and Radio items:

Toggle items and Radio items are a bit strange. Both are demonstrated
here, and are not as simple as the old GtkToggleAction and

** Toggle items:
For a toggle item we add a "bool" action, which is just a regular
GAction with a boolean "state". For instance, with gtkmm:
  m_refToggle = refActionGroup->add_action_bool("sometoggle",
    sigc::mem_fun(*this, &ExampleWindow::on_menu_toggle),
    false); //false is the default value

Notice that this is nicer in gtkmm than in GTK+. In this bug (now a
duplicate) I described the nasty C API for it:

We added similar convenience API in TreeView. For instance,

We also hide the "parameter" parameter from the signal handler in the
common case, because it's an unusual and confusing thing that you
usually won't need. There's add_action_with_parameter() if you need it:

But I still find our API a bit strange. We could add our own
Gio::SimpleToggleAction and Gio::SimpleRadioAction classes to make the
API nicer, but I'd rather delay that because I suspect that they might
appear in the C API itself sometime:
And we would lay that out in the glade file like any other menu item.

Unlike with GtkAction, in the signal handler, we must now explicitly
toggle that state. Otherwise clicking the menu item won't change it's
appearance at all. For instance:
  void ExampleWindow::on_menu_toggle()
    bool active = false;

Obviously, it's good that this gives you the chance to reject the change
and complain to the user, though I feel that that's the unusual case and
shouldn't be the default.

Maybe we could add some code in gtkmm to change the state automatically,
making auto_state_change an extra boolean parameter to add_action_bool()
and Action::create_bool(). We do much the same thing in the TreeView
API, by providing append_column_editable().

** Radio items:

Radio items are even stranger. Unlike GtkRadioAction, we now have just
one GAction to represent, for instance, 3 radio menu items. That GAction
has a state, whose type you must choose. gtkmm has convenience methods
to create string-based or integer-based radio actions. For instance:
  m_refChoice = refActionGroup->add_action_radio_string("choice",
    sigc::mem_fun(*this, &ExampleWindow::on_menu_choices),
    "a"); //"a" is the default value.

Unlike with toggle items, we get the chosen state via the action's
"parameter". Like toggle items, we need to actually set the state to see
any visual effect. For instance:
   void ExampleWindow::on_menu_choices(const Glib::ustring& parameter)

Again, I find this a bit tedious.

We specify the possible states in the XML, as "target" values for an
<item>. Several radio items can have the same action name, but different
target values.

I think this confusion/conflation of "state", "parameter" and "target"
is a big mistake, made worse by the different use of state and parameter
depending on whether it's a toggle or radio item, or just a regular
action with a parameter. I complained about that here, but it seems far
too late to fix it now:
See also

I also don't like duplicating these magic state values across both the
code and the XML file. If we could use enums then it would at least be
more structured. See bug

* Summary

As a complete porting example, this commit replaces the use of UIManager
+GtkMenu with Builder+GMenu in one of our examples:
And this one adds back the toolbar:

There are some other issues that remain unresolved or unclear to me:

1. The accelerators in a main menu will not work unless you also use
Gtk::Application, Gtk::ApplicationWindow and
Gtk::Applicatin::add_accelerator(). It's been suggested that building a
GtkMenuBar from a GMenu, as we do in our examples, should not be done.
1.1 But the alternative, using Gtk::Application::set_menu_bar(), as
shown in one of our examples, does not seem to allow for separate menu
bars in separate windows, while also taking the menu-creating code away
from the window's implementation.

1.2 Those "accel" attributes in the XML are not well documented anyway,
and will soon be deprecated:

2. I don't understand when or why we should use the standard "app" and
"win" action groups instead of custom action groups such as our
"example" ones. I asked here:

3. There is no replacement for GtkRecentAction:
though there is some suggestion that there shouldn't be:

4. There is no similar XML markup for toolbars, to replace the <toolbar>
markup with GtkUIManager. So our examples just create the Gtk::Toolbar
in code and call 
  gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE
(button->gobj()), "example.quit");
For instance, in our example:
We can't use just
because we'd have to break ABI to add a new base class.

Overall, I think we should take the unusual step of undeprecating
Gtk::UIManager and friends in gtkmm before our stable release, even
though the underlying C API is deprecated. Many C developers also seem
to feel that the deprecation in GTK+ was rushed, but we had no response
to requests to delay it. Hopefully things will be clearer for gtkmm

Murray Cumming
murrayc murrayc com

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]