十九、Gtk4-Ui file for menu and action entries

Ui file for menu

你可能认为构建菜单真的很麻烦。是的,程序很复杂,需要很多时间来编码。这种情况类似于构建小构建。当我们构建部件时,使用ui文件是避免这种复杂性的好方法。菜单也是如此。

菜单的ui文件有界面和菜单标签。文件以interface标签开始和结束。

<interface>
  <menu id="menubar">
  </menu>
</interface>

menu标签对应于GMenu对象。Id属性定义对象的名称。它将由GtkBuilder引用。

<submenu>
  <attribute name="label">File</attribute>
    <item>
      <attribute name="label">New</attribute>
      <attribute name="action">win.new</attribute>
    </item>
</submenu>

item标签对应GMenu中的一个item,该项目与GMenuItem具有相同的结构。上面的项有一个label属性。它的值是"New"。这个item还有一个action属性,它的值是"win.new"。"win"是前缀,"new"是动作名称。submenu标签同时对应GMenuItem和GMenu。GMenuItem有一个指向GMenu的链接。

上面的ui文件可以描述如下。

<item>
  <attribute name="label">File</attribute>
    <link name="submenu">
      <item>
        <attribute name="label">New</attribute>
        <attribute name="action">win.new</attribute>
      </item>
    </link>
</item>

link标签表示与submenu的链接。同时也表达了子菜单submenu本身。这个文件比之前的ui文件更好地说明了菜单和菜单项之间的关系。但是子菜单标签简单易懂。所以,我们通常更喜欢前一种ui风格。

有关更多信息,请参阅GTK 4 API参考——PopoverMenu。

下面是示例程序menu3的屏幕截图。它位于目录src/menu3中。
在这里插入图片描述

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <interface>
 3   <menu id="menubar">
 4     <submenu>
 5       <attribute name="label">File</attribute>
 6       <section>
 7         <item>
 8           <attribute name="label">New</attribute>
 9           <attribute name="action">app.new</attribute>
10         </item>
11         <item>
12           <attribute name="label">Open</attribute>
13           <attribute name="action">app.open</attribute>
14         </item>
15       </section>
16       <section>
17         <item>
18           <attribute name="label">Save</attribute>
19           <attribute name="action">win.save</attribute>
20         </item>
21         <item>
22           <attribute name="label">Save As…</attribute>
23           <attribute name="action">win.saveas</attribute>
24         </item>
25       </section>
26       <section>
27         <item>
28           <attribute name="label">Close</attribute>
29           <attribute name="action">win.close</attribute>
30         </item>
31       </section>
32       <section>
33         <item>
34           <attribute name="label">Quit</attribute>
35           <attribute name="action">app.quit</attribute>
36         </item>
37       </section>
38     </submenu>
39     <submenu>
40       <attribute name="label">Edit</attribute>
41       <section>
42         <item>
43           <attribute name="label">Cut</attribute>
44           <attribute name="action">app.cut</attribute>
45         </item>
46         <item>
47           <attribute name="label">Copy</attribute>
48           <attribute name="action">app.copy</attribute>
49         </item>
50         <item>
51           <attribute name="label">Paste</attribute>
52           <attribute name="action">app.paste</attribute>
53         </item>
54       </section>
55       <section>
56         <item>
57           <attribute name="label">Select All</attribute>
58           <attribute name="action">app.selectall</attribute>
59         </item>
60       </section>
61     </submenu>
62     <submenu>
63       <attribute name="label">View</attribute>
64       <section>
65         <item>
66           <attribute name="label">Full Screen</attribute>
67           <attribute name="action">win.fullscreen</attribute>
68         </item>
69       </section>
70     </submenu>
71   </menu>
72 </interface>

资源编译器glib-compile- resources把xml格式ui文件转换为资源resource。

1 <?xml version="1.0" encoding="UTF-8"?>
2 <gresources>
3   <gresource prefix="/com/github/ToshioCP/menu3">
4     <file>menu3.ui</file>
5   </gresource>
6 </gresources>

