Using the GAction Framework

In GUI development you typically want to centralize event handlers given that it is pretty common to have multiple GUI elements (button, menu item, etc) invoking the same event. In many frameworks these centralized event handlers are typically referred to as actions. In GTK the action framework is part of GIO and is referred to as GAction. An overview of the framework can be found in the Gnome wiki in this How Do I entry.

Having done Delphi development 15 years ago I had some familiarity with the concept but it still took a bit of time to get up to speed on GActions. While the Gnome wiki provided some basic information, I still found it confusing in some spots so I thought I’d write a bit about my experience using GActions in Terminix as it specifically relates to the D language and the GtkD framework.

First some basics, every GAction has two elements that identify it, a prefix and a name. The prefix is a way to categorize actions and within a prefix the name of the action must be unique. The prefix and the name together, with a period between them, make a detailed action name and this is how you reference actions when binding them to UI elements. So if you have a prefix of “terminal” and a name of “copy” the detailed name would be “terminal.copy”.

Actions are stored in containers which typically implement the interfaces gio.ActionMapIF and gio.ActionGroupIF. Out of the box, GTK implements these interfaces in Application and ApplicationWindow. Actions registered against these objects are automatically assigned the prefix of “app” and “win” respectively. Typically actions registered against the Application object are used in the Gnome application menu and actions registered against ApplicationWindow operate against that window.

An important thing to understand though is you are not limited to registering actions against just Application and ApplicationWindow, you can register actions against any widget in your application using custom prefixes. The key is using the gio.SimpleActionGroup to create your own container to register actions against. You can then bind the SimpleActionGroup to a widget via the method insertActionGroup. An example of this is shown below:

SimpleActionGroup sagTerminalActions = new SimpleActionGroup();
//...create actions and register to the group
insertActionGroup(“terminal”, sagTerminalActions);

Understanding how to create your own action groups gives you a lot of flexibility in organizing your actions. Terminix is an application where the UI is heavily nested and generally I wanted to keep each layer isolated from each other. I didn’t want the Terminal to know too much about the containers it is nested within. Being able to create these custom action groups allows the terminal to have it’s own set of actions without needing a mechanism to bind them to the window.

The other benefit to this approach is that the GIO framework will allow you to have multiple instances of the same action and will determine which instance get’s invoked based on the scope. In Terminix I have multiple terminals each with the same set of “terminal” actions registered. The GAction framework automatically invokes the right action instance based on the terminal that has focus presumably by walking up the widget hierarchy until it finds the first widget with the action it is looking for.

To register an action, you simply create an instance of gio.SimpleAction which is a concrete implementation of the ActionIF interface and the add it to the desired ActionMap. A simple example would look like:

action = new SimpleAction(“copy”, null);
action.addOnActivate(delegate(Variant, SimpleAction) {
vte.copyClipboard();
});
sagTerminalActions.add(action)

Once you have your action, the next step is to bind it to a user interface element. One thing to be aware of is that in GTK there are actually two action frameworks, the one in the gio package (SimpleAction, SimpleActionGroup) and one in the gtk package (Action, ActionGroup). The one in GTK is considered deprecated and it is highly recommended to use the GIO action framework where possible. In general, GTK widgets that work with GIO actions use the method setActionName to specify the target action, the method setActionTargetValue is used by the GTK framework.

To bind the action to a UI element, you simply call the setActionName method with the detailed action name, for example:

button.setActionName(“terminal.copy);

Actions can be represented in menus using a variety of UI elements such as check and radio buttons. This involves declaring that an action has state which is represented by a GLib Variant in the Activate signal. I won’t spend any time on this topic, however for those interested I wrote an actions demo showing how to do this with popovers and it is available in the GtkD repository here.

If you want your action to have an accelerator, i.e. a keyboard shortcut, you need to register the key and the detailed action name with the your global GTK Application instance using the method Application.setAccelsForAction. Here is an example of this:

app.setAccelsForAction(“terminal.copy”, [“c”]);

This concludes the article on GActions, hopefully as you can see this provides a powerful framework for centralizing your event handlers within a GTK application. One final note, for the purposes of this article I’ve been using strings directly for prefixes and names for illustration. Obviously, you should declare these as constants in your code to ensure consistency. If you need to change the name of a prefix or action, having the string declared in one spot makes that job much easier.

Leave a Reply

Your email address will not be published. Required fields are marked *


*