2023-01-03 07:30:06 +01:00
|
|
|
Up: [README.md](../README.md), Prev: [Section 18](sec18.md), Next: [Section 20](sec20.md)
|
2021-01-22 14:14:05 +01:00
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
# Ui file for menu and action entries
|
2021-01-22 14:14:05 +01:00
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
## Ui file for menu
|
2021-01-22 14:14:05 +01:00
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
You may have thought that building menus was really bothersome.
|
|
|
|
Yes, the program was complicated and it needs lots of time to code them.
|
2021-06-15 04:24:42 +02:00
|
|
|
The situation is similar to building widgets.
|
2023-01-03 07:30:06 +01:00
|
|
|
When we built widgets, using ui file was a good way to avoid such complication.
|
2021-06-15 04:24:42 +02:00
|
|
|
The same goes for menus.
|
2021-01-22 14:14:05 +01:00
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
The ui file for menus has interface and menu tags.
|
|
|
|
The file starts and ends with interface tags.
|
2021-01-22 14:14:05 +01:00
|
|
|
|
2021-02-17 15:59:50 +01:00
|
|
|
~~~xml
|
2021-06-15 04:24:42 +02:00
|
|
|
<interface>
|
|
|
|
<menu id="menubar">
|
|
|
|
</menu>
|
|
|
|
</interface>
|
2021-02-17 15:59:50 +01:00
|
|
|
~~~
|
2021-01-22 14:14:05 +01:00
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
`menu` tag corresponds to GMenu object.
|
|
|
|
`id` attribute defines the name of the object.
|
|
|
|
It will be referred by GtkBuilder.
|
2021-01-22 14:14:05 +01:00
|
|
|
|
2021-02-17 15:59:50 +01:00
|
|
|
~~~xml
|
2021-06-15 04:24:42 +02:00
|
|
|
<submenu>
|
|
|
|
<attribute name="label">File</attribute>
|
|
|
|
<item>
|
|
|
|
<attribute name="label">New</attribute>
|
|
|
|
<attribute name="action">win.new</attribute>
|
|
|
|
</item>
|
|
|
|
</submenu>
|
2021-02-17 15:59:50 +01:00
|
|
|
~~~
|
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
`item` tag corresponds to an item in the GMenu which has the same structure as GMenuItem.
|
2021-06-15 04:24:42 +02:00
|
|
|
The item above has a label attribute.
|
|
|
|
Its value is "New".
|
|
|
|
The item also has an action attribute and its value is "win.new".
|
|
|
|
"win" is a prefix and "new" is an action name.
|
|
|
|
`submenu` tag corresponds to both GMenuItem and GMenu.
|
|
|
|
The GMenuItem has a link to GMenu.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
The ui file above can be described as follows.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
|
|
|
~~~xml
|
2021-06-15 04:24:42 +02:00
|
|
|
<item>
|
|
|
|
<attribute name="label">File</attribute>
|
|
|
|
<link name="submenu">
|
|
|
|
<item>
|
|
|
|
<attribute name="label">New</attribute>
|
|
|
|
<attribute name="action">win.new</attribute>
|
|
|
|
</item>
|
|
|
|
</link>
|
|
|
|
</item>
|
2021-02-17 15:59:50 +01:00
|
|
|
~~~
|
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
`link` tag expresses the link to submenu.
|
|
|
|
And at the same time it also expresses the submenu itself.
|
|
|
|
This file illustrates the relationship between the menus and items better than the prior ui file.
|
|
|
|
But `submenu` tag is simple and easy to understand.
|
2023-01-03 07:30:06 +01:00
|
|
|
So, we usually prefer the former ui style.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
For further information, see [GTK 4 API reference -- PopoverMenu](https://docs.gtk.org/gtk4/class.PopoverMenu.html#menu-models).
|
|
|
|
|
|
|
|
The following is a screenshot of the sample program `menu3`.
|
|
|
|
It is located in the directory [src/menu3](../src/menu3).
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
![menu3](../image/menu3.png)
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
The following is the ui file for `menu3`.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
|
|
|
~~~xml
|
2021-06-15 04:24:42 +02:00
|
|
|
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>
|
2023-01-03 07:30:06 +01:00
|
|
|
9 <attribute name="action">app.new</attribute>
|
2021-06-15 04:24:42 +02:00
|
|
|
10 </item>
|
|
|
|
11 <item>
|
|
|
|
12 <attribute name="label">Open</attribute>
|
2023-01-03 07:30:06 +01:00
|
|
|
13 <attribute name="action">app.open</attribute>
|
2021-06-15 04:24:42 +02:00
|
|
|
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>
|
2023-01-03 07:30:06 +01:00
|
|
|
44 <attribute name="action">app.cut</attribute>
|
2021-06-15 04:24:42 +02:00
|
|
|
45 </item>
|
|
|
|
46 <item>
|
|
|
|
47 <attribute name="label">Copy</attribute>
|
2023-01-03 07:30:06 +01:00
|
|
|
48 <attribute name="action">app.copy</attribute>
|
2021-06-15 04:24:42 +02:00
|
|
|
49 </item>
|
|
|
|
50 <item>
|
|
|
|
51 <attribute name="label">Paste</attribute>
|
2023-01-03 07:30:06 +01:00
|
|
|
52 <attribute name="action">app.paste</attribute>
|
2021-06-15 04:24:42 +02:00
|
|
|
53 </item>
|
|
|
|
54 </section>
|
|
|
|
55 <section>
|
|
|
|
56 <item>
|
|
|
|
57 <attribute name="label">Select All</attribute>
|
2023-01-03 07:30:06 +01:00
|
|
|
58 <attribute name="action">app.selectall</attribute>
|
2021-06-15 04:24:42 +02:00
|
|
|
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>
|
|
|
|
~~~
|
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
The ui file is converted to the resource by the resource compiler `glib-compile-resouces` with xml file.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
~~~xml
|
|
|
|
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>
|
2021-02-17 15:59:50 +01:00
|
|
|
~~~
|
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
GtkBuilder builds menus from the resource.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
|
|
|
~~~C
|
2021-06-15 04:24:42 +02:00
|
|
|
GtkBuilder *builder = gtk_builder_new_from_resource ("/com/github/ToshioCP/menu3/menu3.ui");
|
|
|
|
GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, "menubar"));
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
|
|
|
|
g_object_unref (builder);
|
2021-02-17 15:59:50 +01:00
|
|
|
~~~
|
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
The builder instance is freed after the GMenuModel `menubar` is inserted to the application.
|
|
|
|
If you do it before the insertion, bad thing will happen -- your computer might freeze.
|
2023-07-15 10:27:53 +02:00
|
|
|
It is because you don't own the `menubar` instance.
|
|
|
|
The function `gtk_builder_get_object` just returns the pointer to `menubar` and doesn't increase the reference count of `menubar`.
|
|
|
|
So, if you released `bulder` before `gtk_application_set_menubar`, `builder` would be destroyed and `menubar` as well.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
## Action entry
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
The coding for building actions and signal handlers is bothersome work as well.
|
|
|
|
Therefore, it should be automated.
|
|
|
|
You can implement them easily with GActionEntry structure and `g_action_map_add_action_entries` function.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
GActionEntry contains action name, signal handlers, parameter and state.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
|
|
|
~~~C
|
2021-06-15 04:24:42 +02:00
|
|
|
typedef struct _GActionEntry GActionEntry;
|
|
|
|
|
|
|
|
struct _GActionEntry
|
|
|
|
{
|
|
|
|
/* action name */
|
2023-01-03 07:30:06 +01:00
|
|
|
const char *name;
|
2021-06-15 04:24:42 +02:00
|
|
|
/* activate handler */
|
|
|
|
void (* activate) (GSimpleAction *action, GVariant *parameter, gpointer user_data);
|
|
|
|
/* the type of the parameter given as a single GVariant type string */
|
2023-01-03 07:30:06 +01:00
|
|
|
const char *parameter_type;
|
2021-06-15 04:24:42 +02:00
|
|
|
/* initial state given in GVariant text format */
|
2023-01-03 07:30:06 +01:00
|
|
|
const char *state;
|
2021-06-15 04:24:42 +02:00
|
|
|
/* change-state handler */
|
|
|
|
void (* change_state) (GSimpleAction *action, GVariant *value, gpointer user_data);
|
|
|
|
/*< private >*/
|
|
|
|
gsize padding[3];
|
|
|
|
};
|
|
|
|
~~~
|
|
|
|
For example, the actions in the previous section are:
|
2021-02-17 15:59:50 +01:00
|
|
|
|
|
|
|
~~~C
|
2021-06-15 04:24:42 +02:00
|
|
|
{ "fullscreen", NULL, NULL, "false", fullscreen_changed }
|
2023-01-03 07:30:06 +01:00
|
|
|
{ "color", color_activated, "s", "'red'", NULL }
|
2021-06-15 04:24:42 +02:00
|
|
|
{ "quit", quit_activated, NULL, NULL, NULL },
|
2021-02-17 15:59:50 +01:00
|
|
|
~~~
|
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
- Fullscreen action is stateful, but doesn't have parameters.
|
|
|
|
So, the third element (parameter type) is NULL.
|
|
|
|
[GVariant text format](https://docs.gtk.org/glib/gvariant-text.html) provides "true" and "false" as boolean GVariant values.
|
|
|
|
The initial state of the action is false (the fourth element).
|
|
|
|
It doesn't have activate handler, so the second element is NULL.
|
|
|
|
Instead, it has change-state handler.
|
|
|
|
The fifth element `fullscreen_changed` is the handler.
|
|
|
|
- Color action is stateful and has a parameter.
|
|
|
|
The parameter type is string.
|
|
|
|
[GVariant format strings](https://docs.gtk.org/glib/gvariant-format-strings.html) provides string formats to represent GVariant types.
|
|
|
|
The third element "s" means GVariant string type.
|
|
|
|
GVariant text format defines that strings are surrounded by single or double quotes.
|
|
|
|
So, the string red is 'red' or "red".
|
|
|
|
The fourth element is `"'red'"`, which is a C string format and the string is 'red'.
|
|
|
|
You can write `"\"red\""` instead.
|
2023-07-15 10:27:53 +02:00
|
|
|
The second element `color_activated` is the activate handler.
|
2023-01-03 07:30:06 +01:00
|
|
|
The action doesn't have change-state handler, so the fifth element is NULL.
|
|
|
|
- Quit action is non-stateful and has no parameter.
|
|
|
|
So, the third and fourth elements are NULL.
|
2023-07-15 10:27:53 +02:00
|
|
|
The second element `quit_activated` is the activate handler.
|
2023-01-03 07:30:06 +01:00
|
|
|
The action doesn't have change-state handler, so the fifth element is NULL.
|
|
|
|
|
|
|
|
The function `g_action_map_add_action_entries` does everything
|
|
|
|
to create GSimpleAction instances and add them to a GActionMap (an application or window).
|
2021-02-17 15:59:50 +01:00
|
|
|
|
|
|
|
~~~C
|
2021-06-15 04:24:42 +02:00
|
|
|
const GActionEntry app_entries[] = {
|
2023-01-03 07:30:06 +01:00
|
|
|
{ "color", color_activated, "s", "'red'", NULL },
|
2021-06-15 04:24:42 +02:00
|
|
|
{ "quit", quit_activated, NULL, NULL, NULL }
|
|
|
|
};
|
|
|
|
g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries,
|
|
|
|
G_N_ELEMENTS (app_entries), app);
|
2021-02-17 15:59:50 +01:00
|
|
|
~~~
|
|
|
|
|
2021-06-15 04:24:42 +02:00
|
|
|
The code above does:
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
- Builds the "color" and "quit" actions
|
2023-07-15 10:27:53 +02:00
|
|
|
- Connects the action and the "activate" signal handlers (`color_activated` and `quit_activated`).
|
2023-01-03 07:30:06 +01:00
|
|
|
- Adds the actions to the action map `app`.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
The same goes for the other action.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
|
|
|
~~~C
|
2021-06-15 04:24:42 +02:00
|
|
|
const GActionEntry win_entries[] = {
|
2023-01-03 07:30:06 +01:00
|
|
|
{ "fullscreen", NULL, NULL, "false", fullscreen_changed }
|
2021-06-15 04:24:42 +02:00
|
|
|
};
|
|
|
|
g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries,
|
|
|
|
G_N_ELEMENTS (win_entries), win);
|
2021-02-17 15:59:50 +01:00
|
|
|
~~~
|
2021-06-15 04:24:42 +02:00
|
|
|
The code above does:
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
- Builds the "fullscreen" action.
|
|
|
|
- Connects the action and the signal handler `fullscreen_changed`
|
|
|
|
- Its initial state is set to false.
|
|
|
|
- Adds the action to the action map `win`.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
## Example
|
2021-02-17 15:59:50 +01:00
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
Source files are `menu3.c`, `menu3.ui`, `menu3.gresource.xml` and `meson.build`.
|
|
|
|
They are in the directory [src/menu3](../src/menu3).
|
|
|
|
The following are `menu3.c` and `meson.build`.
|
2021-02-17 15:59:50 +01:00
|
|
|
|
|
|
|
~~~C
|
2021-06-15 04:24:42 +02:00
|
|
|
1 #include <gtk/gtk.h>
|
|
|
|
2
|
|
|
|
3 static void
|
2023-01-03 07:30:06 +01:00
|
|
|
4 new_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
2021-06-15 04:24:42 +02:00
|
|
|
5 }
|
|
|
|
6
|
|
|
|
7 static void
|
2023-01-03 07:30:06 +01:00
|
|
|
8 open_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
2021-06-15 04:24:42 +02:00
|
|
|
9 }
|
|
|
|
10
|
|
|
|
11 static void
|
2023-01-03 07:30:06 +01:00
|
|
|
12 save_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
2021-06-15 04:24:42 +02:00
|
|
|
13 }
|
|
|
|
14
|
|
|
|
15 static void
|
2023-01-03 07:30:06 +01:00
|
|
|
16 saveas_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
2021-06-15 04:24:42 +02:00
|
|
|
17 }
|
|
|
|
18
|
|
|
|
19 static void
|
2023-01-03 07:30:06 +01:00
|
|
|
20 close_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
|
|
|
21 GtkWindow *win = GTK_WINDOW (user_data);
|
2021-06-15 04:24:42 +02:00
|
|
|
22
|
2023-01-03 07:30:06 +01:00
|
|
|
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
|
2023-07-15 10:27:53 +02:00
|
|
|
54 quit_activated (GSimpleAction *action, GVariant *parameter, gpointer user_data) {
|
|
|
|
55 GApplication *app = G_APPLICATION (user_data);
|
|
|
|
56
|
|
|
|
57 g_application_quit (app);
|
|
|
|
58 }
|
|
|
|
59
|
|
|
|
60 static void
|
|
|
|
61 app_activate (GApplication *app) {
|
|
|
|
62 GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));
|
|
|
|
63
|
|
|
|
64 const GActionEntry win_entries[] = {
|
|
|
|
65 { "save", save_activated, NULL, NULL, NULL },
|
|
|
|
66 { "saveas", saveas_activated, NULL, NULL, NULL },
|
|
|
|
67 { "close", close_activated, NULL, NULL, NULL },
|
|
|
|
68 { "fullscreen", NULL, NULL, "false", fullscreen_changed }
|
|
|
|
69 };
|
|
|
|
70 g_action_map_add_action_entries (G_ACTION_MAP (win), win_entries, G_N_ELEMENTS (win_entries), win);
|
|
|
|
71
|
|
|
|
72 gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE);
|
|
|
|
73
|
|
|
|
74 gtk_window_set_title (GTK_WINDOW (win), "menu3");
|
|
|
|
75 gtk_window_set_default_size (GTK_WINDOW (win), 400, 300);
|
|
|
|
76 gtk_window_present (GTK_WINDOW (win));
|
|
|
|
77 }
|
|
|
|
78
|
|
|
|
79 static void
|
|
|
|
80 app_startup (GApplication *app) {
|
|
|
|
81 GtkBuilder *builder = gtk_builder_new_from_resource ("/com/github/ToshioCP/menu3/menu3.ui");
|
|
|
|
82 GMenuModel *menubar = G_MENU_MODEL (gtk_builder_get_object (builder, "menubar"));
|
|
|
|
83
|
|
|
|
84 gtk_application_set_menubar (GTK_APPLICATION (app), menubar);
|
|
|
|
85 g_object_unref (builder);
|
|
|
|
86
|
|
|
|
87 const GActionEntry app_entries[] = {
|
|
|
|
88 { "new", new_activated, NULL, NULL, NULL },
|
|
|
|
89 { "open", open_activated, NULL, NULL, NULL },
|
|
|
|
90 { "cut", cut_activated, NULL, NULL, NULL },
|
|
|
|
91 { "copy", copy_activated, NULL, NULL, NULL },
|
|
|
|
92 { "paste", paste_activated, NULL, NULL, NULL },
|
|
|
|
93 { "selectall", selectall_activated, NULL, NULL, NULL },
|
|
|
|
94 { "quit", quit_activated, NULL, NULL, NULL }
|
|
|
|
95 };
|
|
|
|
96 g_action_map_add_action_entries (G_ACTION_MAP (app), app_entries, G_N_ELEMENTS (app_entries), app);
|
|
|
|
97 }
|
|
|
|
98
|
|
|
|
99 #define APPLICATION_ID "com.github.ToshioCP.menu3"
|
|
|
|
100
|
|
|
|
101 int
|
|
|
|
102 main (int argc, char **argv) {
|
|
|
|
103 GtkApplication *app;
|
|
|
|
104 int stat;
|
|
|
|
105
|
|
|
|
106 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
|
|
|
|
107 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
|
|
|
|
108 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
|
|
|
|
109
|
|
|
|
110 stat =g_application_run (G_APPLICATION (app), argc, argv);
|
|
|
|
111 g_object_unref (app);
|
|
|
|
112 return stat;
|
|
|
|
113 }
|
|
|
|
114
|
2021-06-15 04:24:42 +02:00
|
|
|
~~~
|
|
|
|
|
|
|
|
meson.build
|
2021-02-17 15:59:50 +01:00
|
|
|
|
|
|
|
~~~meson
|
2021-06-15 04:24:42 +02:00
|
|
|
1 project('menu3', 'c')
|
2021-02-17 15:59:50 +01:00
|
|
|
2
|
|
|
|
3 gtkdep = dependency('gtk4')
|
|
|
|
4
|
|
|
|
5 gnome=import('gnome')
|
2021-06-15 04:24:42 +02:00
|
|
|
6 resources = gnome.compile_resources('resources','menu3.gresource.xml')
|
|
|
|
7
|
|
|
|
8 sourcefiles=files('menu3.c')
|
|
|
|
9
|
|
|
|
10 executable('menu3', sourcefiles, resources, dependencies: gtkdep)
|
2021-02-17 15:59:50 +01:00
|
|
|
~~~
|
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
Action handlers need to follow the following format.
|
|
|
|
|
|
|
|
~~~C
|
|
|
|
static void
|
|
|
|
handler (GSimpleAction *action_name, GVariant *parameter, gpointer user_data) { ... ... ... }
|
|
|
|
~~~
|
|
|
|
|
|
|
|
You can't write, for example, "GApplication *app" instead of "gpointer user_data".
|
|
|
|
Because `g_action_map_add_action_entries` expects that handlers follow the format above.
|
|
|
|
|
|
|
|
There are `menu2_ui.c` and `menu2.ui` under the `menu` directory.
|
|
|
|
They are other examples to show menu ui file and `g_action_map_add_action_entries`.
|
|
|
|
It includes a stateful action with parameters.
|
|
|
|
|
|
|
|
~~~xml
|
|
|
|
<item>
|
|
|
|
<attribute name="label">Red</attribute>
|
|
|
|
<attribute name="action">app.color</attribute>
|
|
|
|
<attribute name="target">red</attribute>
|
|
|
|
</item>
|
|
|
|
~~~
|
|
|
|
|
|
|
|
Action name and target are separated like this.
|
|
|
|
Action attribute includes prefix and name only.
|
|
|
|
You can't write like `<attribute name="action">app.color::red</attribute>`.
|
2021-01-22 14:14:05 +01:00
|
|
|
|
2023-01-03 07:30:06 +01:00
|
|
|
Up: [README.md](../README.md), Prev: [Section 18](sec18.md), Next: [Section 20](sec20.md)
|