有些动作action有状态。状态的典型值是布尔值或字符串。但是,如果你愿意,也可以使用其他类型的状态。
具有状态的动作称为有状态的。
Stateful action without a paramete
有些菜单被称为切换菜单。例如,全屏菜单有一个状态,它有两个值——全屏和非全屏。每次单击菜单时,状态的值都会改变。一个动作对应全屏菜单也有一个状态。它的值为TRUE或FALSE,称为布尔值。TRUE表示全屏,FALSE表示非全屏。
下面是除信号处理程序之外实现全屏菜单的示例代码。稍后将介绍信号处理程序。
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));
... ... ...
GMenuItem *menu_item_fullscreen = g_menu_item_new ("Full Screen", "win.fullscreen");
- act_fullscreen是一个GSimpleAction实例。它是用g_simple_action_new_stateful创建的。这个函数有三个参数。第一个参数"fullscreen"是操作的名称。第二个参数是形参类型。NULL表示操作没有参数。第三个参数是动作的初始状态。它是一个GVariant值。下一小节将解释GVariant。函数g_variant_new_boolean (FALSE)返回一个GVariant值,即布尔值FALSE。如果有两个或更多的顶层窗口,每个窗口都有自己的act_fullscreen操作。因此,操作的数量与窗口的数量相同。
- 连接动作act_fullscreen和“change-state”信号处理程序fullscreen_changed。如果单击全屏菜单,则激活相应的动作act_fullscreen。但没有处理器连接到"激活"信号。然后,对于具有NULL参数类型(如act_fullscreen)的布尔声明的操作,默认行为是通过“change-state”信号切换它们。
- 该操作被添加到GtkWindow win中。因此,行动的范围是 “win” – window。
- menu_item_fullscreen是一个GMenuItem实例。有两个参数。第一个参数"Full Screen"是menu_item_fullscreen的标签。第二个参数是一个动作。动作“赢”。Fullscreen “前缀是"win”,操作名称是" Fullscreen "。前缀表示操作属于窗口。
1 static void
2 fullscreen_changed(GSimpleAction *action, GVariant *value, GtkWindow *win) {
3 if (g_variant_get_boolean (value))
4 gtk_window_maximize (win);
5 else
6 gtk_window_unmaximize (win);
7 g_simple_action_set_state (action, value);
8 }
- fullscreen_changed处理程序有三个参数。第一个参数是发出“change-state”信号的动作。第二个参数是操作的新状态的值。第三个参数是一个用户数据,在g_signal_connect中设置。
- 如果值是布尔类型且为TRUE,则最大化窗口;否则unmaximizes。
- 用value设置操作的状态。注意:第二个参数是切换后的状态值,但在此阶段,操作的状态具有原始值。因此,您需要通过g_simple_action_set_state用新值设置状态。
你可以使用“activate”信号代替“change-state”信号,或者同时使用两种信号。但上面的方法是最简单也是最好的。
GVariant
GVarient可以包含布尔值、字符串或其他类型的值。例如,下面的程序将TRUE赋值给类型为GVariant的值。
GVariant *value = g_variant_new_boolean (TRUE);
另一个例子是:
GVariant *value2 = g_variant_new_string ("Hello");
value2是一个GVariant,它的值是字符串类型"Hello"。GVariant可以包含其他类型,如int16、int32、int64、double等。
如果你想获得原始值,可以使用g_variant_get系列函数。例如,可以使用g_variant_get_boolean获取布尔值。
gboolean bool = g_variant_get_boolean (value);
因为value被创建为布尔类型GVariant和TRUE value,所以bool等于TRUE。同样,你可以从value2中得到一个字符串
const char *str = g_variant_get_string (value2, NULL);
第二个形参是一个指向gsize类型变量的指针(gsize定义为unsigned long)。如果它不是NULL,那么字符串的长度将由函数设置。如果是NULL,什么都不会发生。返回的字符串str不能更改。
Stateful action with a parameter
另一个有状态操作的例子是与颜色选择菜单相对应的操作。例如,有三个菜单,每个菜单分别有红色、绿色和蓝色。它们确定GtkLabel部件的背景颜色。一个操作连接到三个菜单。这个动作有一个值为"red"、"green"或"blue"的状态。值是字符串。这些颜色作为参数提供给信号处理程序。
... ... ...
GSimpleAction *act_color = g_simple_action_new_stateful ("color",
g_variant_type_new("s"), g_variant_new_string ("red"));
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");
g_signal_connect (act_color, "activate", G_CALLBACK (color_activated), NULL);
... ... ...
- act_color是一个GSimpleAction实例。它是用g_simple_action_new_stateful创建的。这个函数有三个参数。第一个参数"color"是操作的名称。第二个参数是一个参数类型,它是GVariantType。g_variant_type_new(“s”)创建字符串类型GVariantType (G_VARIANT_TYPE_STRING)。第三个参数是动作的初始状态。它是一个GVariant。GVariantType将在下一小节中解释。函数g_variant_new_string (“red”)返回一个GVariant值,其字符串值为"red"。
- menu_item_red是一个GMenuItem实例。有两个参数。第一个实参"Red"是menu_item_red的标签。第二个参数是一个详细的操作。它的前缀是"app",动作名称是"color",目标是"red"。Target作为参数发送给动作。menu_item_green和menu_item_blue也是如此。
- 连接动作act_color和“activate”信号处理程序color_activated。如果单击三个菜单中的一个,则通过菜单给出的target(参数)激活动作act_color。
下面是“activate”信号处理程序。
static void
color_activated(GSimpleAction *action, GVariant *parameter) {
char *color = g_strdup_printf ("label.lb {background-color: %s;}",
g_variant_get_string (parameter, NULL));
gtk_css_provider_load_from_data (provider, color, -1);
g_free (color);
g_action_change_state (G_ACTION (action), parameter);
}
- 有三个参数。第一个参数是发出“activate”信号的动作。第二个参数是给动作的参数。它是由菜单指定的颜色。第三个参数被省略了,因为g_signal_connect的第四个参数是NULL。
- color是由g_strdup_printf创建的CSS字符串。g_strdup_printf的参数与printf C标准函数相同。G_variant_get_string获取参数中包含的字符串。你不能改变或释放字符串。
- 设置css提供程序的颜色。label.lb是一个选择器。lable是GtkLabel的节点名,lb是一个类。label.lb选择具有lb类的GtkLabel。例如,菜单有GtkLabel来显示它们的标签,但它们没有lb类。因此,CSS不会改变它们的背景颜色。 - {background-color %s}将背景色设置为from参数中的颜色。
- 释放字符串color。
- 通过g_action_change_state改变状态。
注意:如果您没有设置“activate”信号处理程序,信号将被转发到“change-state”信号。因此,你可以使用“change-state”信号而不是“activate”信号。看src/menu/menu2_change_state.c.
GVariantType
GVariantType给出GVariant的类型。GVariant可以包含多种类型。而且类型通常需要在运行时识别。
GVariantType由一个表示类型的字符串创建。
“b”表示布尔类型。
“s”表示字符串类型。
下面的程序是一个简单的例子。最后输出字符串"s"。
1 #include <glib.h>
2
3 int
4 main (int argc, char **argv) {
5 GVariantType *vtype = g_variant_type_new ("s");
6 const char *type_string = g_variant_type_peek_string (vtype);
7 g_print ("%s\n",type_string);
8 }
- g_variant_type_new创建GVariantType。它使用字符串类型“s”,表示字符串。
- g_variant_type_peek_string查看了vtype。它是创建vtype时传给它的字符串"s"。
- 将字符串打印到终端。
Example
下面的代码包含了上面的有状态操作。这个程序有这样的菜单:
- 全屏菜单在最大和非最大窗口之间切换窗口大小。如果窗口是最大尺寸的,即fullscreen,则在fullscreen标签之前添加一个复选标记。
- 红绿蓝菜单决定了任何标签的背景颜色。菜单的左侧有单选按钮。选中菜单的单选按钮打开。
- 退出菜单退出应用程序。
代码如下。
1 #include <gtk/gtk.h>
2
3 /* The provider below provides application wide CSS data. */
4 GtkCssProvider *provider;
5
6 static void
7 fullscreen_changed(GSimpleAction *action, GVariant *value, GtkWindow *win) {
8 if (g_variant_get_boolean (value))
9 gtk_window_maximize (win);
10 else
11 gtk_window_unmaximize (win);
12 g_simple_action_set_state (action, value);
13 }
14
15 static void
16 color_activated(GSimpleAction *action, GVariant *parameter) {
17 char *color = g_strdup_printf ("label.lb {background-color: %s;}", g_variant_get_string (parameter, NULL));
18 /* Change the CSS data in the provider. */
19 /* Previous data is thrown away. */
20 gtk_css_provider_load_from_data (provider, color, -1);
21 g_free (color);
22 g_action_change_state (G_ACTION (action), parameter);
23 }
24
25 static void
26 remove_provider (GApplication *app, GtkCssProvider *provider) {
27 GdkDisplay *display = gdk_display_get_default();
28
29 gtk_style_context_remove_provider_for_display (display, GTK_STYLE_PROVIDER (provider));
30 g_object_unref (provider);
31 }
32
33 static void
34 app_activate (GApplication *app) {
35 GtkWindow *win = GTK_WINDOW (gtk_application_window_new (GTK_APPLICATION (app)));
36 gtk_window_set_title (win, "menu2");
37 gtk_window_set_default_size (win, 400, 300);
38
39 GtkWidget *lb = gtk_label_new (NULL);
40 gtk_widget_add_css_class (lb, "lb"); /* the class is used by CSS Selector */
41 gtk_window_set_child (win, lb);
42
43 GSimpleAction *act_fullscreen
44 = g_simple_action_new_stateful ("fullscreen", NULL, g_variant_new_boolean (FALSE));
45 g_signal_connect (act_fullscreen, "change-state", G_CALLBACK (fullscreen_changed), win);
46 g_action_map_add_action (G_ACTION_MAP (win), G_ACTION (act_fullscreen));
47
48 gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE);
49
50 gtk_window_present (win);
51 }
52
53 static void
54 app_startup (GApplication *app) {
55 GSimpleAction *act_color
56 = g_simple_action_new_stateful ("color", g_variant_type_new("s"), g_variant_new_string ("red"));
57 GSimpleAction *act_quit
58 = g_simple_action_new ("quit", NULL);
59 g_signal_connect (act_color, "activate", G_CALLBACK (color_activated), NULL);
60 g_signal_connect_swapped (act_quit, "activate", G_CALLBACK (g_application_quit), app);
61 g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_color));
62 g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_quit));
63
64 GMenu *menubar = g_menu_new ();
65 GMenu *menu = g_menu_new ();
66 GMenu *section1 = g_menu_new ();
67 GMenu *section2 = g_menu_new ();
68 GMenu *section3 = g_menu_new ();
69 GMenuItem *menu_item_fullscreen = g_menu_item_new ("Full Screen", "win.fullscreen");
70 GMenuItem *menu_item_red = g_menu_item_new ("Red", "app.color::red");
71 GMenuItem *menu_item_green = g_menu_item_new ("Green", "app.color::green");
72 GMenuItem *menu_item_blue = g_menu_item_new ("Blue", "app.color::blue");
73 GMenuItem *menu_item_quit = g_menu_item_new ("Quit", "app.quit");
74
75 g_menu_append_item (section1, menu_item_fullscreen);
76 g_menu_append_item (section2, menu_item_red);
77 g_menu_append_item (section2, menu_item_green);
78 g_menu_append_item (section2, menu_item_blue);
79 g_menu_append_item (section3, menu_item_quit);
80 g_object_unref (menu_item_red);
81 g_object_unref (menu_item_green);
82 g_object_unref (menu_item_blue);
83 g_object_unref (menu_item_fullscreen);
84 g_object_unref (menu_item_quit);
85
86 g_menu_append_section (menu, NULL, G_MENU_MODEL (section1));
87 g_menu_append_section (menu, "Color", G_MENU_MODEL (section2));
88 g_menu_append_section (menu, NULL, G_MENU_MODEL (section3));
89 g_menu_append_submenu (menubar, "Menu", G_MENU_MODEL (menu));
90
91 gtk_application_set_menubar (GTK_APPLICATION (app), G_MENU_MODEL (menubar));
92
93 provider = gtk_css_provider_new ();
94 /* Initialize the css data */
95 gtk_css_provider_load_from_data (provider, "label.lb {background-color: red;}", -1);
96 /* Add CSS to the default GdkDisplay. */
97 GdkDisplay *display = gdk_display_get_default ();
98 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider),
99 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
100 g_signal_connect (app, "shutdown", G_CALLBACK (remove_provider), provider);
101 }
102
103 #define APPLICATION_ID "com.github.ToshioCP.menu2"
104
105 int
106 main (int argc, char **argv) {
107 GtkApplication *app;
108 int stat;
109
110 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
111 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
112 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
113
114 stat =g_application_run (G_APPLICATION (app), argc, argv);
115 g_object_unref (app);
116 return stat;
117 }
118
- 6-23:连接到操作的信号处理程序。
- 25-31:当应用程序退出时调用处理程序remove_provider。它将提供程序从显示中移除并释放提供程序。
- 33-51:激活信号处理程序。
- 35-37:创建一个新窗口,并将其分配给win。它的标题和默认大小分别被设置为"menu2"和400x300。
- 39-41:创建新的标签并分配给lb,名称为lb,与CSS中使用的名称相同。这是添加到win作为一个child。
- 43-46:创建切换动作并将其分配给act_fullscreen。它连接到信号处理程序fullscreen_changed。它被添加到窗口中,所以作用域是“win”。操作对应于窗口。因此,如果有两个或更多窗口,则创建两个或更多操作。
- 48:函数gtk_application_window_set_show_menubar向窗口添加一个菜单栏。
- 50:显示窗口。
- 53-101:启动信号处理程序。
- 55-62:创建两个动作act_color和act_quit。这些操作只存在一个,因为启动处理程序被调用了一次。它们连接到它们的处理程序并添加到应用程序中。它们的作用域是“app”。
- 64-89:构建菜单。
- 91:菜单栏添加到应用程序中。
- 93-100:创建css provider,设置数据并添加到默认显示。应用程序上的“shutdown”信号连接到处理程序“remove_provider”。因此,当应用程序退出时,提供程序将从显示中移除并释放。
Compile
将当前目录更改为src/menu。
$ comp menu2
$./a.out
然后,您将看到一个窗口,内容的背景色为红色。您可以将大小更改为最大,然后再更改为原始大小。您可以更改背景颜色为绿色或蓝色。
如果再次运行应用程序,同一个屏幕上会出现另一个窗口。两个窗口的背景颜色相同。因为act_color动作具有“app”作用域,并且CSS应用于由windows共享的默认显示。