Gtk4-tutorial/docs/sec17.html

256 lines
20 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>Gtk4 tutorial</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
pre{overflow: visible;}
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::after
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
div.sourceCode { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
pre:not(.sourceCode) { margin: 10px; padding: 16px 10px 8px 10px; border: 2px solid silver; background-color: ghostwhite; overflow-x:scroll}
table {margin-left: auto; margin-right: auto; border-collapse: collapse; border: 1px solid;}
th {padding: 2px 6px; border: 1px solid; background-color: ghostwhite;}
td {padding: 2px 6px; border: 1px solid;}
img {display: block; margin-left: auto; margin-right: auto;}
figcaption {text-align: center;}
</style>
</head>
<body style="padding-top: 70px;">
<div class="container">
<nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-primary">
<div class="container-fluid">
<span class="navbar-brand">Gtk4 tutorial</span>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="index.html">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="sec16.html">Prev: section16</a>
</li>
<li class="nav-item">
<a class="nav-link" href="sec18.html">Next: section18</a>
</li>
</ul>
</div>
</div>
</nav>
<h1 id="menu-and-action">Menu and action</h1>
<h2 id="menu">Menu</h2>
<p>Users often use menus to tell a command to the computer. It is like this:</p>
<figure>
<img src="image/menu.png" alt="" /><figcaption>Menu</figcaption>
</figure>
<p>Now lets analyze the menu above. There are two types of object.</p>
<ul>
<li>“File”, “Edit”, “View”, “Cut”, “Copy”, “Paste” and “Select All”. They are called “menu item” or simply “item”. When the user clicks one of these items, then something will happen.</li>
<li>Menubar, submenu referenced by “Edit” item and two sections. They are called “menu”. Menu is an ordered list of items. They are similar to arrays.</li>
</ul>
<figure>
<img src="image/menu_structure.png" alt="" /><figcaption>Menu structure</figcaption>
</figure>
<ul>
<li>Menubar is a menu which has three items, which are “File”, “Edit” and “View”.</li>
<li>The menu item labeled “Edit” has a link to the submenu which has two items. These two items dont have labels. Each item refers to a section.</li>
<li>The first section is a menu which has three items “Cut”, “Copy” and “Paste”.</li>
<li>The second section is a menu which has one item “Select All”.</li>
</ul>
<p>Menus can build a complicated structure thanks to the links of menu items.</p>
<h2 id="gmenumodel-gmenu-and-gmenuitem">GMenuModel, GMenu and GMenuItem</h2>
<p>GMenuModel is an abstract object which represents a menu. GMenu is a simple implementation of GMenuModel and a child object of GMenuModel.</p>
<pre><code>GObject -- GMenuModel -- GMenu</code></pre>
<p>Because GMenuModel is an abstract object, it isnt instantiatable. Therefore, it doesnt have any functions to create its instance. If you want to create a menu, use <code>g_menu_new</code> to create a GMenu instance. GMenu inherits all the functions of GMenuModel because of the child object.</p>
<p>GMenuItem is an object directly derived from GObject. GMenuItem and Gmenu (or GMenuModel) dont have a parent-child relationship.</p>
<pre><code>GObject -- GMenuModel -- GMenu
GObject -- GMenuItem</code></pre>
<p>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.</p>
<p>Some menu items have a link to another GMenu. There are two types of links, submenu and section.</p>
<p>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.</p>
<p>The following code shows how to append GMenuItem to GMenu.</p>
<pre><code>GMenu *menu = g_menu_new ();
GMenuItem *menu_item_quit = g_menu_item_new (&quot;Quit&quot;, &quot;app.quit&quot;);
g_menu_append_item (menu, menu_item_quit);
g_object_unref (menu_item_quit);</code></pre>
<h2 id="menu-and-action-1">Menu and action</h2>
<p>One of the attributes of menu items is an action. This attribute points an action object.</p>
<p>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.</p>
<p>An action corresponds to a menu item will be activated when the menu item is clicked. Then the action emits an activate signal.</p>
<ol type="1">
<li>menu item is clicked.</li>
<li>The corresponding action is activated.</li>
<li>The action emits a signal.</li>
<li>The connected handler is invoked.</li>
</ol>
<p>The following code is an example.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true"></a>quit_activated(GSimpleAction *action, GVariant *parameter, gpointer app) { ... ... ...}</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true"></a>GSimpleAction *act_quit = g_simple_action_new (<span class="st">&quot;quit&quot;</span>, NULL);</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true"></a>g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_quit));</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true"></a>g_signal_connect (act_quit, <span class="st">&quot;activate&quot;</span>, G_CALLBACK (quit_activated), app);</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true"></a>GMenuItem *menu_item_quit = g_menu_item_new (<span class="st">&quot;Quit&quot;</span>, <span class="st">&quot;app.quit&quot;</span>);</span></code></pre></div>
<ol type="1">
<li><code>menu_item_quit</code> 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.</li>
<li><code>act_quit</code> is an action. It has a name “quit”. The function <code>g_simple_action_new</code> creates a stateless action. So, <code>act_quit</code> is stateless. The meaning of stateless will be explained later. The argument <code>NULL</code> means that the action doesnt have an parameter. Most of the actions are stateless and have no parameter.</li>
<li>The action <code>act_quit</code> is added to the GtkApplication instance with <code>g_action_map_add_action</code>. When <code>act_quit</code> is activated, it will emit “activate” signal.</li>
<li>“activate” signal of the action is connected to the handler <code>quit_activated</code>. So, if the action is activated, the handler will be invoked.</li>
</ol>
<h2 id="simple-example">Simple example</h2>
<p>The following is a simple example of menus and actions.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb5-2"><a href="#cb5-2"></a></span>
<span id="cb5-3"><a href="#cb5-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-4"><a href="#cb5-4"></a>quit_activated(GSimpleAction *action, GVariant *parameter, gpointer user_data) {</span>
<span id="cb5-5"><a href="#cb5-5"></a> GApplication *app = G_APPLICATION (user_data);</span>
<span id="cb5-6"><a href="#cb5-6"></a></span>
<span id="cb5-7"><a href="#cb5-7"></a> g_application_quit (app);</span>
<span id="cb5-8"><a href="#cb5-8"></a>}</span>
<span id="cb5-9"><a href="#cb5-9"></a></span>
<span id="cb5-10"><a href="#cb5-10"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-11"><a href="#cb5-11"></a>app_activate (GApplication *app, gpointer user_data) {</span>
<span id="cb5-12"><a href="#cb5-12"></a> GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));</span>
<span id="cb5-13"><a href="#cb5-13"></a> gtk_window_set_title (GTK_WINDOW (win), <span class="st">&quot;menu1&quot;</span>);</span>
<span id="cb5-14"><a href="#cb5-14"></a> gtk_window_set_default_size (GTK_WINDOW (win), <span class="dv">400</span>, <span class="dv">300</span>);</span>
<span id="cb5-15"><a href="#cb5-15"></a></span>
<span id="cb5-16"><a href="#cb5-16"></a> GSimpleAction *act_quit = g_simple_action_new (<span class="st">&quot;quit&quot;</span>, NULL);</span>
<span id="cb5-17"><a href="#cb5-17"></a> g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (act_quit));</span>
<span id="cb5-18"><a href="#cb5-18"></a> g_signal_connect (act_quit, <span class="st">&quot;activate&quot;</span>, G_CALLBACK (quit_activated), app);</span>
<span id="cb5-19"><a href="#cb5-19"></a></span>
<span id="cb5-20"><a href="#cb5-20"></a> GMenu *menubar = g_menu_new ();</span>
<span id="cb5-21"><a href="#cb5-21"></a> GMenuItem *menu_item_menu = g_menu_item_new (<span class="st">&quot;Menu&quot;</span>, NULL);</span>
<span id="cb5-22"><a href="#cb5-22"></a> GMenu *menu = g_menu_new ();</span>
<span id="cb5-23"><a href="#cb5-23"></a> GMenuItem *menu_item_quit = g_menu_item_new (<span class="st">&quot;Quit&quot;</span>, <span class="st">&quot;app.quit&quot;</span>);</span>
<span id="cb5-24"><a href="#cb5-24"></a> g_menu_append_item (menu, menu_item_quit);</span>
<span id="cb5-25"><a href="#cb5-25"></a> g_object_unref (menu_item_quit);</span>
<span id="cb5-26"><a href="#cb5-26"></a> g_menu_item_set_submenu (menu_item_menu, G_MENU_MODEL (menu));</span>
<span id="cb5-27"><a href="#cb5-27"></a> g_menu_append_item (menubar, menu_item_menu);</span>
<span id="cb5-28"><a href="#cb5-28"></a> g_object_unref (menu_item_menu);</span>
<span id="cb5-29"><a href="#cb5-29"></a></span>
<span id="cb5-30"><a href="#cb5-30"></a> gtk_application_set_menubar (GTK_APPLICATION (app), G_MENU_MODEL (menubar));</span>
<span id="cb5-31"><a href="#cb5-31"></a> gtk_application_window_set_show_menubar (GTK_APPLICATION_WINDOW (win), TRUE);</span>
<span id="cb5-32"><a href="#cb5-32"></a> gtk_window_present (GTK_WINDOW (win));</span>
<span id="cb5-33"><a href="#cb5-33"></a><span class="co">/* gtk_widget_show (win); is also OKay instead of gtk_window_present. */</span></span>
<span id="cb5-34"><a href="#cb5-34"></a>}</span>
<span id="cb5-35"><a href="#cb5-35"></a></span>
<span id="cb5-36"><a href="#cb5-36"></a><span class="pp">#define APPLICATION_ID &quot;com.github.ToshioCP.menu1&quot;</span></span>
<span id="cb5-37"><a href="#cb5-37"></a></span>
<span id="cb5-38"><a href="#cb5-38"></a><span class="dt">int</span></span>
<span id="cb5-39"><a href="#cb5-39"></a>main (<span class="dt">int</span> argc, <span class="dt">char</span> **argv) {</span>
<span id="cb5-40"><a href="#cb5-40"></a> GtkApplication *app;</span>
<span id="cb5-41"><a href="#cb5-41"></a> <span class="dt">int</span> stat;</span>
<span id="cb5-42"><a href="#cb5-42"></a></span>
<span id="cb5-43"><a href="#cb5-43"></a> app = gtk_application_new (APPLICATION_ID, G_APPLICATION_FLAGS_NONE);</span>
<span id="cb5-44"><a href="#cb5-44"></a> g_signal_connect (app, <span class="st">&quot;activate&quot;</span>, G_CALLBACK (app_activate), NULL);</span>
<span id="cb5-45"><a href="#cb5-45"></a></span>
<span id="cb5-46"><a href="#cb5-46"></a> stat =g_application_run (G_APPLICATION (app), argc, argv);</span>
<span id="cb5-47"><a href="#cb5-47"></a> g_object_unref (app);</span>
<span id="cb5-48"><a href="#cb5-48"></a> <span class="cf">return</span> stat;</span>
<span id="cb5-49"><a href="#cb5-49"></a>}</span></code></pre></div>
<ul>
<li>3-8: <code>quit_activated</code> is a handler of the “activate” signal on the action <code>act_quit</code>. Handlers of the “activate” signal have three parameters.
<ol type="1">
<li>The action instance on which the signal is emitted.</li>
<li>Parameter. In this example it is <code>NULL</code> because the second argument of <code>g_simple_action_new</code> (line 15) is <code>NULL</code>. You don t need to care about it.</li>
<li>User data. It is the fourth parameter in the <code>g_signal_connect</code> (line 18) that connects the action and the handler.</li>
</ol></li>
<li>7: A function <code>g_application_quit</code> immediately quits the application.</li>
<li>10-34: <code>app_activate</code> is a handler of “activate” signal on the GtkApplication instance.</li>
<li>12-14: Creates a GtkApplicationWindow <code>win</code>. And sets the title and the default size.</li>
<li>16: Creates GSimpleAction <code>act_quit</code>. It is stateless. The first argument of <code>g_simple_action_new</code> is a name of the action and the second argument is a parameter. If you dont need the parameter, pass <code>NULL</code>. Therefore, <code>act_quit</code> has a name “quit” and no parameter.</li>
<li>17: Adds the action to GtkApplication <code>app</code>. GtkApplication implements an interface GActionMap and GActionGroup. GtkApplication (GActionMap) can have a group of actions and the actions are added with the function <code>g_action_map_add_action</code>. This function is described in <a href="https://docs.gtk.org/gio/method.ActionMap.add_action.html">Gio API Reference, g_action_map_add_action</a>.</li>
<li>18: Connects “activate” signal of the action and the handler <code>quit_activated</code>.</li>
<li>20-23: Creates GMenu and GMenuItem instances. <code>menubar</code> and <code>menu</code> are GMenu. <code>menu_item_menu</code> and <code>menu_item_quit</code> are GMenuItem. <code>menu_item_menu</code> has a label “Menu” and no action. <code>menu_item_quit</code> has a label “Quit” and an action “app.quit”. The action “app.quit” is a combination of “app” and “quit”. “app” is a prefix and it means that the action belongs to GtkApplication. “quit” is the name of the action. Therefore, “app.quit” points the action which belongs to the GtkApplication instance and is named “quit”.</li>
<li>24-25: Appends <code>menu_item_quit</code> to <code>menu</code>. As I mentioned before, all the attributes and links are copied and used to form a new item in <code>menu</code>. Therefore after the appending, <code>menu_item_quit</code> is no longer needed. It is freed by <code>g_object_unref</code>.</li>
<li>26: Sets the submenu link in <code>menu_item_menu</code> to point <code>menu</code>.</li>
<li>27-28: Appends <code>menu_item_menu</code> to <code>menubar</code>. Then frees <code>menu_item_menu</code>. GMenu and GMenuItem are connected and finally a menu is made up. The structure of the menu is shown in the diagram below.</li>
<li>30: The menu is inserted to GtkApplication.</li>
<li>31: Sets GtkApplicationWindow to show the menubar.</li>
<li>32: Shows the window.</li>
</ul>
<figure>
<img src="image/menu1.png" alt="" /><figcaption>menu and action</figcaption>
</figure>
<figure>
<img src="image/menu1_screenshot.png" alt="" /><figcaption>Screenshot of menu1</figcaption>
</figure>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
</html>