Menu and action

Users often use menus to tell a command to the computer. It is like this:

Menu

Now let’s analyze the menu above. There are two types of object.

Menu structure

Menus can build a complicated structure thanks to the links of menu items.

GMenuModel, GMenu and GMenuItem

GMenuModel is an abstract object which represents a menu. GMenu is a simple implementation of GMenuModel and a child object of GMenuModel.

GObject -- GMenuModel -- GMenu

Because GMenuModel is an abstract object, it isn’t instantiatable. Therefore, it doesn’t have any functions to create its instance. If you want to create a menu, use g_menu_new to create a GMenu instance. GMenu inherits all the functions of GMenuModel.

GMenuItem is an object directly derived from GObject. GMenuItem and Gmenu (or GMenuModel) don’t have a parent-child relationship.

GObject -- GMenuModel -- GMenu
GObject -- GMenuItem

GMenuItem has attributes. One of the attributes is label. For example, there is a menu item which has “Edit” label in the first diagram. “Cut”, “Copy”, “Paste” and “Select All” are also the labels of the menu items. Other attributes will be explained later.

Some menu items have a link to another GMenu. There are two types of links, submenu and section.

GMenuItem can be inserted, appended or prepended to GMenu. When it is inserted, all of the attributes and link values are copied and stored in the menu. The GMenuItem itself is not really inserted. Therefore, after the insertion, GMenuItem is useless and it should be freed. The same goes for appending or prepending.

The following code shows how to append GMenuItem to GMenu.

GMenu *menu = g_menu_new ();
GMenuItem *menu_item_quit = g_menu_item_new ("Quit", "app.quit");
g_menu_append_item (menu, menu_item_quit);
g_object_unref (menu_item_quit);

One of the attributes of menu items is an action. This attribute points an action object.

There are two action objects, GSimpleAction and GPropertyAction. GSimpleAction is often used. And it is used with a menu item. Only GSimpleAction is described in this section.

An action corresponds to a menu item will be activated when the menu item is clicked. Then the action emits an activate signal.

  1. menu item is clicked.
  2. The corresponding action is activated.
  3. The action emits a signal.
  4. The connected handler is invoked.

The following code is an example.

static void
quit_activated(GSimpleAction *action, GVariant *parameter, gpointer app) { ... ... ...}

GSimpleAction *act_quit = g_simple_action_new ("quit", NULL);
g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_quit));
g_signal_connect (act_quit, "activate", G_CALLBACK (quit_activated), app);
GMenuItem *menu_item_quit = g_menu_item_new ("Quit", "app.quit");

If the menu is clicked, the corresponding action “quit” will be activated and emits an “activate” signal. Then, the handler quit_activated is called.

A menu bar and menus are traditional style. Menu buttons are often used instead of a menu bar lately, but the old style is still used widely.

Applications have only one menu bar. If an application has two or more windows which have menu bars, the menu bars are exactly the same. Because every window refers to the same menubar instance in the application.

An application’s menu bar is usually unchanged once it is set. So, it is appropriate to set it in the “startup” handler. Because it is called only once in the primary application instance.

I think it is good for readers to clarify how applications behave.

Therefore, an “activate” or “open” handler can be called twice or more. On the other hand, a “startup” handler is called once. So, setting a menubar should be done in the “startup” handler.

static void
app_startup (GApplication *app) {
... ... ...
  gtk_application_set_menubar (GTK_APPLICATION (app), G_MENU_MODEL (menubar));
... ... ...
}

Simple example

The following is a simple example of menus and actions. The source file menu1.c is located at src/menu directory.

@@@include menu/menu1.c @@@

menu and action

Compiling

Change your current directory to src/menu. Use comp to compile menu1.c.

$ comp menu1
$ ./a.out

Then, a window appears. Click on “Menu” on the menubar, then a menu appears. Click on “Quit” menu, then the application quits.

Screenshot of menu1

Primary and remote application instances

Let’s try running the application twice. Use & in your shell command line, then the application runs concurrently.

$ ./a.out &
[1] 70969
$ ./a.out
$ 

Then, two windows appear.

Both the windows have menu bars. And they are exactly the same. The two windows belong to the primary instance.

If you click on the “Quit” menu, the application (the primary instance) quits.

menu1 – two windows

The second run makes a new window. However, it depends on the “activate” handler. If you create your window in the startup handler and the activate handler just presents the window, no new window is created at the second run. For example, tfe (text file editor) doesn’t create a second window. It just creates a new notebook page. Because its activate handler doesn’t create any window but just creates a new notebook page.

Second or more executions often happen on the desktop applications. If you double-click the icon twice or more, the application is run multiple times. Therefore, you need to think about your startup and activate (open) handler carefully.