2022-04-21 16:53:28 +02:00
|
|
|
|
<!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">
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<title>GTK 4 tutorial</title>
|
2022-04-21 16:53:28 +02:00
|
|
|
|
<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; }
|
2022-04-17 13:54:42 +02:00
|
|
|
|
}
|
2022-04-21 16:53:28 +02:00
|
|
|
|
@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>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
<h1 id="menus-and-actions">Menus and actions</h1>
|
|
|
|
|
<h2 id="menus">Menus</h2>
|
|
|
|
|
<p>Users often use menus to tell a command to the application. It is
|
|
|
|
|
like this:</p>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
<figure>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<img src="image/menu.png" alt="Menu" />
|
|
|
|
|
<figcaption aria-hidden="true">Menu</figcaption>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
</figure>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
<p>There are two types of objects.</p>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
<ul>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<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>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
</ul>
|
|
|
|
|
<figure>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<img src="image/menu_structure.png" alt="Menu structure" />
|
|
|
|
|
<figcaption aria-hidden="true">Menu structure</figcaption>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
</figure>
|
|
|
|
|
<ul>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<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 don’t 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>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
<li>The second section is a menu which has one item – “Select All”.</li>
|
|
|
|
|
</ul>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<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>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
<pre><code>GObject -- GMenuModel -- GMenu</code></pre>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<p>Because GMenuModel is an abstract object, it isn’t instantiatable.
|
|
|
|
|
Therefore, it doesn’t 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.</p>
|
|
|
|
|
<p>GMenuItem is an object directly derived from GObject. GMenuItem and
|
|
|
|
|
Gmenu (or GMenuModel) don’t have a parent-child relationship.</p>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
<pre><code>GObject -- GMenuModel -- GMenu
|
|
|
|
|
GObject -- GMenuItem</code></pre>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<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. “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 are copied and stored in
|
|
|
|
|
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>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
<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 ("Quit", "app.quit");
|
|
|
|
|
g_menu_append_item (menu, menu_item_quit);
|
|
|
|
|
g_object_unref (menu_item_quit);</code></pre>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
<h2 id="menu-and-action">Menu and action</h2>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<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>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
<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>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
|
|
|
|
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>quit_activated<span class="op">(</span>GSimpleAction <span class="op">*</span>action<span class="op">,</span> GVariant <span class="op">*</span>parameter<span class="op">,</span> gpointer app<span class="op">)</span> <span class="op">{</span> <span class="op">...</span> <span class="op">...</span> <span class="op">...}</span></span>
|
|
|
|
|
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span>
|
|
|
|
|
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>GSimpleAction <span class="op">*</span>act_quit <span class="op">=</span> g_simple_action_new <span class="op">(</span><span class="st">"quit"</span><span class="op">,</span> NULL<span class="op">);</span></span>
|
|
|
|
|
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>g_action_map_add_action <span class="op">(</span>G_ACTION_MAP <span class="op">(</span>app<span class="op">),</span> G_ACTION <span class="op">(</span>act_quit<span class="op">));</span></span>
|
|
|
|
|
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>g_signal_connect <span class="op">(</span>act_quit<span class="op">,</span> <span class="st">"activate"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>quit_activated<span class="op">),</span> app<span class="op">);</span></span>
|
|
|
|
|
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>GMenuItem <span class="op">*</span>menu_item_quit <span class="op">=</span> g_menu_item_new <span class="op">(</span><span class="st">"Quit"</span><span class="op">,</span> <span class="st">"app.quit"</span><span class="op">);</span></span></code></pre></div>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>The variable <code>menu_item_quit</code> points a menu item. It is
|
|
|
|
|
actually a pointer, but we often say that <code>menu_item_quit</code>
|
|
|
|
|
<em>is</em> a menu item. It has a label “Quit” and is connected to an
|
|
|
|
|
action “app.quit”. “app” is a prefix and “quit” is the name of the
|
|
|
|
|
action. The prefix “app” means that the action belongs to the
|
|
|
|
|
GtkApplication instance.</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
|
|
|
|
|
doesn’t 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>. So, the action’s
|
|
|
|
|
scope is application. The prefix of <code>app.quit</code> indicates the
|
|
|
|
|
scope.</li>
|
|
|
|
|
<li>“activate” signal of the action is connected to the handler
|
|
|
|
|
<code>quit_activated</code>.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<p>If the menu is clicked, the corresponding action “quit” will be
|
|
|
|
|
activated and emits an “activate” signal. Then, the handler
|
|
|
|
|
<code>quit_activated</code> is called.</p>
|
|
|
|
|
<h2 id="menu-bar">Menu bar</h2>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
<p>A menu bar and menus are traditional. Menu buttons are often used
|
|
|
|
|
instead of a menu bar lately, but the old style is still used
|
2023-01-03 07:30:06 +01:00
|
|
|
|
widely.</p>
|
|
|
|
|
<p>Applications have only one menu bar. If an application has two or
|
|
|
|
|
more windows which have menu bars, the menu bars are exactly the same.
|
|
|
|
|
Because every window refers to the same menubar instance in the
|
|
|
|
|
application.</p>
|
|
|
|
|
<p>An application’s menu bar is usually unchanged once it is set. So, it
|
2023-07-15 10:27:53 +02:00
|
|
|
|
is appropriate to set it in the “startup” handler. Because the handler
|
|
|
|
|
is called only once in the primary application instance.</p>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<p>I think it is good for readers to clarify how applications
|
|
|
|
|
behave.</p>
|
|
|
|
|
<ul>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
<li>When an application runs for the first time, the instance is called
|
|
|
|
|
primary.</li>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<li>The primary instance registers itself to the system. If it succeeds,
|
|
|
|
|
it emits “startup” signal.</li>
|
|
|
|
|
<li>When the instance is activated, an “activate” or “open” signal is
|
|
|
|
|
emitted.</li>
|
|
|
|
|
<li>If the application is run for the second time or later and there
|
|
|
|
|
exists a primary instance, the instance is called a remote
|
|
|
|
|
instance.</li>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
<li>A remote instance doesn’t emit “startup” signal.</li>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<li>If it tries to emit an “activate” or “open” signal, the signals are
|
|
|
|
|
not emitted on the remote instance but primary instance.</li>
|
|
|
|
|
<li>The remote instance quits.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<p>Therefore, an “activate” or “open” handler can be called twice or
|
2023-07-15 10:27:53 +02:00
|
|
|
|
more. On the other hand, a “startup” handler is called once. So, the
|
|
|
|
|
menubar should be set in the “startup” handler.</p>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
|
|
|
|
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">)</span> <span class="op">{</span></span>
|
|
|
|
|
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
|
|
|
|
|
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> gtk_application_set_menubar <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">),</span> G_MENU_MODEL <span class="op">(</span>menubar<span class="op">));</span></span>
|
|
|
|
|
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
|
|
|
|
|
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
<h2 id="simple-example">Simple example</h2>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<p>The following is a simple example of menus and actions. The source
|
|
|
|
|
file <code>menu1.c</code> is located at src/menu directory.</p>
|
|
|
|
|
<div class="sourceCode" id="cb6"><pre
|
|
|
|
|
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1"></a><span class="pp">#include </span><span class="im"><gtk/gtk.h></span></span>
|
|
|
|
|
<span id="cb6-2"><a href="#cb6-2"></a></span>
|
|
|
|
|
<span id="cb6-3"><a href="#cb6-3"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
|
|
|
|
<span id="cb6-4"><a href="#cb6-4"></a>quit_activated<span class="op">(</span>GSimpleAction <span class="op">*</span>action<span class="op">,</span> GVariant <span class="op">*</span>parameter<span class="op">,</span> GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
|
|
|
|
|
<span id="cb6-5"><a href="#cb6-5"></a> g_application_quit <span class="op">(</span>application<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-6"><a href="#cb6-6"></a><span class="op">}</span></span>
|
|
|
|
|
<span id="cb6-7"><a href="#cb6-7"></a></span>
|
|
|
|
|
<span id="cb6-8"><a href="#cb6-8"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
|
|
|
|
<span id="cb6-9"><a href="#cb6-9"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
|
|
|
|
|
<span id="cb6-10"><a href="#cb6-10"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
<span id="cb6-11"><a href="#cb6-11"></a> GtkWidget <span class="op">*</span>win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>app<span class="op">);</span></span>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<span id="cb6-12"><a href="#cb6-12"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">"menu1"</span><span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-13"><a href="#cb6-13"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">400</span><span class="op">,</span> <span class="dv">300</span><span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-14"><a href="#cb6-14"></a></span>
|
|
|
|
|
<span id="cb6-15"><a href="#cb6-15"></a> gtk_application_window_set_show_menubar <span class="op">(</span>GTK_APPLICATION_WINDOW <span class="op">(</span>win<span class="op">),</span> TRUE<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-16"><a href="#cb6-16"></a> gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
|
|
|
|
|
<span id="cb6-17"><a href="#cb6-17"></a><span class="op">}</span></span>
|
|
|
|
|
<span id="cb6-18"><a href="#cb6-18"></a></span>
|
|
|
|
|
<span id="cb6-19"><a href="#cb6-19"></a><span class="dt">static</span> <span class="dt">void</span></span>
|
|
|
|
|
<span id="cb6-20"><a href="#cb6-20"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
|
|
|
|
|
<span id="cb6-21"><a href="#cb6-21"></a> GtkApplication <span class="op">*</span>app <span class="op">=</span> GTK_APPLICATION <span class="op">(</span>application<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-22"><a href="#cb6-22"></a></span>
|
|
|
|
|
<span id="cb6-23"><a href="#cb6-23"></a> GSimpleAction <span class="op">*</span>act_quit <span class="op">=</span> g_simple_action_new <span class="op">(</span><span class="st">"quit"</span><span class="op">,</span> NULL<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-24"><a href="#cb6-24"></a> g_action_map_add_action <span class="op">(</span>G_ACTION_MAP <span class="op">(</span>app<span class="op">),</span> G_ACTION <span class="op">(</span>act_quit<span class="op">));</span></span>
|
|
|
|
|
<span id="cb6-25"><a href="#cb6-25"></a> g_signal_connect <span class="op">(</span>act_quit<span class="op">,</span> <span class="st">"activate"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>quit_activated<span class="op">),</span> application<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-26"><a href="#cb6-26"></a></span>
|
|
|
|
|
<span id="cb6-27"><a href="#cb6-27"></a> GMenu <span class="op">*</span>menubar <span class="op">=</span> g_menu_new <span class="op">();</span></span>
|
|
|
|
|
<span id="cb6-28"><a href="#cb6-28"></a> GMenuItem <span class="op">*</span>menu_item_menu <span class="op">=</span> g_menu_item_new <span class="op">(</span><span class="st">"Menu"</span><span class="op">,</span> NULL<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-29"><a href="#cb6-29"></a> GMenu <span class="op">*</span>menu <span class="op">=</span> g_menu_new <span class="op">();</span></span>
|
|
|
|
|
<span id="cb6-30"><a href="#cb6-30"></a> GMenuItem <span class="op">*</span>menu_item_quit <span class="op">=</span> g_menu_item_new <span class="op">(</span><span class="st">"Quit"</span><span class="op">,</span> <span class="st">"app.quit"</span><span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-31"><a href="#cb6-31"></a> g_menu_append_item <span class="op">(</span>menu<span class="op">,</span> menu_item_quit<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-32"><a href="#cb6-32"></a> g_object_unref <span class="op">(</span>menu_item_quit<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-33"><a href="#cb6-33"></a> g_menu_item_set_submenu <span class="op">(</span>menu_item_menu<span class="op">,</span> G_MENU_MODEL <span class="op">(</span>menu<span class="op">));</span></span>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
<span id="cb6-34"><a href="#cb6-34"></a> g_object_unref <span class="op">(</span>menu<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-35"><a href="#cb6-35"></a> g_menu_append_item <span class="op">(</span>menubar<span class="op">,</span> menu_item_menu<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-36"><a href="#cb6-36"></a> g_object_unref <span class="op">(</span>menu_item_menu<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-37"><a href="#cb6-37"></a></span>
|
|
|
|
|
<span id="cb6-38"><a href="#cb6-38"></a> gtk_application_set_menubar <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">),</span> G_MENU_MODEL <span class="op">(</span>menubar<span class="op">));</span></span>
|
|
|
|
|
<span id="cb6-39"><a href="#cb6-39"></a><span class="op">}</span></span>
|
|
|
|
|
<span id="cb6-40"><a href="#cb6-40"></a></span>
|
|
|
|
|
<span id="cb6-41"><a href="#cb6-41"></a><span class="pp">#define APPLICATION_ID "com.github.ToshioCP.menu1"</span></span>
|
|
|
|
|
<span id="cb6-42"><a href="#cb6-42"></a></span>
|
|
|
|
|
<span id="cb6-43"><a href="#cb6-43"></a><span class="dt">int</span></span>
|
|
|
|
|
<span id="cb6-44"><a href="#cb6-44"></a>main <span class="op">(</span><span class="dt">int</span> argc<span class="op">,</span> <span class="dt">char</span> <span class="op">**</span>argv<span class="op">)</span> <span class="op">{</span></span>
|
|
|
|
|
<span id="cb6-45"><a href="#cb6-45"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
|
|
|
|
|
<span id="cb6-46"><a href="#cb6-46"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
|
|
|
|
|
<span id="cb6-47"><a href="#cb6-47"></a></span>
|
|
|
|
|
<span id="cb6-48"><a href="#cb6-48"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span>APPLICATION_ID<span class="op">,</span> G_APPLICATION_DEFAULT_FLAGS<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-49"><a href="#cb6-49"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">"startup"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_startup<span class="op">),</span> NULL<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-50"><a href="#cb6-50"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">"activate"</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_activate<span class="op">),</span> NULL<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-51"><a href="#cb6-51"></a></span>
|
|
|
|
|
<span id="cb6-52"><a href="#cb6-52"></a> stat <span class="op">=</span>g_application_run <span class="op">(</span>G_APPLICATION <span class="op">(</span>app<span class="op">),</span> argc<span class="op">,</span> argv<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-53"><a href="#cb6-53"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
|
|
|
|
|
<span id="cb6-54"><a href="#cb6-54"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
|
|
|
|
|
<span id="cb6-55"><a href="#cb6-55"></a><span class="op">}</span></span></code></pre></div>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
<ul>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<li>3-6: <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.
|
2022-04-17 13:54:42 +02:00
|
|
|
|
<ol type="1">
|
|
|
|
|
<li>The action instance on which the signal is emitted.</li>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<li>Parameter. In this example it is <code>NULL</code> because the
|
|
|
|
|
second argument of <code>g_simple_action_new</code> (line 23) 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 25) that connects the action and the
|
|
|
|
|
handler.</li>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
</ol></li>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<li>5: The function <code>g_application_quit</code> immediately quits
|
|
|
|
|
the application.</li>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
<li>8-17: <code>app_activate</code> is an “activate” signal handler on
|
|
|
|
|
the application.</li>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<li>11-13: Creates a GtkApplicationWindow <code>win</code>. And sets the
|
|
|
|
|
title and the default size.</li>
|
|
|
|
|
<li>15: Sets GtkApplicationWindow to show the menubar.</li>
|
|
|
|
|
<li>16: Shows the window.</li>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
<li>19-38: <code>app_startup</code> is a “startup” signal handler on the
|
|
|
|
|
application.</li>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<li>23: 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 don’t need the
|
|
|
|
|
parameter, pass <code>NULL</code>. Therefore, <code>act_quit</code> has
|
|
|
|
|
a name “quit” and no parameter.</li>
|
|
|
|
|
<li>24: 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>. Because this action belongs to
|
|
|
|
|
GtkApplication, its scope is “app” and it is referred with “app.quit” if
|
|
|
|
|
the prefix (scope) is necessary.</li>
|
|
|
|
|
<li>25: Connects “activate” signal of the action and the handler
|
|
|
|
|
<code>quit_activated</code>.</li>
|
|
|
|
|
<li>27-30: 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”.</li>
|
|
|
|
|
<li>31-32: 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 addition,
|
|
|
|
|
<code>menu_item_quit</code> is no longer needed. It is freed by
|
|
|
|
|
<code>g_object_unref</code>.</li>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
<li>33-34: Sets the submenu link in <code>menu_item_menu</code> to point
|
|
|
|
|
<code>menu</code>. Then, <code>menu</code> is no more useful and it is
|
|
|
|
|
freed.</li>
|
|
|
|
|
<li>35-36: Appends <code>menu_item_menu</code> to <code>menubar</code>.
|
|
|
|
|
Then frees <code>menu_item_menu</code>. GMenu and GMenuItem are built
|
|
|
|
|
and finally connected to the variable <code>menubar</code>. The
|
|
|
|
|
structure of the menu is shown in the diagram below.</li>
|
|
|
|
|
<li>38: The menubar is inserted to the application.</li>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
</ul>
|
|
|
|
|
<figure>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<img src="image/menu1.png" alt="menu and action" />
|
|
|
|
|
<figcaption aria-hidden="true">menu and action</figcaption>
|
|
|
|
|
</figure>
|
|
|
|
|
<h2 id="compiling">Compiling</h2>
|
|
|
|
|
<p>Change your current directory to <code>src/menu</code>. Use comp to
|
|
|
|
|
compile <code>menu1.c</code>.</p>
|
|
|
|
|
<pre><code>$ comp menu1
|
|
|
|
|
$ ./a.out</code></pre>
|
|
|
|
|
<p>Then, a window appears. Click on “Menu” on the menubar, then a menu
|
|
|
|
|
appears. Click on “Quit” menu, then the application quits.</p>
|
|
|
|
|
<figure>
|
|
|
|
|
<img src="image/menu1_screenshot.png" alt="Screenshot of menu1" />
|
|
|
|
|
<figcaption aria-hidden="true">Screenshot of menu1</figcaption>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
</figure>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<h2 id="primary-and-remote-application-instances">Primary and remote
|
|
|
|
|
application instances</h2>
|
|
|
|
|
<p>Let’s try running the application twice. Use <code>&</code> in
|
|
|
|
|
your shell command line, then the application runs concurrently.</p>
|
|
|
|
|
<pre><code>$ ./a.out &
|
|
|
|
|
[1] 70969
|
|
|
|
|
$ ./a.out
|
|
|
|
|
$ </code></pre>
|
|
|
|
|
<p>Then, two windows appear.</p>
|
|
|
|
|
<ul>
|
|
|
|
|
<li>The first <code>./a.out</code> calls the application and a primary
|
|
|
|
|
instance is created. It calls “startup” and “activate” handlers and
|
|
|
|
|
shows a window.</li>
|
|
|
|
|
<li>The second<code>./a.out</code> calls the the application again and
|
|
|
|
|
the created instance is a remote one. It doesn’t emit “startup” signal.
|
|
|
|
|
And it activates the application but the “activate” signal is emitted on
|
|
|
|
|
the primary instance. The remote instance quits.</li>
|
|
|
|
|
<li>The primary instance called “activate” handler. The handler creates
|
|
|
|
|
a new window. It adds a menu bar to the window with
|
|
|
|
|
<code>gtk_application_window_set_show_menubar</code> function.</li>
|
|
|
|
|
</ul>
|
|
|
|
|
<p>Both the windows have menu bars. And they are exactly the same. The
|
|
|
|
|
two windows belong to the primary instance.</p>
|
|
|
|
|
<p>If you click on the “Quit” menu, the application (the primary
|
|
|
|
|
instance) quits.</p>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
<figure>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<img src="image/menu1_two_windows.png" alt="menu1 – two windows" />
|
|
|
|
|
<figcaption aria-hidden="true">menu1 – two windows</figcaption>
|
2022-04-17 13:54:42 +02:00
|
|
|
|
</figure>
|
2023-07-15 10:27:53 +02:00
|
|
|
|
<p>The second execution makes a new window. However, it depends on the
|
2023-01-03 07:30:06 +01:00
|
|
|
|
“activate” handler. If you create your window in the startup handler and
|
|
|
|
|
the activate handler just presents the window, no new window is created
|
2023-07-15 10:27:53 +02:00
|
|
|
|
at the second execution. For example, tfe (text file editor) doesn’t
|
|
|
|
|
create a second window. It just creates a new notebook page. Because its
|
|
|
|
|
activate handler doesn’t create any window but just creates a new
|
|
|
|
|
notebook page.</p>
|
2023-01-03 07:30:06 +01:00
|
|
|
|
<p>Second or more executions often happen on the desktop applications.
|
|
|
|
|
If you double-click the icon twice or more, the application is run
|
|
|
|
|
multiple times. Therefore, you need to think about your startup and
|
|
|
|
|
activate (open) handler carefully.</p>
|
2022-04-21 16:53:28 +02:00
|
|
|
|
</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>
|