Up: index.html, Prev: Section 16, Next: Section 18

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 because of the child object.

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 in this section. “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 of the item are copied and used to form a new item within 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");
  1. menu_item_quit is a menu item. It has a label “Quit” and is connected to an action “app.quit”. “app” is a prefix and “quit” is a name of the action. The prefix “app” means that the action belongs to a GtkApplication instance. If the menu is clicked, then the corresponding action “quit” which belongs to the GtkApplication will be activated.
  2. act_quit is an action. It has a name “quit”. The function g_simple_action_new creates a stateless action. So, act_quit is stateless. The meaning of stateless will be explained later. The argument NULL means that the action doesn’t have an parameter. Most of the actions are stateless and have no parameter.
  3. The action act_quit is added to the GtkApplication instance with g_action_map_add_action. When act_quit is activated, it will emit “activate” signal.
  4. “activate” signal of the action is connected to the handler quit_activated. So, if the action is activated, the handler will be invoked.

Simple example

The following is a simple example of menus and actions.

#include <gtk/gtk.h>

static void
quit_activated(GSimpleAction *action, GVariant *parameter, gpointer user_data) {
  GApplication *app = G_APPLICATION (user_data);

  g_application_quit (app);
}

static void
app_activate (GApplication *app, gpointer user_data) {
  GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));
  gtk_window_set_title (GTK_WINDOW (win), "menu1");
  gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);

  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);

  GMenu *menubar = g_menu_new ();
  GMenuItem *menu_item_menu = g_menu_item_new ("Menu", NULL);
  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);
  g_menu_item_set_submenu (menu_item_menu, G_MENU_MODEL (menu));
  g_menu_append_item (menubar, menu_item_menu);
  g_object_unref (menu_item_menu);

  gtk_application_set_menubar (GTK_APPLICATION (app), G_MENU_MODEL (menubar));
  gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE);
  gtk_window_present (GTK_WINDOW (win));
/*  gtk_widget_show (win); is also OKay instead of gtk_window_present. */
}

#define APPLICATION_ID "com.github.ToshioCP.menu1"

int
main (int argc, char **argv) {
  GtkApplication *app;
  int stat;

  app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);

  stat =g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);
  return stat;
}
menu and action
Screenshot of menu1

Up: index.html, Prev: Section 16, Next: Section 18