Some actions have states. The typical values of states are boolean or string. However, other types of states are possible if you want.
Actions which have states are called stateful.
Some menus are called toggle menu. For example, fullscreen menu has a state which has two values – fullscreen and non-fullscreen. The value of the state is changed every time the menu is clicked. An action corresponds to the fullscreen menu also have a state. Its value is TRUE or FALSE and it is called boolean value. TRUE corresponds to fullscreen and FALSE to non-fullscreen.
The following is an example code to implement a fullscreen menu except the signal handler. The signal handler will be shown later.
*act_fullscreen = g_simple_action_new_stateful ("fullscreen",
GSimpleAction , g_variant_new_boolean (FALSE));
NULL(act_fullscreen, "change-state", G_CALLBACK (fullscreen_changed), win);
g_signal_connect (G_ACTION_MAP (win), G_ACTION (act_fullscreen));
g_action_map_add_action ... ... ...
*menu_item_fullscreen = g_menu_item_new ("Full Screen", "win.fullscreen"); GMenuItem
act_fullscreen
is a GSimpleAction instance. It is
created with g_simple_action_new_stateful
. The function has
three arguments. The first argument “fullscreen” is the name of the
action. The second argument is a parameter type. NULL
means
the action doesn’t have a parameter. The third argument is the initial
state of the action. It is a GVariant value. GVariant will be explained
in the next subsection. The function
g_variant_new_boolean (FALSE)
returns a boolean type
GVariant value which is FALSE
. If there are two or more top
level windows, each window has its own act_fullscreen
action. So, the number of the actions is the same as the number of the
windows.act_fullscreen
has “change-state” signal.
The signal is connected to a handler fullscreen_changed
. If
the fullscreen menu is clicked, then the corresponding action
act_fullscreen
is activated. But no handler is connected to
the “activate” signal. Then, the default behavior for boolean-stated
actions with a NULL parameter type like act_fullscreen
is
to toggle them via the “change-state” signal.win
. Therefore,
the scope of the action is “win”.menu_item_fullscreen
is a GMenuItem instance. There are
two arguments. The first argument “Full Screen” is the label of
menu_item_fullscreen
. The second argument is an action. The
action “win.fullscreen” has a prefix “win” and an action name
“fullscreen”. The prefix says that the action belongs to the
window.static void
fullscreen_changed(GSimpleAction *action, GVariant *value, GtkWindow *win) {
if (g_variant_get_boolean (value))
gtk_window_maximize (win);
else
gtk_window_unmaximize (win);
g_simple_action_set_state (action, value);
}
fullscreen_changed
has three parameters.
The first parameter is the action which emits the “change-state” signal.
The second parameter is the value of the new state of the action. The
third parameter is a user data which is set in
g_signal_connect
.TRUE
, then it
maximizes the window. Otherwise it un-maximizes.value
. Note: At this
stage, that means the stage before
g_simple_action_set_state
is called, the state of the
action still has the original value. So, you need to set the state with
the new value by g_simple_action_set_state
.You can use “activate” signal instead of “change-state” signal, or both signals. But the way above is the simplest and the best.
GVariant is a fundamental type. It isn’t a child of GObject. GVariant
can contain boolean, string or other type values. For example, the
following program assigns TRUE to value
whose type is
GVariant.
*value = g_variant_new_boolean (TRUE); GVariant
Another example is:
*value2 = g_variant_new_string ("Hello"); GVariant
value2
is a GVariant and it has a string type value
“Hello”. GVariant can contain other types like int16, int32, int64,
double and so on.
If you want to get the original value, use g_variant_get series functions. For example, you can get the boolean value with g_variant_get_boolean.
bool = g_variant_get_boolean (value); gboolean
Since value
has been created as a boolean type GVariant
with TRUE
value, bool
equals
TRUE
. In the same way, you can get a string from
value2
const char *str = g_variant_get_string (value2, NULL);
The second parameter is a pointer to gsize type variable (gsize is
defined as unsigned long). If it isn’t NULL, then the pointed value is
used as the length by the function. If it is NULL, nothing happens. The
returned string str
is owned by the instance and can’t be
changed or freed by the caller.
Another example of stateful actions is an action corresponds to color select menus. For example, there are three menus and each menu has red, green or blue color respectively. They determine the background color of a GtkLabel widget. One action is connected to the three menus. The action has a state whose value is “red”, “green” or “blue”. The values are string. Those colors are given to the signal handler as a parameter.
... ... ...
*vtype = g_variant_type_new("s");
GVariantType *act_color
GSimpleAction = g_simple_action_new_stateful ("color", vtype, g_variant_new_string ("red"));
(vtype);
g_variant_type_free *menu_item_red = g_menu_item_new ("Red", "app.color::red");
GMenuItem *menu_item_green = g_menu_item_new ("Green", "app.color::green");
GMenuItem *menu_item_blue = g_menu_item_new ("Blue", "app.color::blue");
GMenuItem (act_color, "activate", G_CALLBACK (color_activated), NULL);
g_signal_connect ... ... ...
g_variant_type_new
. The argument
of the function is a GVariant type string. So,
g_variant_type_new("s")
returns a GVariantType structure
contains a string type. The returned value, GVariantType structure, is
owned by the caller. So, you need to free it when it becomes
useless.act_color
points a GSimpleAction instance.
It is created with g_simple_action_new_stateful
. The
function has three arguments. The first argument “color” is the name of
the action. The second argument is a parameter type which is
GVariantType. g_variant_type_new("s")
creates GVariantType
which is a string type (G_VARIANT_TYPE_STRING
). The third
argument is the initial state of the action. It is a GVariant. The
function g_variant_new_string ("red")
returns a GVariant
value which has the string value “red”. GVariant has a reference count
and g_variant_new_...
series functions returns a GVariant
value with a floating reference. That means the caller doesn’t own the
value at this point. And g_simple_action_new_stateful
function consumes the floating reference so you don’t need to care about
releasing the GVariant instance.vtype
is useless after
g_simple_action_new_stateful
. It is released with the
function g_variant_type_free
.menu_item_red
points a GMenuItem instance.
The function g_menu_item_new
has two arguments. The first
argument “Red” is the label of menu_item_red
. The second
argument is a detailed action. Its prefix is “app”, action name is
“color” and target is “red”. Target is sent to the action as a
parameter. The same goes for menu_item_green
and
menu_item_blue
.g_signal_connect
connects the activate
signal on the action act_color
and the handler
color_activated
. If one of the three menus is clicked, then
the action act_color
is activated with the target
(parameter) which is given by the menu.The following is the “activate” signal handler.
static void
(GSimpleAction *action, GVariant *parameter) {
color_activatedchar *color = g_strdup_printf ("label.lb {background-color: %s;}",
(parameter, NULL));
g_variant_get_string (provider, color, -1);
gtk_css_provider_load_from_data (color);
g_free (G_ACTION (action), parameter);
g_action_change_state }
g_signal_connect
function. But it is
left out because the fourth argument of the
g_signal_connect
has been NULL. The first parameter is the
action which emits the “activate” signal. The second parameter is the
parameter, or target, given to the action. It is a color specified by
the menu.color
is a CSS string created by
g_strdup_printf
. The arguments of
g_strdup_printf
are the same as printf C standard function.
The function g_variant_get_string
gets the string contained
in parameter
. The string is owned by the instance and you
mustn’t change or free it. The string label.lb
is a
selector. It consists of label
, a node name of GtkLabel,
and lb
which is a class name. It selects GtkLabel which has
lb
class. For example, menus have GtkLabel to display their
labels, but they don’t have lb
class. So, the CSS doesn’t
change their background color. The string
{background-color %s}
makes the background color
%s
to which the color from parameter
is
assigned.color
.g_action_change_state
.Note: If you haven’t set an “activate” signal handler, the signal is
forwarded to “change-state” signal. So, you can use “change-state”
signal instead of “activate” signal. See
src/menu/menu2_change_state.c
.
GVariantType gives a type of GVariant. GVariantType is created with a type string.
The following program is a simple example. It finally outputs the string “s”.
#include <glib.h>
int
main (int argc, char **argv) {
GVariantType *vtype = g_variant_type_new ("s");
const char *type_string = g_variant_type_peek_string (vtype);
g_print ("%s\n",type_string);
g_variant_type_free (vtype);
}
g_variant_type_new
creates a GVariantType
structure. The argument “s” is a type string. It means string. The
returned structure is owned by the caller. When it becomes useless, you
need to free it with the function g_variant_type_free
.g_variant_type_peek_string
takes a peek at
vtype
. It is the string “s” given to vtype
when it was created. The string is owned by the instance and the caller
can’t change or free it.vtype
before g_print
because the string type_string
is owned by vtype
.vtype
.The following code includes stateful actions above. This program has menus like this:
The code is as follows.
#include <gtk/gtk.h>
/* The provider below provides application wide CSS data. */
GtkCssProvider *provider;
static void
fullscreen_changed(GSimpleAction *action, GVariant *value, GtkWindow *win) {
if (g_variant_get_boolean (value))
gtk_window_maximize (win);
else
gtk_window_unmaximize (win);
g_simple_action_set_state (action, value);
}
static void
color_activated(GSimpleAction *action, GVariant *parameter) {
char *color = g_strdup_printf ("label.lb {background-color: %s;}", g_variant_get_string (parameter, NULL));
/* Change the CSS data in the provider. */
/* Previous data is thrown away. */
gtk_css_provider_load_from_data (provider, color, -1);
g_free (color);
g_action_change_state (G_ACTION (action), parameter);
}
static void
app_shutdown (GApplication *app, GtkCssProvider *provider) {
gtk_style_context_remove_provider_for_display (gdk_display_get_default(), GTK_STYLE_PROVIDER (provider));
}
static void
app_activate (GApplication *app) {
GtkWindow *win = GTK_WINDOW (gtk_application_window_new (GTK_APPLICATION (app)));
gtk_window_set_title (win, "menu2");
gtk_window_set_default_size (win, 400, 300);
GtkWidget *lb = gtk_label_new (NULL);
gtk_widget_add_css_class (lb, "lb"); /* the class is used by CSS Selector */
gtk_window_set_child (win, lb);
GSimpleAction *act_fullscreen
= g_simple_action_new_stateful ("fullscreen", NULL, g_variant_new_boolean (FALSE));
g_signal_connect (act_fullscreen, "change-state", G_CALLBACK (fullscreen_changed), win);
g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_fullscreen));
gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE);
gtk_window_present (win);
}
static void
app_startup (GApplication *app) {
GVariantType *vtype = g_variant_type_new("s");
GSimpleAction *act_color
= g_simple_action_new_stateful ("color", vtype, g_variant_new_string ("red"));
g_variant_type_free (vtype);
GSimpleAction *act_quit
= g_simple_action_new ("quit", NULL);
g_signal_connect (act_color, "activate", G_CALLBACK (color_activated), NULL);
g_signal_connect_swapped (act_quit, "activate", G_CALLBACK (g_application_quit), app);
g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_color));
g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_quit));
GMenu *menubar = g_menu_new ();
GMenu *menu = g_menu_new ();
GMenu *section1 = g_menu_new ();
GMenu *section2 = g_menu_new ();
GMenu *section3 = g_menu_new ();
GMenuItem *menu_item_fullscreen = g_menu_item_new ("Full Screen", "win.fullscreen");
GMenuItem *menu_item_red = g_menu_item_new ("Red", "app.color::red");
GMenuItem *menu_item_green = g_menu_item_new ("Green", "app.color::green");
GMenuItem *menu_item_blue = g_menu_item_new ("Blue", "app.color::blue");
GMenuItem *menu_item_quit = g_menu_item_new ("Quit", "app.quit");
g_menu_append_item (section1, menu_item_fullscreen);
g_menu_append_item (section2, menu_item_red);
g_menu_append_item (section2, menu_item_green);
g_menu_append_item (section2, menu_item_blue);
g_menu_append_item (section3, menu_item_quit);
g_object_unref (menu_item_red);
g_object_unref (menu_item_green);
g_object_unref (menu_item_blue);
g_object_unref (menu_item_fullscreen);
g_object_unref (menu_item_quit);
g_menu_append_section (menu, NULL, G_MENU_MODEL (section1));
g_menu_append_section (menu, "Color", G_MENU_MODEL (section2));
g_menu_append_section (menu, NULL, G_MENU_MODEL (section3));
g_menu_append_submenu (menubar, "Menu", G_MENU_MODEL (menu));
g_object_unref (section1);
g_object_unref (section2);
g_object_unref (section3);
g_object_unref (menu);
gtk_application_set_menubar (GTK_APPLICATION (app), G_MENU_MODEL (menubar));
provider = gtk_css_provider_new ();
/* Initialize the css data */
gtk_css_provider_load_from_data (provider, "label.lb {background-color: red;}", -1);
/* Add CSS to the default GdkDisplay. */
gtk_style_context_add_provider_for_display (gdk_display_get_default (),
GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), provider);
g_object_unref (provider); /* release provider, but it's still alive because the display owns it */
}
#define APPLICATION_ID "com.github.ToshioCP.menu2"
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
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;
}
app_shutdown
is called when the
application quits. It removes the provider from the display.win
. Its
title and default size are set to “menu2” and 400x300 respectively.lb
The
label is given a CSS class “lb”. It is added to win
as a
child.act_fullscreen
. It’s connected to the signal handler
fullscreen_changed
. It’s added to the window, so the action
scope is “win”. So, if there are two or more windows, the actions are
created two or more.gtk_application_window_set_show_menubar
adds a menubar to
the window.act_color
and act_quit
are created. These actions exists only one because the startup handler
is called once. They are connected to their handlers and added to the
application. Their scopes are “app”.Change your current directory to src/menu
.
$ comp menu2
$./a.out
Then, you will see a window and the background color of the content is red. You can change the size to maximum and change back to the original size. You can change the background color to green or blue.
If you run the second application during the first application is
running, another window will appear in the same screen. Both of the
window have the same background color. Because the
act_color
action has “app” scope and the CSS is applied to
the default display shared by the windows.
$ ./a.out & # Run the first application
[1] 82113
$ ./a.out # Run the second application
$