Gtk4-tutorial/docs/sec9.html
Toshio Sekiya 7b14d03a24 Change the width of HTML contents.
Update the distribution package information.
2023-12-10 16:39:32 +08:00

592 lines
55 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
<title>GTK 4 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="sec8.html">Prev: section8</a>
</li>
<li class="nav-item">
<a class="nav-link" href="sec10.html">Next: section10</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="row justify-content-center">
<div class="col-xl-10 col-xxl-9">
<h1 id="gtkbuilder-and-ui-file">GtkBuilder and UI file</h1>
<h2 id="new-open-and-save-button">New, Open and Save button</h2>
<p>We made very simple editor in the previous section. It reads files at
the start and writes them out at the end of the program. It works, but
is not so good. It would be better if we had “New”, “Open”, “Save” and
“Close” buttons. This section describes how to put those buttons into
the window.</p>
<figure>
<img src="image/screenshot_tfe2.png"
alt="Screenshot of the file editor" />
<figcaption aria-hidden="true">Screenshot of the file
editor</figcaption>
</figure>
<p>The screenshot above shows the layout. The function
<code>app_open</code> in the source code <code>tfe2.c</code> is as
follows.</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb1-2"><a href="#cb1-2"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> gint n_files<span class="op">,</span> gchar <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-3"><a href="#cb1-3"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb1-4"><a href="#cb1-4"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span>
<span id="cb1-5"><a href="#cb1-5"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span>
<span id="cb1-7"><a href="#cb1-7"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb1-8"><a href="#cb1-8"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb1-9"><a href="#cb1-9"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb1-10"><a href="#cb1-10"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb1-11"><a href="#cb1-11"></a> gsize length<span class="op">;</span></span>
<span id="cb1-12"><a href="#cb1-12"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb1-13"><a href="#cb1-13"></a> <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb1-14"><a href="#cb1-14"></a> GError <span class="op">*</span>err <span class="op">=</span> NULL<span class="op">;</span></span>
<span id="cb1-15"><a href="#cb1-15"></a></span>
<span id="cb1-16"><a href="#cb1-16"></a> GtkWidget <span class="op">*</span>boxv<span class="op">;</span></span>
<span id="cb1-17"><a href="#cb1-17"></a> GtkWidget <span class="op">*</span>boxh<span class="op">;</span></span>
<span id="cb1-18"><a href="#cb1-18"></a> GtkWidget <span class="op">*</span>dmy1<span class="op">;</span></span>
<span id="cb1-19"><a href="#cb1-19"></a> GtkWidget <span class="op">*</span>dmy2<span class="op">;</span></span>
<span id="cb1-20"><a href="#cb1-20"></a> GtkWidget <span class="op">*</span>dmy3<span class="op">;</span></span>
<span id="cb1-21"><a href="#cb1-21"></a> GtkWidget <span class="op">*</span>btnn<span class="op">;</span> <span class="co">/* button for new */</span></span>
<span id="cb1-22"><a href="#cb1-22"></a> GtkWidget <span class="op">*</span>btno<span class="op">;</span> <span class="co">/* button for open */</span></span>
<span id="cb1-23"><a href="#cb1-23"></a> GtkWidget <span class="op">*</span>btns<span class="op">;</span> <span class="co">/* button for save */</span></span>
<span id="cb1-24"><a href="#cb1-24"></a> GtkWidget <span class="op">*</span>btnc<span class="op">;</span> <span class="co">/* button for close */</span></span>
<span id="cb1-25"><a href="#cb1-25"></a></span>
<span id="cb1-26"><a href="#cb1-26"></a> win <span class="op">=</span> gtk_application_window_new <span class="op">(</span>GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb1-27"><a href="#cb1-27"></a> gtk_window_set_title <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="st">&quot;file editor&quot;</span><span class="op">);</span></span>
<span id="cb1-28"><a href="#cb1-28"></a> gtk_window_set_default_size <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> <span class="dv">600</span><span class="op">,</span> <span class="dv">400</span><span class="op">);</span></span>
<span id="cb1-29"><a href="#cb1-29"></a></span>
<span id="cb1-30"><a href="#cb1-30"></a> boxv <span class="op">=</span> gtk_box_new <span class="op">(</span>GTK_ORIENTATION_VERTICAL<span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb1-31"><a href="#cb1-31"></a> gtk_window_set_child <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> boxv<span class="op">);</span></span>
<span id="cb1-32"><a href="#cb1-32"></a></span>
<span id="cb1-33"><a href="#cb1-33"></a> boxh <span class="op">=</span> gtk_box_new <span class="op">(</span>GTK_ORIENTATION_HORIZONTAL<span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb1-34"><a href="#cb1-34"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxv<span class="op">),</span> boxh<span class="op">);</span></span>
<span id="cb1-35"><a href="#cb1-35"></a></span>
<span id="cb1-36"><a href="#cb1-36"></a> dmy1 <span class="op">=</span> gtk_label_new<span class="op">(</span>NULL<span class="op">);</span> <span class="co">/* dummy label for left space */</span></span>
<span id="cb1-37"><a href="#cb1-37"></a> gtk_label_set_width_chars <span class="op">(</span>GTK_LABEL <span class="op">(</span>dmy1<span class="op">),</span> <span class="dv">10</span><span class="op">);</span></span>
<span id="cb1-38"><a href="#cb1-38"></a> dmy2 <span class="op">=</span> gtk_label_new<span class="op">(</span>NULL<span class="op">);</span> <span class="co">/* dummy label for center space */</span></span>
<span id="cb1-39"><a href="#cb1-39"></a> gtk_widget_set_hexpand <span class="op">(</span>dmy2<span class="op">,</span> TRUE<span class="op">);</span></span>
<span id="cb1-40"><a href="#cb1-40"></a> dmy3 <span class="op">=</span> gtk_label_new<span class="op">(</span>NULL<span class="op">);</span> <span class="co">/* dummy label for right space */</span></span>
<span id="cb1-41"><a href="#cb1-41"></a> gtk_label_set_width_chars <span class="op">(</span>GTK_LABEL <span class="op">(</span>dmy3<span class="op">),</span> <span class="dv">10</span><span class="op">);</span></span>
<span id="cb1-42"><a href="#cb1-42"></a> btnn <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">&quot;New&quot;</span><span class="op">);</span></span>
<span id="cb1-43"><a href="#cb1-43"></a> btno <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">&quot;Open&quot;</span><span class="op">);</span></span>
<span id="cb1-44"><a href="#cb1-44"></a> btns <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">&quot;Save&quot;</span><span class="op">);</span></span>
<span id="cb1-45"><a href="#cb1-45"></a> btnc <span class="op">=</span> gtk_button_new_with_label <span class="op">(</span><span class="st">&quot;Close&quot;</span><span class="op">);</span></span>
<span id="cb1-46"><a href="#cb1-46"></a></span>
<span id="cb1-47"><a href="#cb1-47"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> dmy1<span class="op">);</span></span>
<span id="cb1-48"><a href="#cb1-48"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> btnn<span class="op">);</span></span>
<span id="cb1-49"><a href="#cb1-49"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> btno<span class="op">);</span></span>
<span id="cb1-50"><a href="#cb1-50"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> dmy2<span class="op">);</span></span>
<span id="cb1-51"><a href="#cb1-51"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> btns<span class="op">);</span></span>
<span id="cb1-52"><a href="#cb1-52"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> btnc<span class="op">);</span></span>
<span id="cb1-53"><a href="#cb1-53"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxh<span class="op">),</span> dmy3<span class="op">);</span></span>
<span id="cb1-54"><a href="#cb1-54"></a></span>
<span id="cb1-55"><a href="#cb1-55"></a> nb <span class="op">=</span> gtk_notebook_new <span class="op">();</span></span>
<span id="cb1-56"><a href="#cb1-56"></a> gtk_widget_set_hexpand <span class="op">(</span>nb<span class="op">,</span> TRUE<span class="op">);</span></span>
<span id="cb1-57"><a href="#cb1-57"></a> gtk_widget_set_vexpand <span class="op">(</span>nb<span class="op">,</span> TRUE<span class="op">);</span></span>
<span id="cb1-58"><a href="#cb1-58"></a> gtk_box_append <span class="op">(</span>GTK_BOX <span class="op">(</span>boxv<span class="op">),</span> nb<span class="op">);</span></span>
<span id="cb1-59"><a href="#cb1-59"></a></span>
<span id="cb1-60"><a href="#cb1-60"></a> <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> n_files<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb1-61"><a href="#cb1-61"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span>i<span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&amp;</span>err<span class="op">))</span> <span class="op">{</span></span>
<span id="cb1-62"><a href="#cb1-62"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
<span id="cb1-63"><a href="#cb1-63"></a> tv <span class="op">=</span> tfe_text_view_new <span class="op">();</span></span>
<span id="cb1-64"><a href="#cb1-64"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb1-65"><a href="#cb1-65"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
<span id="cb1-66"><a href="#cb1-66"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb1-67"><a href="#cb1-67"></a></span>
<span id="cb1-68"><a href="#cb1-68"></a> tfe_text_view_set_file <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> g_file_dup <span class="op">(</span>files<span class="op">[</span>i<span class="op">]));</span></span>
<span id="cb1-69"><a href="#cb1-69"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb1-70"><a href="#cb1-70"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb1-71"><a href="#cb1-71"></a> filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span>i<span class="op">]);</span></span>
<span id="cb1-72"><a href="#cb1-72"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb1-73"><a href="#cb1-73"></a> gtk_notebook_append_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">,</span> lab<span class="op">);</span></span>
<span id="cb1-74"><a href="#cb1-74"></a> nbp <span class="op">=</span> gtk_notebook_get_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">);</span></span>
<span id="cb1-75"><a href="#cb1-75"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">&quot;tab-expand&quot;</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb1-76"><a href="#cb1-76"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb1-77"><a href="#cb1-77"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb1-78"><a href="#cb1-78"></a> g_printerr <span class="op">(</span><span class="st">&quot;%s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> err<span class="op">-&gt;</span>message<span class="op">);</span></span>
<span id="cb1-79"><a href="#cb1-79"></a> g_clear_error <span class="op">(&amp;</span>err<span class="op">);</span></span>
<span id="cb1-80"><a href="#cb1-80"></a> <span class="op">}</span></span>
<span id="cb1-81"><a href="#cb1-81"></a> <span class="op">}</span></span>
<span id="cb1-82"><a href="#cb1-82"></a> <span class="cf">if</span> <span class="op">(</span>gtk_notebook_get_n_pages <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">))</span> <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-83"><a href="#cb1-83"></a> gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb1-84"><a href="#cb1-84"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb1-85"><a href="#cb1-85"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb1-86"><a href="#cb1-86"></a><span class="op">}</span></span></code></pre></div>
<p>The function <code>app_open</code> builds the widgets in the main
application window.</p>
<ul>
<li>26-28: Creates a GtkApplicationWindow instance and sets the title
and default size.</li>
<li>30-31: Creates a GtkBox instance <code>boxv</code>. It is a vertical
box and a child of GtkApplicationWindow. It has two children. The first
child is a horizontal box. The second child is a GtkNotebook.</li>
<li>33-34: Creates a GtkBox instance <code>boxh</code> and appends it to
<code>boxv</code> as the first child.</li>
<li>36-41: Creates three dummy labels. The labels <code>dmy1</code> and
<code>dmy3</code> has a character width of ten. The other label
<code>dmy2</code> has hexpand property which is set to be TRUE. This
makes the label expands horizontally as long as possible.</li>
<li>42-45: Creates four buttons.</li>
<li>47-53: Appends these GtkLabel and GtkButton to
<code>boxh</code>.</li>
<li>55-58: Creates a GtkNotebook instance and sets hexpand and vexpand
properties to be TRUE. This makes it expand horizontally and vertically
as big as possible. It is appended to <code>boxv</code> as the second
child.</li>
</ul>
<p>The number of widget-build lines is 33(=58-26+1). We also needed many
variables (<code>boxv</code>, <code>boxh</code>, <code>dmy1</code>, …)
and most of them used only for building the widgets. Are there any good
solution to reduce these works?</p>
<p>Gtk provides GtkBuilder. It reads user interface (UI) data and builds
a window. It reduces this cumbersome work.</p>
<h2 id="the-ui-file">The UI File</h2>
<p>Look at the UI file <code>tfe3.ui</code> that defines widget
structure.</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb2-1"><a href="#cb2-1"></a><span class="fu">&lt;?xml</span><span class="ot"> version=</span><span class="st">&quot;1.0&quot;</span><span class="ot"> encoding=</span><span class="st">&quot;UTF-8&quot;</span><span class="fu">?&gt;</span></span>
<span id="cb2-2"><a href="#cb2-2"></a>&lt;<span class="kw">interface</span>&gt;</span>
<span id="cb2-3"><a href="#cb2-3"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkApplicationWindow&quot;</span><span class="ot"> id=</span><span class="st">&quot;win&quot;</span>&gt;</span>
<span id="cb2-4"><a href="#cb2-4"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;title&quot;</span>&gt;file editor&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-5"><a href="#cb2-5"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;default-width&quot;</span>&gt;600&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-6"><a href="#cb2-6"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;default-height&quot;</span>&gt;400&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-7"><a href="#cb2-7"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-8"><a href="#cb2-8"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span>&gt;</span>
<span id="cb2-9"><a href="#cb2-9"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;orientation&quot;</span>&gt;GTK_ORIENTATION_VERTICAL&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-10"><a href="#cb2-10"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-11"><a href="#cb2-11"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span>&gt;</span>
<span id="cb2-12"><a href="#cb2-12"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;orientation&quot;</span>&gt;GTK_ORIENTATION_HORIZONTAL&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-13"><a href="#cb2-13"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-14"><a href="#cb2-14"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span>&gt;</span>
<span id="cb2-15"><a href="#cb2-15"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;width-chars&quot;</span>&gt;10&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-16"><a href="#cb2-16"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-17"><a href="#cb2-17"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-18"><a href="#cb2-18"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-19"><a href="#cb2-19"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span>&gt;</span>
<span id="cb2-20"><a href="#cb2-20"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;New&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-21"><a href="#cb2-21"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-22"><a href="#cb2-22"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-23"><a href="#cb2-23"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-24"><a href="#cb2-24"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span>&gt;</span>
<span id="cb2-25"><a href="#cb2-25"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;Open&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-26"><a href="#cb2-26"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-27"><a href="#cb2-27"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-28"><a href="#cb2-28"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-29"><a href="#cb2-29"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span>&gt;</span>
<span id="cb2-30"><a href="#cb2-30"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;hexpand&quot;</span>&gt;TRUE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-31"><a href="#cb2-31"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-32"><a href="#cb2-32"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-33"><a href="#cb2-33"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-34"><a href="#cb2-34"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span>&gt;</span>
<span id="cb2-35"><a href="#cb2-35"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;Save&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-36"><a href="#cb2-36"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-37"><a href="#cb2-37"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-38"><a href="#cb2-38"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-39"><a href="#cb2-39"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span>&gt;</span>
<span id="cb2-40"><a href="#cb2-40"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;Close&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-41"><a href="#cb2-41"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-42"><a href="#cb2-42"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-43"><a href="#cb2-43"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-44"><a href="#cb2-44"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span>&gt;</span>
<span id="cb2-45"><a href="#cb2-45"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;width-chars&quot;</span>&gt;10&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-46"><a href="#cb2-46"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-47"><a href="#cb2-47"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-48"><a href="#cb2-48"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-49"><a href="#cb2-49"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-50"><a href="#cb2-50"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-51"><a href="#cb2-51"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkNotebook&quot;</span><span class="ot"> id=</span><span class="st">&quot;nb&quot;</span>&gt;</span>
<span id="cb2-52"><a href="#cb2-52"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;hexpand&quot;</span>&gt;TRUE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-53"><a href="#cb2-53"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;vexpand&quot;</span>&gt;TRUE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-54"><a href="#cb2-54"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-55"><a href="#cb2-55"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-56"><a href="#cb2-56"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-57"><a href="#cb2-57"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb2-58"><a href="#cb2-58"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb2-59"><a href="#cb2-59"></a>&lt;/<span class="kw">interface</span>&gt;</span></code></pre></div>
<p>The is a XML file. Tags begin with <code>&lt;</code> and end with
<code>&gt;</code>. There are two types of tags, the start tag and the
end tag. For example, <code>&lt;interface&gt;</code> is a start tag and
<code>&lt;/interface&gt;</code> is an end tag. The UI file begins and
ends with interface tags. Some tags, for example object tags, can have a
class and id attributes in their start tag.</p>
<ul>
<li>1: XML declaration. It specifies that the XML version is 1.0 and the
encoding is UTF-8.</li>
<li>3-6: An object tag with <code>GtkApplicationWindow</code> class and
<code>win</code> id. This is the top level window. And the three
properties of the window are defined. The <code>title</code> property is
“file editor”, <code>default-width</code> property is 600 and
<code>default-height</code> property is 400.</li>
<li>7: Child tag means a child widget. For example, line 7 tells us that
GtkBox object is a child widget of <code>win</code>.</li>
</ul>
<p>Compare this ui file and the lines 26-58 in the <code>app_open</code>
function of <code>tfe2.c</code>. Both builds the same window with its
descendant widgets.</p>
<p>You can check the ui file with <code>gtk4-builder-tool</code>.</p>
<ul>
<li><code>gtk4-builder-tool validate &lt;ui file name&gt;</code>
validates the ui file. If the ui file includes some syntactical error,
<code>gtk4-builder-tool</code> prints the error.</li>
<li><code>gtk4-builder-tool simplify &lt;ui file name&gt;</code>
simplifies the ui file and prints the result. If <code>--replace</code>
option is given, it replaces the ui file with the simplified one. If the
ui file specifies a value of property but it is default, then it will be
removed. For example, the default orientation is horizontal so the
simplification removes line 12. And some values are simplified. For
example, “TRUE”and “FALSE” becomes “1” and “0” respectively. However,
“TRUE” or “FALSE” is better for maintenance.</li>
</ul>
<p>It is a good idea to check your ui file before compiling.</p>
<h2 id="gtkbuilder">GtkBuilder</h2>
<p>GtkBuilder builds widgets based on a ui file.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>build <span class="op">=</span> gtk_builder_new_from_file <span class="op">(</span><span class="st">&quot;tfe3.ui&quot;</span><span class="op">);</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;win&quot;</span><span class="op">));</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>nb <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;nb&quot;</span><span class="op">));</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>g_object_unref<span class="op">(</span>build<span class="op">);</span></span></code></pre></div>
<p>The function <code>gtk_builder_new_from_file</code> reads the file
<code>tfe3.ui</code>. Then, it builds the widgets and creates GtkBuilder
object. All the widgets are connected based on the parent-children
relationship described in the ui file. We can retrieve objects from the
builder object with <code>gtk_builder_get_object</code> function. The
top level window, its id is “win” in the ui file, is taken and assigned
to the variable <code>win</code>, the application property of which is
set to <code>app</code> with the <code>gtk_window_set_application</code>
function. GtkNotebook with the id “nb” in the ui file is also taken and
assigned to the variable <code>nb</code>. After the window and
application are connected, GtkBuilder instance is useless. It is
released with <code>g_object_unref</code> function.</p>
<p>The ui file reduces lines in the C source file.</p>
<pre><code>$ cd tfe; diff tfe2.c tfe3.c
59a60
&gt; GtkBuilder *build;
61,104c62,66
&lt; GtkWidget *boxv;
&lt; GtkWidget *boxh;
&lt; GtkWidget *dmy1;
&lt; GtkWidget *dmy2;
&lt; GtkWidget *dmy3;
&lt; GtkWidget *btnn; /* button for new */
&lt; GtkWidget *btno; /* button for open */
&lt; GtkWidget *btns; /* button for save */
&lt; GtkWidget *btnc; /* button for close */
&lt;
&lt; win = gtk_application_window_new (GTK_APPLICATION (app));
&lt; gtk_window_set_title (GTK_WINDOW (win), &quot;file editor&quot;);
&lt; gtk_window_set_default_size (GTK_WINDOW (win), 600, 400);
&lt;
&lt; boxv = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
&lt; gtk_window_set_child (GTK_WINDOW (win), boxv);
&lt;
&lt; boxh = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
&lt; gtk_box_append (GTK_BOX (boxv), boxh);
&lt;
&lt; dmy1 = gtk_label_new(NULL); /* dummy label for left space */
&lt; gtk_label_set_width_chars (GTK_LABEL (dmy1), 10);
&lt; dmy2 = gtk_label_new(NULL); /* dummy label for center space */
&lt; gtk_widget_set_hexpand (dmy2, TRUE);
&lt; dmy3 = gtk_label_new(NULL); /* dummy label for right space */
&lt; gtk_label_set_width_chars (GTK_LABEL (dmy3), 10);
&lt; btnn = gtk_button_new_with_label (&quot;New&quot;);
&lt; btno = gtk_button_new_with_label (&quot;Open&quot;);
&lt; btns = gtk_button_new_with_label (&quot;Save&quot;);
&lt; btnc = gtk_button_new_with_label (&quot;Close&quot;);
&lt;
&lt; gtk_box_append (GTK_BOX (boxh), dmy1);
&lt; gtk_box_append (GTK_BOX (boxh), btnn);
&lt; gtk_box_append (GTK_BOX (boxh), btno);
&lt; gtk_box_append (GTK_BOX (boxh), dmy2);
&lt; gtk_box_append (GTK_BOX (boxh), btns);
&lt; gtk_box_append (GTK_BOX (boxh), btnc);
&lt; gtk_box_append (GTK_BOX (boxh), dmy3);
&lt;
&lt; nb = gtk_notebook_new ();
&lt; gtk_widget_set_hexpand (nb, TRUE);
&lt; gtk_widget_set_vexpand (nb, TRUE);
&lt; gtk_box_append (GTK_BOX (boxv), nb);
&lt;
---
&gt; build = gtk_builder_new_from_file (&quot;tfe3.ui&quot;);
&gt; win = GTK_WIDGET (gtk_builder_get_object (build, &quot;win&quot;));
&gt; gtk_window_set_application (GTK_WINDOW (win), GTK_APPLICATION (app));
&gt; nb = GTK_WIDGET (gtk_builder_get_object (build, &quot;nb&quot;));
&gt; g_object_unref(build);
138c100
&lt; app = gtk_application_new (&quot;com.github.ToshioCP.tfe2&quot;, G_APPLICATION_HANDLES_OPEN);
---
&gt; app = gtk_application_new (&quot;com.github.ToshioCP.tfe3&quot;, G_APPLICATION_HANDLES_OPEN);
144a107
&gt; </code></pre>
<p><code>61,104c62,66</code> means that 44 (=104-61+1) lines are changed
to 5 (=66-62+1) lines. Therefore, 39 lines are reduced. Using ui file
not only shortens C source files, but also makes the widgets structure
clear.</p>
<p>Now Ill show you <code>app_open</code> function in the C file
<code>tfe3.c</code>.</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="dt">static</span> <span class="dt">void</span></span>
<span id="cb5-2"><a href="#cb5-2"></a>app_open <span class="op">(</span>GApplication <span class="op">*</span>app<span class="op">,</span> GFile <span class="op">**</span> files<span class="op">,</span> gint n_files<span class="op">,</span> gchar <span class="op">*</span>hint<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3"></a> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb5-4"><a href="#cb5-4"></a> GtkWidget <span class="op">*</span>nb<span class="op">;</span></span>
<span id="cb5-5"><a href="#cb5-5"></a> GtkWidget <span class="op">*</span>lab<span class="op">;</span></span>
<span id="cb5-6"><a href="#cb5-6"></a> GtkNotebookPage <span class="op">*</span>nbp<span class="op">;</span></span>
<span id="cb5-7"><a href="#cb5-7"></a> GtkWidget <span class="op">*</span>scr<span class="op">;</span></span>
<span id="cb5-8"><a href="#cb5-8"></a> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb5-9"><a href="#cb5-9"></a> GtkTextBuffer <span class="op">*</span>tb<span class="op">;</span></span>
<span id="cb5-10"><a href="#cb5-10"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb5-11"><a href="#cb5-11"></a> gsize length<span class="op">;</span></span>
<span id="cb5-12"><a href="#cb5-12"></a> <span class="dt">char</span> <span class="op">*</span>filename<span class="op">;</span></span>
<span id="cb5-13"><a href="#cb5-13"></a> <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb5-14"><a href="#cb5-14"></a> GError <span class="op">*</span>err <span class="op">=</span> NULL<span class="op">;</span></span>
<span id="cb5-15"><a href="#cb5-15"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb5-16"><a href="#cb5-16"></a></span>
<span id="cb5-17"><a href="#cb5-17"></a> build <span class="op">=</span> gtk_builder_new_from_file <span class="op">(</span><span class="st">&quot;tfe3.ui&quot;</span><span class="op">);</span></span>
<span id="cb5-18"><a href="#cb5-18"></a> win <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;win&quot;</span><span class="op">));</span></span>
<span id="cb5-19"><a href="#cb5-19"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> GTK_APPLICATION <span class="op">(</span>app<span class="op">));</span></span>
<span id="cb5-20"><a href="#cb5-20"></a> nb <span class="op">=</span> GTK_WIDGET <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;nb&quot;</span><span class="op">));</span></span>
<span id="cb5-21"><a href="#cb5-21"></a> g_object_unref<span class="op">(</span>build<span class="op">);</span></span>
<span id="cb5-22"><a href="#cb5-22"></a> <span class="cf">for</span> <span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> n_files<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb5-23"><a href="#cb5-23"></a> <span class="cf">if</span> <span class="op">(</span>g_file_load_contents <span class="op">(</span>files<span class="op">[</span>i<span class="op">],</span> NULL<span class="op">,</span> <span class="op">&amp;</span>contents<span class="op">,</span> <span class="op">&amp;</span>length<span class="op">,</span> NULL<span class="op">,</span> <span class="op">&amp;</span>err<span class="op">))</span> <span class="op">{</span></span>
<span id="cb5-24"><a href="#cb5-24"></a> scr <span class="op">=</span> gtk_scrolled_window_new <span class="op">();</span></span>
<span id="cb5-25"><a href="#cb5-25"></a> tv <span class="op">=</span> tfe_text_view_new <span class="op">();</span></span>
<span id="cb5-26"><a href="#cb5-26"></a> tb <span class="op">=</span> gtk_text_view_get_buffer <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb5-27"><a href="#cb5-27"></a> gtk_text_view_set_wrap_mode <span class="op">(</span>GTK_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WRAP_WORD_CHAR<span class="op">);</span></span>
<span id="cb5-28"><a href="#cb5-28"></a> gtk_scrolled_window_set_child <span class="op">(</span>GTK_SCROLLED_WINDOW <span class="op">(</span>scr<span class="op">),</span> tv<span class="op">);</span></span>
<span id="cb5-29"><a href="#cb5-29"></a></span>
<span id="cb5-30"><a href="#cb5-30"></a> tfe_text_view_set_file <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> g_file_dup <span class="op">(</span>files<span class="op">[</span>i<span class="op">]));</span></span>
<span id="cb5-31"><a href="#cb5-31"></a> gtk_text_buffer_set_text <span class="op">(</span>tb<span class="op">,</span> contents<span class="op">,</span> length<span class="op">);</span></span>
<span id="cb5-32"><a href="#cb5-32"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb5-33"><a href="#cb5-33"></a> filename <span class="op">=</span> g_file_get_basename <span class="op">(</span>files<span class="op">[</span>i<span class="op">]);</span></span>
<span id="cb5-34"><a href="#cb5-34"></a> lab <span class="op">=</span> gtk_label_new <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb5-35"><a href="#cb5-35"></a> gtk_notebook_append_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">,</span> lab<span class="op">);</span></span>
<span id="cb5-36"><a href="#cb5-36"></a> nbp <span class="op">=</span> gtk_notebook_get_page <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">),</span> scr<span class="op">);</span></span>
<span id="cb5-37"><a href="#cb5-37"></a> g_object_set <span class="op">(</span>nbp<span class="op">,</span> <span class="st">&quot;tab-expand&quot;</span><span class="op">,</span> TRUE<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb5-38"><a href="#cb5-38"></a> g_free <span class="op">(</span>filename<span class="op">);</span></span>
<span id="cb5-39"><a href="#cb5-39"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb5-40"><a href="#cb5-40"></a> g_printerr <span class="op">(</span><span class="st">&quot;%s.</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">,</span> err<span class="op">-&gt;</span>message<span class="op">);</span></span>
<span id="cb5-41"><a href="#cb5-41"></a> g_clear_error <span class="op">(&amp;</span>err<span class="op">);</span></span>
<span id="cb5-42"><a href="#cb5-42"></a> <span class="op">}</span></span>
<span id="cb5-43"><a href="#cb5-43"></a> <span class="op">}</span></span>
<span id="cb5-44"><a href="#cb5-44"></a> <span class="cf">if</span> <span class="op">(</span>gtk_notebook_get_n_pages <span class="op">(</span>GTK_NOTEBOOK <span class="op">(</span>nb<span class="op">))</span> <span class="op">&gt;</span> <span class="dv">0</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-45"><a href="#cb5-45"></a> gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb5-46"><a href="#cb5-46"></a> <span class="op">}</span> <span class="cf">else</span></span>
<span id="cb5-47"><a href="#cb5-47"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb5-48"><a href="#cb5-48"></a><span class="op">}</span></span></code></pre></div>
<p>The whole source code of <code>tfe3.c</code> is stored in the src/tfe
directory.</p>
<h3 id="using-ui-string">Using ui string</h3>
<p>GtkBuilder can build widgets with string. Use
<code>gtk_builder_new_from_string</code> instead of
<code>gtk_builder_new_from_file</code>.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="dt">char</span> <span class="op">*</span>uistring<span class="op">;</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>uistring <span class="op">=</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;&lt;interface&gt;&quot;</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;object class=&quot;</span>GtkApplicationWindow<span class="st">&quot; id=&quot;</span>win<span class="st">&quot;&gt;&quot;</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;property name=</span><span class="sc">\&quot;</span><span class="st">title</span><span class="sc">\&quot;</span><span class="st">&gt;file editor&lt;/property&gt;&quot;</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;property name=</span><span class="sc">\&quot;</span><span class="st">default-width</span><span class="sc">\&quot;</span><span class="st">&gt;600&lt;/property&gt;&quot;</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;property name=</span><span class="sc">\&quot;</span><span class="st">default-height</span><span class="sc">\&quot;</span><span class="st">&gt;400&lt;/property&gt;&quot;</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;child&gt;&quot;</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;object class=</span><span class="sc">\&quot;</span><span class="st">GtkBox</span><span class="sc">\&quot;</span><span class="st">&gt;&quot;</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;&lt;property name=&quot;</span>orientation<span class="st">&quot;&gt;GTK_ORIENTATION_VERTICAL&lt;/property&gt;&quot;</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a><span class="st">&quot;&lt;/interface&gt;&quot;</span><span class="op">;</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a>build <span class="op">=</span> gtk_builder_new_from_string <span class="op">(</span>uistring<span class="op">,</span> <span class="op">-</span><span class="dv">1</span><span class="op">);</span></span></code></pre></div>
<p>This method has an advantage and disadvantage. The advantage is that
the ui string is written in the source code. So, no ui file is needed on
runtime. The disadvantage is that writing C string is a bit bothersome
because of the double quotes. If you want to use this method, you should
write a script that transforms ui file into C-string.</p>
<ul>
<li>Add backslash before each double quote.</li>
<li>Add double quotes at the left and right of the string in each
line.</li>
</ul>
<h3 id="gresource">Gresource</h3>
<p>Gresource is similar to string. But Gresource is compressed binary
data, not text data. And theres a compiler that compiles ui file into
Gresource. It can compile not only text files but also binary files such
as images, sounds and so on. And after compilation, it bundles them up
into one Gresource object.</p>
<p>An xml file is necessary for the resource compiler
<code>glib-compile-resources</code>. It describes resource files.</p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb7-1"><a href="#cb7-1"></a><span class="fu">&lt;?xml</span><span class="ot"> version=</span><span class="st">&quot;1.0&quot;</span><span class="ot"> encoding=</span><span class="st">&quot;UTF-8&quot;</span><span class="fu">?&gt;</span></span>
<span id="cb7-2"><a href="#cb7-2"></a>&lt;<span class="kw">gresources</span>&gt;</span>
<span id="cb7-3"><a href="#cb7-3"></a> &lt;<span class="kw">gresource</span><span class="ot"> prefix=</span><span class="st">&quot;/com/github/ToshioCP/tfe3&quot;</span>&gt;</span>
<span id="cb7-4"><a href="#cb7-4"></a> &lt;<span class="kw">file</span>&gt;tfe3.ui&lt;/<span class="kw">file</span>&gt;</span>
<span id="cb7-5"><a href="#cb7-5"></a> &lt;/<span class="kw">gresource</span>&gt;</span>
<span id="cb7-6"><a href="#cb7-6"></a>&lt;/<span class="kw">gresources</span>&gt;</span></code></pre></div>
<ul>
<li>2: <code>gresources</code> tag can include multiple gresources
(gresource tags). However, this xml has only one gresource.</li>
<li>3: The gresource has a prefix
<code>/com/github/ToshioCP/tfe3</code>.</li>
<li>4: The name of the gresource is <code>tfe3.ui</code>. The resource
will be pointed with <code>/com/github/ToshioCP/tfe3/tfe3.ui</code> by
GtkBuilder. The pattern is “prefix” + “name”. If you want to add more
files, insert them between line 4 and 5.</li>
</ul>
<p>Save this xml text to <code>tfe3.gresource.xml</code>. The gresource
compiler <code>glib-compile-resources</code> shows its usage with the
argument <code>--help</code>.</p>
<pre><code>$ glib-compile-resources --help
Usage:
glib-compile-resources [OPTION..] FILE
Compile a resource specification into a resource file.
Resource specification files have the extension .gresource.xml,
and the resource file have the extension called .gresource.
Help Options:
-h, --help Show help options
Application Options:
--version Show program version and exit
--target=FILE Name of the output file
--sourcedir=DIRECTORY The directories to load files referenced in FILE from (default: current directory)
--generate Generate output in the format selected for by the target filename extension
--generate-header Generate source header
--generate-source Generate source code used to link in the resource file into your code
--generate-dependencies Generate dependency list
--dependency-file=FILE Name of the dependency file to generate
--generate-phony-targets Include phony targets in the generated dependency file
--manual-register Don&#39;t automatically create and register resource
--internal Don&#39;t export functions; declare them G_GNUC_INTERNAL
--external-data Don&#39;t embed resource data in the C file; assume it&#39;s linked externally instead
--c-name C identifier name used for the generated source code
-C, --compiler The target C compiler (default: the CC environment variable)</code></pre>
<p>Now run the compiler.</p>
<pre><code>$ glib-compile-resources tfe3.gresource.xml --target=resources.c --generate-source</code></pre>
<p>Then a C source file <code>resources.c</code> is generated. Modify
<code>tfe3.c</code> and save it as <code>tfe3_r.c</code>.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&quot;resources.c&quot;</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/tfe3/tfe3.ui&quot;</span><span class="op">);</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a><span class="op">...</span> <span class="op">...</span> <span class="op">...</span></span></code></pre></div>
<p>The function <code>gtk_builder_new_from_resource</code> builds
widgets from a resource.</p>
<p>Then, compile and run it.</p>
<pre><code>$ comp tfe3_r
$ ./a.out tfe2.c</code></pre>
<p>A window appears and it is the same as the screenshot at the
beginning of this page.</p>
<p>Generally, resource is the best way for C language. If you use other
languages like Ruby, string is better than resource.</p>
</div>
</div>
</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>