GtkBuilder从资源中构建菜单。

GtkBuilder *builder = gtk_builder_new_from_resource ("/com/github/ToshioCP/menu3/menu3.ui");
GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, "menubar"));

gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
g_object_unref (builder);

在将GMenuModel菜单栏插入应用程序之后,构建器实例就会被释放。如果你在插入之前这样做,不好的事情会发生——你的电脑可能会死机。

Action entry

构建动作和信号处理程序的编写也是很麻烦的工作。因此,它应该是自动化的。你可以使用GActionEntry结构和g_action_map_add_action_entries函数轻松实现它们。

GActionEntry包含动作名称、信号处理程序、参数和状态。

typedef struct _GActionEntry GActionEntry;

struct _GActionEntry
{
  /* action name */
  const char *name;
  /* activate handler */
  void (* activate) (GSimpleAction *action, GVariant *parameter, gpointer user_data);
  /* the type of the parameter given as a single GVariant type string */
  const char *parameter_type;
  /* initial state given in GVariant text format */
  const char *state;
  /* change-state handler */
  void (* change_state) (GSimpleAction *action, GVariant *value, gpointer user_data);
  /*< private >*/
  gsize padding[3];
};

例如,上一节中的操作如下:

{ "fullscreen", NULL, NULL, "false", fullscreen_changed }
{ "color", color_activated, "s", "'red'", NULL }
{ "quit", quit_activated, NULL, NULL, NULL },
  • 全屏操作是有状态的,但没有参数。因此,第三个元素(参数类型)是NULL。GVariant文本格式提供“true”和“false”作为布尔值。操作的初始状态为false(第四个元素)。它没有activate处理程序,所以第二个元素是NULL。相反,它有change-state处理程序。fullscreen_changed的第五个元素是处理程序。
  • 颜色动作是有状态的,并且有一个参数。参数类型为string。GVariant格式字符串提供了表示GVariant类型的字符串格式。第三个元素“s”表示GVariant字符串类型。GVariant文本格式定义了字符串用单引号或双引号括起来。字符串red是’red’或"red"。第四个元素是"‘red’“,这是一个C字符串格式,字符串是’red’。你可以写成”“red”"。第二个元素color_activated是activate处理程序。这个动作没有change-state处理程序,因此第5个元素是NULL。
  • 退出操作是无状态的,没有参数。因此,第三和第四个元素为NULL。第二个成员quit_activated是activate处理程序。这个动作没有change-state处理程序,因此第5个元素是NULL。

函数g_action_map_add_action_entries负责创建GSimpleAction实例并将它们添加到GActionMap(一个应用程序或窗口)。

const GActionEntry app_entries[] = {
  { "color", color_activated, "s", "'red'", NULL },
  { "quit", quit_activated, NULL, NULL, NULL }
};
g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries,
                                 G_N_ELEMENTS (app_entries), app);

上面的代码做了:

  • 创建"color"和"quit"操作
  • 连接动作和“activate”信号处理程序(color_activated和quit_activated)。
  • 将操作添加到操作映射应用程序。

另一个动作也是如此。

const GActionEntry win_entries[] = {
  { "fullscreen", NULL, NULL, "false", fullscreen_changed }
};
g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries,
                                 G_N_ELEMENTS (win_entries), win);

上面的代码做了:

  • 构建“全屏”操作。
  • 连接动作和信号处理程序fullscreen_changed
  • 它的初始状态设置为false。
  • 将action添加到action map win。

Example

源文件是menu3.c、menu3.ui、menu3.gresource.xml和meson.build。它们位于目录src/menu3中。下面是menu3.c和meson.build。

  1 #include <gtk/gtk.h>
  2 
  3 static void
  4 new_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
  5 }
  6 
  7 static void
  8 open_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
  9 }
 10 
 11 static void
 12 save_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 13 }
 14 
 15 static void
 16 saveas_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 17 }
 18 
 19 static void
 20 close_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 21   GtkWindow *win = GTK_WINDOW (user_data);
 22 
 23   gtk_window_destroy (win);
 24 }
 25 
 26 static void
 27 cut_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 28 }
 29 
 30 static void
 31 copy_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 32 }
 33 
 34 static void
 35 paste_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 36 }
 37 
 38 static void
 39 selectall_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
 40 }
 41 
 42 static void
 43 fullscreen_changed (GSimpleAction *action, GVariant *state, gpointer user_data) {
 44   GtkWindow *win = GTK_WINDOW (user_data);
 45 
 46   if (g_variant_get_boolean (state))
 47     gtk_window_maximize (win);
 48   else
 49     gtk_window_unmaximize (win);
 50   g_simple_action_set_state (action, state);
 51 }
 52 
 53 static void
 54 quit_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data)
 55 {
 56   GApplication *app = G_APPLICATION (user_data);
 57 
 58   g_application_quit (app);
 59 }
 60 
 61 static void
 62 app_activate (GApplication *app) {
 63   GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));
 64 
 65   const GActionEntry win_entries[] = {
 66     { "save", save_activated, NULL, NULL, NULL },
 67     { "saveas", saveas_activated, NULL, NULL, NULL },
 68     { "close", close_activated, NULL, NULL, NULL },
 69     { "fullscreen", NULL, NULL, "false", fullscreen_changed }
 70   };
 71   g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries, G_N_ELEMENTS (win_entries), win);
 72 
 73   gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE);
 74 
 75   gtk_window_set_title (GTK_WINDOW (win), "menu3");
 76   gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
 77   gtk_widget_show (win);
 78 }
 79 
 80 static void
 81 app_startup (GApplication *app) {
 82   GtkBuilder *builder = gtk_builder_new_from_resource ("/com/github/ToshioCP/menu3/menu3.ui");
 83   GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, "menubar"));
 84 
 85   gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
 86   g_object_unref (builder);
 87 
 88   const GActionEntry app_entries[] = {
 89     { "new", new_activated, NULL, NULL, NULL },
 90     { "open", open_activated, NULL, NULL, NULL },
 91     { "cut", cut_activated, NULL, NULL, NULL },
 92     { "copy", copy_activated, NULL, NULL, NULL },
 93     { "paste", paste_activated, NULL, NULL, NULL },
 94     { "selectall", selectall_activated, NULL, NULL, NULL },
 95     { "quit", quit_activated, NULL, NULL, NULL }
 96   };
 97   g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries, G_N_ELEMENTS (app_entries), app);
 98 }
 99 
100 #define APPLICATION_ID "com.github.ToshioCP.menu3"
101 
102 int
103 main (int argc, char **argv) {
104   GtkApplication *app;
105   int stat;
106 
107   app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
108   g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
109   g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
110 
111   stat =g_application_run (G_APPLICATION (app), argc, argv);
112   g_object_unref (app);
113   return stat;
114 }
115 

meson.build

 1 project('menu3', 'c')
 2 
 3 gtkdep = dependency('gtk4')
 4 
 5 gnome=import('gnome')
 6 resources = gnome.compile_resources('resources','menu3.gresource.xml')
 7 
 8 sourcefiles=files('menu3.c')
 9 
10 executable('menu3', sourcefiles, resources, dependencies: gtkdep)

动作处理程序需要遵循以下格式。

static void
handler (GSimpleAction *action_name, GVariant *parameter, gpointer user_data) { ... ... ... }

例如,你不能写“GApplication *app”而不是“gpointer user_data”。因为g_action_map_add_action_entries期望处理程序遵循上述格式。

有menu2_ui.c和menu2.ui在菜单目录下。它们是显示菜单ui文件和g_action_map_add_action_entries的其他例子。它包括一个带参数的有状态动作。

<item>
  <attribute name="label">Red</attribute>
  <attribute name="action">app.color</attribute>
  <attribute name="target">red</attribute>
</item>

动作名称和目标像这样分开。动作属性只包括前缀和名称。你不能这样写<attribute name="action">app.color::red</attribute>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值