Update section 24 to 27

This commit is contained in:
Toshio Sekiya 2023-07-23 11:49:22 +09:00
parent 8aba1da141
commit 251f60910f
31 changed files with 1489 additions and 1011 deletions

View file

@ -85,7 +85,7 @@ In short,
1. [Pango, CSS and Application](gfm/sec23.md)
1. [GtkDrawingArea and Cairo](gfm/sec24.md)
1. [Periodic Events](gfm/sec25.md)
1. [Combine GtkDrawingArea and TfeTextView](gfm/sec26.md)
1. [Custom drawing](gfm/sec26.md)
1. [Tiny turtle graphics interpreter](gfm/sec27.md)
1. [GtkListView](gfm/sec28.md)
1. [GtkGridView and activate signal](gfm/sec29.md)

BIN
docs/image/cd0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
docs/image/cd1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
docs/image/cd2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
docs/image/rect.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -190,7 +190,7 @@ destruction</a></li>
<li><a href="sec23.html">Pango, CSS and Application</a></li>
<li><a href="sec24.html">GtkDrawingArea and Cairo</a></li>
<li><a href="sec25.html">Periodic Events</a></li>
<li><a href="sec26.html">Combine GtkDrawingArea and TfeTextView</a></li>
<li><a href="sec26.html">Custom drawing</a></li>
<li><a href="sec27.html">Tiny turtle graphics interpreter</a></li>
<li><a href="sec28.html">GtkListView</a></li>
<li><a href="sec29.html">GtkGridView and activate signal</a></li>

View file

@ -112,28 +112,22 @@
</div>
</nav>
<h1 id="gtkdrawingarea-and-cairo">GtkDrawingArea and Cairo</h1>
<p>This section and following sections are <em>not</em> updated yet and
the programs were checked on the older GTK version than 4.10. They will
be updated in the near future.</p>
<p>If you want to draw dynamically on the screen, like an image window
of gimp graphics editor, the GtkDrawingArea widget is the most suitable
widget. You can freely draw or redraw an image in this widget. This is
called custom drawing.</p>
<p>GtkDrawingArea provides a cairo drawing context so users can draw
images by using cairo functions. In this section, I will explain:</p>
<p>If you want to draw shapes or paint images dynamically on the screen,
use the GtkDrawingArea widget.</p>
<p>GtkDrawingArea provides a cairo drawing context. You can draw images
with cairo library functions. This section describes:</p>
<ol type="1">
<li>Cairo, but only briefly</li>
<li>Cairo, but briefly</li>
<li>GtkDrawingArea, with a very simple example.</li>
</ol>
<h2 id="cairo">Cairo</h2>
<p>Cairo is a set of two dimensional graphical drawing functions (or
graphics library). There are a lot of documents on <a
href="https://www.cairographics.org/">Cairos website</a>. If you arent
familiar with Cairo, it is worth reading the <a
href="https://www.cairographics.org/tutorial/">tutorial</a>.</p>
<p>The following is an introduction to the Cairo library and how to use
it. First, you need to know about surfaces, sources, masks,
destinations, cairo context and transformations.</p>
<p>Cairo is a drawing library for two dimensional graphics. There are a
lot of documents on <a href="https://www.cairographics.org/">Cairos
website</a>. If you arent familiar with Cairo, it is worth reading the
<a href="https://www.cairographics.org/tutorial/">tutorial</a>.</p>
<p>The following is an introduction to the Cairo library. First, you
need to know surfaces, sources, masks, destinations, cairo context and
transformations.</p>
<ul>
<li>A surface represents an image. It is like a canvas. We can draw
shapes and images with different colors on surfaces.</li>
@ -147,8 +141,8 @@ is a function to draw a path to the destination by the transfer.</li>
<li>A transformation can be applied before the transfer completes. The
transformation which is applied is called affine, which is a
mathematical term meaning transofrmations that preserve straight lines.
Scaling, rotating, reflecting, shearing and translating are all examples
of affine transformations. They are mathematically represented by matrix
Scaling, rotating, reflecting, shearing and translating are examples of
affine transformations. They are mathematically represented by matrix
multiplication and vector addition. In this section we dont use it,
instead we will only use the identity transformation. This means that
the coordinates in the source and mask are the same as the coordinates
@ -225,35 +219,32 @@ color depth. Width and height are in pixels and given as integers.</li>
<li>14: Creates cairo context. The surface given as an argument will be
the destination of the context.</li>
<li>18: <code>cairo_set_source_rgb</code> creates a source pattern,
which in this case is a solid white paint. The second to fourth
arguments are red, green and blue color values respectively, and they
are of type float. The values are between zero (0.0) and one (1.0), with
black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).</li>
which is a solid white paint. The second to fourth arguments are red,
green and blue color values respectively, and they are of type float.
The values are between zero (0.0) and one (1.0). Black is (0.0,0.0,0.0)
and white is (1.0,1.0,1.0).</li>
<li>19: <code>cairo_paint</code> copies everywhere in the source to
destination. The destination is filled with white pixels with this
command.</li>
<li>21: Sets the source color to black.</li>
<li>22: <code>cairo_set_line_width</code> set the width of lines. In
<li>22: <code>cairo_set_line_width</code> sets the width of lines. In
this case, the line width is set to be two pixels and will end up that
same size. (It is because the transformation is identity. If the
transformation isnt identity, for example scaling with the factor
three, the actual width in destination will be six (2x3=6) pixels.)</li>
<li>23: Draws a rectangle (square) on the mask. The square is located at
the center.</li>
<li>24: <code>cairo_stroke</code> transfer the source to destination
<li>24: <code>cairo_stroke</code> transfers the source to destination
through the rectangle in the mask.</li>
<li>27: Outputs the image to a png file <code>rectangle.png</code>.</li>
<li>28: Destroys the context. At the same time the source is
<li>31: Outputs the image to a png file <code>rectangle.png</code>.</li>
<li>32: Destroys the context. At the same time the source is
destroyed.</li>
<li>29: Destroys the surface.</li>
<li>33: Destroys the surface.</li>
</ul>
<p>To compile this, change your current directory to
<code>src/misc</code> and type the following.</p>
<pre><code>$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`</code></pre>
<figure>
<img src="image/rectangle.png" alt="rectangle.png" />
<figcaption aria-hidden="true">rectangle.png</figcaption>
</figure>
<p>s <img src="image/rectangle.png" alt="rectangle.png" /></p>
<p>See the <a href="https://www.cairographics.org/">Cairos website</a>
for further information.</p>
<h2 id="gtkdrawingarea">GtkDrawingArea</h2>
@ -308,10 +299,10 @@ important in this example.</p>
<ul>
<li>22: Creates a GtkDrawingArea instance.</li>
<li>25: Sets a drawing function of the widget. GtkDrawingArea widget
uses the function to draw the contents of itself whenever its necessary.
For example, when a user drag a mouse pointer and resize a top-level
window, GtkDrawingArea also changes the size. Then, the whole window
needs to be redrawn. For the information of
uses the function <code>draw_function</code> to draw the contents of
itself whenever its necessary. For example, when a user drag a mouse
pointer and resize a top-level window, GtkDrawingArea also changes the
size. Then, the whole window needs to be redrawn. For the information of
<code>gtk_drawing_area_set_draw_func</code>, see <a
href="https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html">Gtk
API Reference gtk_drawing_area_set_draw_func</a>.</li>

View file

@ -279,7 +279,7 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
<span id="cb2-13"><a href="#cb2-13"></a></span>
<span id="cb2-14"><a href="#cb2-14"></a> gtk_drawing_area_set_draw_func<span class="op">(</span>GTK_DRAWING_AREA <span class="op">(</span>clock<span class="op">),</span> draw_clock<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb2-15"><a href="#cb2-15"></a> g_timeout_add<span class="op">(</span><span class="dv">1000</span><span class="op">,</span> <span class="op">(</span>GSourceFunc<span class="op">)</span> time_handler<span class="op">,</span> <span class="op">(</span>gpointer<span class="op">)</span> clock<span class="op">);</span></span>
<span id="cb2-16"><a href="#cb2-16"></a> gtk_widget_show<span class="op">(</span>win<span class="op">);</span></span>
<span id="cb2-16"><a href="#cb2-16"></a> gtk_window_present<span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb2-17"><a href="#cb2-17"></a></span>
<span id="cb2-18"><a href="#cb2-18"></a><span class="op">}</span></span></code></pre></div>
<p>Our <code>time_handler()</code> function is very simple, as it just
@ -448,7 +448,7 @@ class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span i
<span id="cb4-143"><a href="#cb4-143"></a></span>
<span id="cb4-144"><a href="#cb4-144"></a> gtk_drawing_area_set_draw_func<span class="op">(</span>GTK_DRAWING_AREA <span class="op">(</span>clock<span class="op">),</span> draw_clock<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb4-145"><a href="#cb4-145"></a> g_timeout_add<span class="op">(</span><span class="dv">1000</span><span class="op">,</span> <span class="op">(</span>GSourceFunc<span class="op">)</span> time_handler<span class="op">,</span> <span class="op">(</span>gpointer<span class="op">)</span> clock<span class="op">);</span></span>
<span id="cb4-146"><a href="#cb4-146"></a> gtk_widget_show<span class="op">(</span>win<span class="op">);</span></span>
<span id="cb4-146"><a href="#cb4-146"></a> gtk_window_present<span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb4-147"><a href="#cb4-147"></a></span>
<span id="cb4-148"><a href="#cb4-148"></a><span class="op">}</span></span>
<span id="cb4-149"><a href="#cb4-149"></a></span>

View file

@ -111,423 +111,354 @@
</div>
</div>
</nav>
<h1 id="combine-gtkdrawingarea-and-tfetextview">Combine GtkDrawingArea
and TfeTextView</h1>
<p>Now, we will make a new application which has GtkDrawingArea and
TfeTextView in it. Its name is “color”. If you write a name of a color
in TfeTextView and click on the <code>run</code> button, then the color
of GtkDrawingArea changes to the color given by you.</p>
<h1 id="custom-drawing">Custom drawing</h1>
<p>Custom drawing is to draw shapes dynamically. This section shows an
example of custom drawing. You can draw rectangles by dragging the
mouse.</p>
<p>Down the button.</p>
<figure>
<img src="image/color.png" alt="color" />
<figcaption aria-hidden="true">color</figcaption>
<img src="image/cd0.png" alt="down the button" />
<figcaption aria-hidden="true">down the button</figcaption>
</figure>
<p>The following colors are available. (without new line charactor)</p>
<p>Move the mouse</p>
<figure>
<img src="image/cd1.png" alt="Move the mouse" />
<figcaption aria-hidden="true">Move the mouse</figcaption>
</figure>
<p>Up the button.</p>
<figure>
<img src="image/cd2.png" alt="Up the button" />
<figcaption aria-hidden="true">Up the button</figcaption>
</figure>
<p>The programs are at <code>src/custom_drawing</code> directory.
Download the <a
href="https://github.com/ToshioCP/Gtk4-tutorial">repository</a> and see
the directory. There are four files.</p>
<ul>
<li>white</li>
<li>black</li>
<li>red</li>
<li>green</li>
<li>blue</li>
<li>meson.build</li>
<li>rect.c</li>
<li>rect.gresource.xml</li>
<li>rect.ui</li>
</ul>
<p>In addition the following two options are also available.</p>
<ul>
<li>light: Make the color of the drawing area lighter.</li>
<li>dark: Make the color of the drawing area darker.</li>
</ul>
<p>This application can only do very simple things. However, it tells us
that if we add powerful parser to it, we will be able to make it more
efficient. I want to show it to you in the later section by making a
turtle graphics language like Logo program language.</p>
<p>In this section, we focus on how to bind the two objects.</p>
<h2 id="color.ui-and-color.gresource.xml">Color.ui and
color.gresource.xml</h2>
<p>First, We need to make the ui file of the widgets. Title bar, four
buttons in the tool bar, textview and drawing area. The ui file is as
follows.</p>
<h2 id="rect.gresource.xml">rect.gresource.xml</h2>
<p>This file describes a ui file to compile. The compiler
glib-compile-resources uses it.</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode numberSource xml numberLines"><code class="sourceCode xml"><span id="cb1-1"><a href="#cb1-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="cb1-2"><a href="#cb1-2"></a>&lt;<span class="kw">interface</span>&gt;</span>
<span id="cb1-3"><a href="#cb1-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="cb1-4"><a href="#cb1-4"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;title&quot;</span>&gt;color changer&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb1-5"><a href="#cb1-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="cb1-6"><a href="#cb1-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="cb1-7"><a href="#cb1-7"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-8"><a href="#cb1-8"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span><span class="ot"> id=</span><span class="st">&quot;boxv&quot;</span>&gt;</span>
<span id="cb1-9"><a href="#cb1-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="cb1-10"><a href="#cb1-10"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-11"><a href="#cb1-11"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span><span class="ot"> id=</span><span class="st">&quot;boxh1&quot;</span>&gt;</span>
<span id="cb1-12"><a href="#cb1-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="cb1-13"><a href="#cb1-13"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-14"><a href="#cb1-14"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;dmy1&quot;</span>&gt;</span>
<span id="cb1-15"><a href="#cb1-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="cb1-16"><a href="#cb1-16"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-17"><a href="#cb1-17"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-18"><a href="#cb1-18"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-19"><a href="#cb1-19"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btnr&quot;</span>&gt;</span>
<span id="cb1-20"><a href="#cb1-20"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;label&quot;</span>&gt;Run&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb1-21"><a href="#cb1-21"></a> &lt;<span class="kw">signal</span><span class="ot"> name=</span><span class="st">&quot;clicked&quot;</span><span class="ot"> handler=</span><span class="st">&quot;run_cb&quot;</span>&gt;&lt;/<span class="kw">signal</span>&gt;</span>
<span id="cb1-22"><a href="#cb1-22"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-23"><a href="#cb1-23"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-24"><a href="#cb1-24"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-25"><a href="#cb1-25"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btno&quot;</span>&gt;</span>
<span id="cb1-26"><a href="#cb1-26"></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="cb1-27"><a href="#cb1-27"></a> &lt;<span class="kw">signal</span><span class="ot"> name=</span><span class="st">&quot;clicked&quot;</span><span class="ot"> handler=</span><span class="st">&quot;open_cb&quot;</span>&gt;&lt;/<span class="kw">signal</span>&gt;</span>
<span id="cb1-28"><a href="#cb1-28"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-29"><a href="#cb1-29"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-30"><a href="#cb1-30"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-31"><a href="#cb1-31"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;dmy2&quot;</span>&gt;</span>
<span id="cb1-32"><a href="#cb1-32"></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="cb1-33"><a href="#cb1-33"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-34"><a href="#cb1-34"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-35"><a href="#cb1-35"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-36"><a href="#cb1-36"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btns&quot;</span>&gt;</span>
<span id="cb1-37"><a href="#cb1-37"></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="cb1-38"><a href="#cb1-38"></a> &lt;<span class="kw">signal</span><span class="ot"> name=</span><span class="st">&quot;clicked&quot;</span><span class="ot"> handler=</span><span class="st">&quot;save_cb&quot;</span>&gt;&lt;/<span class="kw">signal</span>&gt;</span>
<span id="cb1-39"><a href="#cb1-39"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-40"><a href="#cb1-40"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-41"><a href="#cb1-41"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-42"><a href="#cb1-42"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkButton&quot;</span><span class="ot"> id=</span><span class="st">&quot;btnc&quot;</span>&gt;</span>
<span id="cb1-43"><a href="#cb1-43"></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="cb1-44"><a href="#cb1-44"></a> &lt;<span class="kw">signal</span><span class="ot"> name=</span><span class="st">&quot;clicked&quot;</span><span class="ot"> handler=</span><span class="st">&quot;close_cb&quot;</span>&gt;&lt;/<span class="kw">signal</span>&gt;</span>
<span id="cb1-45"><a href="#cb1-45"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-46"><a href="#cb1-46"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-47"><a href="#cb1-47"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-48"><a href="#cb1-48"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkLabel&quot;</span><span class="ot"> id=</span><span class="st">&quot;dmy3&quot;</span>&gt;</span>
<span id="cb1-49"><a href="#cb1-49"></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="cb1-50"><a href="#cb1-50"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-51"><a href="#cb1-51"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-52"><a href="#cb1-52"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-53"><a href="#cb1-53"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-54"><a href="#cb1-54"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-55"><a href="#cb1-55"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkBox&quot;</span><span class="ot"> id=</span><span class="st">&quot;boxh2&quot;</span>&gt;</span>
<span id="cb1-56"><a href="#cb1-56"></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="cb1-57"><a href="#cb1-57"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;homogeneous&quot;</span>&gt;TRUE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb1-58"><a href="#cb1-58"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-59"><a href="#cb1-59"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkScrolledWindow&quot;</span><span class="ot"> id=</span><span class="st">&quot;scr&quot;</span>&gt;</span>
<span id="cb1-60"><a href="#cb1-60"></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="cb1-61"><a href="#cb1-61"></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="cb1-62"><a href="#cb1-62"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-63"><a href="#cb1-63"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;TfeTextView&quot;</span><span class="ot"> id=</span><span class="st">&quot;tv&quot;</span>&gt;</span>
<span id="cb1-64"><a href="#cb1-64"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;wrap-mode&quot;</span>&gt;GTK_WRAP_WORD_CHAR&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb1-65"><a href="#cb1-65"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-66"><a href="#cb1-66"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-67"><a href="#cb1-67"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-68"><a href="#cb1-68"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-69"><a href="#cb1-69"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb1-70"><a href="#cb1-70"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkDrawingArea&quot;</span><span class="ot"> id=</span><span class="st">&quot;da&quot;</span>&gt;</span>
<span id="cb1-71"><a href="#cb1-71"></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="cb1-72"><a href="#cb1-72"></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="cb1-73"><a href="#cb1-73"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-74"><a href="#cb1-74"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-75"><a href="#cb1-75"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-76"><a href="#cb1-76"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-77"><a href="#cb1-77"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-78"><a href="#cb1-78"></a> &lt;/<span class="kw">child</span>&gt;</span>
<span id="cb1-79"><a href="#cb1-79"></a> &lt;/<span class="kw">object</span>&gt;</span>
<span id="cb1-80"><a href="#cb1-80"></a>&lt;/<span class="kw">interface</span>&gt;</span></code></pre></div>
<ul>
<li>10-53: The horizontal box <code>boxh1</code> makes a tool bar which
has four buttons, <code>Run</code>, <code>Open</code>, <code>Save</code>
and <code>Close</code>. This is similar to the <code>tfe</code> text
editor in <a href="sec9.html">Section 9</a>. There are two differences.
<code>Run</code> button replaces <code>New</code> button. A signal
element is added to each button object. It has “name” attribute which is
a signal name and “handler” attribute which is the name of its signal
handler. Options “-WI, export-dynamic” CFLAG is necessary when you
compile the application. You can achieve this by adding “export_dynamic:
true” argument to the executable function in <code>meson.build</code>.
And be careful that the handler must be defined without static
class.</li>
<li>54-76: The horizontal box <code>boxh2</code> includes
GtkScrolledWindow and GtkDrawingArea. GtkBox has “homogeneous property”
with TRUE value, so the two children have the same width in the box.
TfeTextView is a child of GtkScrolledWindow.</li>
</ul>
<p>The xml file for the resource compiler is almost same as before. Just
substitute “color” for “tfe”.</p>
<span id="cb1-2"><a href="#cb1-2"></a>&lt;<span class="kw">gresources</span>&gt;</span>
<span id="cb1-3"><a href="#cb1-3"></a> &lt;<span class="kw">gresource</span><span class="ot"> prefix=</span><span class="st">&quot;/com/github/ToshioCP/rect&quot;</span>&gt;</span>
<span id="cb1-4"><a href="#cb1-4"></a> &lt;<span class="kw">file</span>&gt;rect.ui&lt;/<span class="kw">file</span>&gt;</span>
<span id="cb1-5"><a href="#cb1-5"></a> &lt;/<span class="kw">gresource</span>&gt;</span>
<span id="cb1-6"><a href="#cb1-6"></a>&lt;/<span class="kw">gresources</span>&gt;</span></code></pre></div>
<p>The prefix is <code>/com/github/ToshioCP/rect</code> and the file is
<code>rect.ui</code>. Therefore, GtkBuilder reads the resource from
<code>/com/github/ToshioCP/rect/rect.ui</code>.</p>
<h2 id="rect.ui">rect.ui</h2>
<p>The following is the ui file that defines the widgets. There are two
widgets which are GtkApplicationWindow and GtkDrawingArea. The ids are
<code>win</code> and <code>da</code> respectively.</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">gresources</span>&gt;</span>
<span id="cb2-3"><a href="#cb2-3"></a> &lt;<span class="kw">gresource</span><span class="ot"> prefix=</span><span class="st">&quot;/com/github/ToshioCP/color&quot;</span>&gt;</span>
<span id="cb2-4"><a href="#cb2-4"></a> &lt;<span class="kw">file</span>&gt;color.ui&lt;/<span class="kw">file</span>&gt;</span>
<span id="cb2-5"><a href="#cb2-5"></a> &lt;/<span class="kw">gresource</span>&gt;</span>
<span id="cb2-6"><a href="#cb2-6"></a>&lt;/<span class="kw">gresources</span>&gt;</span></code></pre></div>
<h2 id="drawing-function-and-surface">Drawing function and surface</h2>
<p>The main point of this program is a drawing function.</p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb3-2"><a href="#cb3-2"></a>draw_func <span class="op">(</span>GtkDrawingArea <span class="op">*</span>drawing_area<span class="op">,</span> cairo_t <span class="op">*</span>cr<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-3"><a href="#cb3-3"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-4"><a href="#cb3-4"></a> cairo_set_source_surface <span class="op">(</span>cr<span class="op">,</span> surface<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb3-5"><a href="#cb3-5"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb3-6"><a href="#cb3-6"></a> <span class="op">}</span></span>
<span id="cb3-7"><a href="#cb3-7"></a><span class="op">}</span></span></code></pre></div>
<p>The <code>surface</code> variable in line 3 is a static variable.</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" tabindex="-1"></a><span class="dt">static</span> cairo_surface_t <span class="op">*</span>surface <span class="op">=</span> NULL<span class="op">;</span></span></code></pre></div>
<p>The drawing function just copies the <code>surface</code> to its own
surface with the <code>cairo_paint</code> function. The surface (pointed
by the static variable <code>surface</code>) is built by the
<code>run</code> function.</p>
<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;default-width&quot;</span>&gt;800&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-height&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;resizable&quot;</span>&gt;FALSE&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-7"><a href="#cb2-7"></a> &lt;<span class="kw">property</span><span class="ot"> name=</span><span class="st">&quot;title&quot;</span>&gt;Custom drawing&lt;/<span class="kw">property</span>&gt;</span>
<span id="cb2-8"><a href="#cb2-8"></a> &lt;<span class="kw">child</span>&gt;</span>
<span id="cb2-9"><a href="#cb2-9"></a> &lt;<span class="kw">object</span><span class="ot"> class=</span><span class="st">&quot;GtkDrawingArea&quot;</span><span class="ot"> id=</span><span class="st">&quot;da&quot;</span>&gt;</span>
<span id="cb2-10"><a href="#cb2-10"></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-11"><a href="#cb2-11"></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-12"><a href="#cb2-12"></a> &lt;/<span class="kw">object</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>&gt;</span>
<span id="cb2-15"><a href="#cb2-15"></a>&lt;/<span class="kw">interface</span>&gt;</span></code></pre></div>
<h2 id="rect.c">rect.c</h2>
<h3 id="gtkapplication">GtkApplication</h3>
<p>This program uses GtkApplication. The application ID is
<code>com.github.ToshioCP.rect</code>.</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><span class="pp">#define APPLICATION_ID &quot;com.github.ToshioCP.rect&quot;</span></span></code></pre></div>
<p>See <a
href="https://developer.gnome.org/documentation/tutorials/application-id.html">GNOME
Developer Documentation</a> for further information.</p>
<p>The function <code>main</code> is called at the beginning of the
application.</p>
<div class="sourceCode" id="cb4"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1"></a><span class="dt">int</span></span>
<span id="cb4-2"><a href="#cb4-2"></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="cb4-3"><a href="#cb4-3"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb4-4"><a href="#cb4-4"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb4-5"><a href="#cb4-5"></a></span>
<span id="cb4-6"><a href="#cb4-6"></a> app <span class="op">=</span> gtk_application_new <span class="op">(</span>APPLICATION_ID<span class="op">,</span> G_APPLICATION_HANDLES_OPEN<span class="op">);</span></span>
<span id="cb4-7"><a href="#cb4-7"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;startup&quot;</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="cb4-8"><a href="#cb4-8"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</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="cb4-9"><a href="#cb4-9"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;shutdown&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_shutdown<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb4-10"><a href="#cb4-10"></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="cb4-11"><a href="#cb4-11"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb4-12"><a href="#cb4-12"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb4-13"><a href="#cb4-13"></a><span class="op">}</span></span></code></pre></div>
<p>It connects three signals and handlers.</p>
<ul>
<li>startup: It is emitted after the application is registered to the
system.</li>
<li>activate: It is emitted when the application is activated.</li>
<li>shutdown: It is emitted just before the application quits.</li>
</ul>
<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>run <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3"></a> GtkTextBuffer <span class="op">*</span>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-4"><a href="#cb5-4"></a> GtkTextIter start_iter<span class="op">;</span></span>
<span id="cb5-5"><a href="#cb5-5"></a> GtkTextIter end_iter<span class="op">;</span></span>
<span id="cb5-6"><a href="#cb5-6"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb5-7"><a href="#cb5-7"></a> cairo_t <span class="op">*</span>cr<span class="op">;</span></span>
<span id="cb5-2"><a href="#cb5-2"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3"></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="cb5-4"><a href="#cb5-4"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb5-5"><a href="#cb5-5"></a> GtkWindow <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb5-6"><a href="#cb5-6"></a> GtkDrawingArea <span class="op">*</span>da<span class="op">;</span></span>
<span id="cb5-7"><a href="#cb5-7"></a> GtkGesture <span class="op">*</span>drag<span class="op">;</span></span>
<span id="cb5-8"><a href="#cb5-8"></a></span>
<span id="cb5-9"><a href="#cb5-9"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">);</span></span>
<span id="cb5-10"><a href="#cb5-10"></a> contents <span class="op">=</span> gtk_text_buffer_get_text <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">,</span> FALSE<span class="op">);</span></span>
<span id="cb5-11"><a href="#cb5-11"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-12"><a href="#cb5-12"></a> cr <span class="op">=</span> cairo_create <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb5-13"><a href="#cb5-13"></a> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;red&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-14"><a href="#cb5-14"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb5-15"><a href="#cb5-15"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;green&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-16"><a href="#cb5-16"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb5-17"><a href="#cb5-17"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;blue&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-18"><a href="#cb5-18"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb5-19"><a href="#cb5-19"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;white&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-20"><a href="#cb5-20"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb5-21"><a href="#cb5-21"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;black&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-22"><a href="#cb5-22"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb5-23"><a href="#cb5-23"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;light&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-24"><a href="#cb5-24"></a> cairo_set_source_rgba <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="fl">0.5</span><span class="op">);</span></span>
<span id="cb5-25"><a href="#cb5-25"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;dark&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb5-26"><a href="#cb5-26"></a> cairo_set_source_rgba <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="fl">0.5</span><span class="op">);</span></span>
<span id="cb5-27"><a href="#cb5-27"></a> <span class="cf">else</span></span>
<span id="cb5-28"><a href="#cb5-28"></a> cairo_set_source_surface <span class="op">(</span>cr<span class="op">,</span> surface<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb5-29"><a href="#cb5-29"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb5-30"><a href="#cb5-30"></a> cairo_destroy <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb5-31"><a href="#cb5-31"></a> <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><span class="op">}</span></span></code></pre></div>
<span id="cb5-9"><a href="#cb5-9"></a> build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/rect/rect.ui&quot;</span><span class="op">);</span></span>
<span id="cb5-10"><a href="#cb5-10"></a> win <span class="op">=</span> GTK_WINDOW <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-11"><a href="#cb5-11"></a> da <span class="op">=</span> GTK_DRAWING_AREA <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;da&quot;</span><span class="op">));</span></span>
<span id="cb5-12"><a href="#cb5-12"></a> gtk_window_set_application <span class="op">(</span>win<span class="op">,</span> app<span class="op">);</span></span>
<span id="cb5-13"><a href="#cb5-13"></a> g_object_unref <span class="op">(</span>build<span class="op">);</span></span>
<span id="cb5-14"><a href="#cb5-14"></a></span>
<span id="cb5-15"><a href="#cb5-15"></a> gtk_drawing_area_set_draw_func <span class="op">(</span>da<span class="op">,</span> draw_cb<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb5-16"><a href="#cb5-16"></a> g_signal_connect_after <span class="op">(</span>da<span class="op">,</span> <span class="st">&quot;resize&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>resize_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb5-17"><a href="#cb5-17"></a></span>
<span id="cb5-18"><a href="#cb5-18"></a> drag <span class="op">=</span> gtk_gesture_drag_new <span class="op">();</span></span>
<span id="cb5-19"><a href="#cb5-19"></a> gtk_gesture_single_set_button <span class="op">(</span>GTK_GESTURE_SINGLE <span class="op">(</span>drag<span class="op">),</span> GDK_BUTTON_PRIMARY<span class="op">);</span></span>
<span id="cb5-20"><a href="#cb5-20"></a> gtk_widget_add_controller <span class="op">(</span>GTK_WIDGET <span class="op">(</span>da<span class="op">),</span> GTK_EVENT_CONTROLLER <span class="op">(</span>drag<span class="op">));</span></span>
<span id="cb5-21"><a href="#cb5-21"></a> g_signal_connect <span class="op">(</span>drag<span class="op">,</span> <span class="st">&quot;drag-begin&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>drag_begin<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb5-22"><a href="#cb5-22"></a> g_signal_connect <span class="op">(</span>drag<span class="op">,</span> <span class="st">&quot;drag-update&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>drag_update<span class="op">),</span> da<span class="op">);</span></span>
<span id="cb5-23"><a href="#cb5-23"></a> g_signal_connect <span class="op">(</span>drag<span class="op">,</span> <span class="st">&quot;drag-end&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>drag_end<span class="op">),</span> da<span class="op">);</span></span>
<span id="cb5-24"><a href="#cb5-24"></a><span class="op">}</span></span></code></pre></div>
<p>The startup handler does three things.</p>
<ul>
<li>9-10: Gets the string in the GtkTextBuffer and inserts it to
<code>contents</code>.</li>
<li>11: If the variable <code>surface</code> points a surface instance,
it is painted as follows.</li>
<li>12- 30: The source is set based on the string <code>contents</code>
and copied to the surface with <code>cairo_paint</code>.</li>
<li>24,26: Alpha channel is used in “light” and “dark” procedure.</li>
</ul>
<p>The drawing area just reflects the <code>surface</code>. But one
problem is resizing. If a user resizes the main window, the drawing area
is also resized. It makes size difference between the surface and the
drawing area. So, the surface needs to be resized to fit the drawing
area.</p>
<p>It is accomplished by connecting the “resize” signal on the drawing
area to a handler.</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>g_signal_connect <span class="op">(</span>GTK_DRAWING_AREA <span class="op">(</span>da<span class="op">),</span> <span class="st">&quot;resize&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>resize_cb<span class="op">),</span> NULL<span class="op">);</span></span></code></pre></div>
<p>The handler is as follows.</p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb7-2"><a href="#cb7-2"></a>resize_cb <span class="op">(</span>GtkDrawingArea <span class="op">*</span>drawing_area<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-3"><a href="#cb7-3"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span></span>
<span id="cb7-4"><a href="#cb7-4"></a> cairo_surface_destroy <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb7-5"><a href="#cb7-5"></a> surface <span class="op">=</span> cairo_image_surface_create <span class="op">(</span>CAIRO_FORMAT_ARGB32<span class="op">,</span> width<span class="op">,</span> height<span class="op">);</span></span>
<span id="cb7-6"><a href="#cb7-6"></a> run <span class="op">();</span></span>
<span id="cb7-7"><a href="#cb7-7"></a><span class="op">}</span></span></code></pre></div>
<p>If the variable <code>surface</code> sets a surface instance, it is
destroyed. A new surface is created and its size fits the drawing area.
The surface is assigned to the variable <code>surface</code>. The
function <code>run</code> is called and the surface is colored.</p>
<p>The signal is emitted when:</p>
<li>Builds the widgets.</li>
<li>Initializes the GtkDrawingArea instance.
<ul>
<li>The drawing area is realized (it appears on the display).</li>
<li>It is changed (resized) while realized</li>
<li>Sets the drawing function</li>
<li>Connects the “resize” signal and the handler.</li>
</ul></li>
<li>Creates the GtkGestureDrag instance and initializes it. Gesture will
be explained in this section later.</li>
</ul>
<p>So, the first surface is created when it is realized.</p>
<h2 id="colorapplication.c">Colorapplication.c</h2>
<p>This is the main file.</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="dt">static</span> <span class="dt">void</span></span>
<span id="cb6-2"><a href="#cb6-2"></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-3"><a href="#cb6-3"></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-4"><a href="#cb6-4"></a> GtkWindow <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb6-5"><a href="#cb6-5"></a></span>
<span id="cb6-6"><a href="#cb6-6"></a> win <span class="op">=</span> gtk_application_get_active_window <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb6-7"><a href="#cb6-7"></a> gtk_window_present <span class="op">(</span>win<span class="op">);</span></span>
<span id="cb6-8"><a href="#cb6-8"></a><span class="op">}</span></span></code></pre></div>
<p>The activate handler just shows the window.</p>
<h3 id="gtkdrawingarea">GtkDrawingArea</h3>
<p>The program has two cairo surfaces and they are pointed by the global
variables.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> cairo_surface_t <span class="op">*</span>surface <span class="op">=</span> NULL<span class="op">;</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> cairo_surface_t <span class="op">*</span>surface_save <span class="op">=</span> NULL<span class="op">;</span></span></code></pre></div>
<p>The drawing process is as follows.</p>
<ul>
<li>Builds widgets by GtkBuilder.</li>
<li>Sets a drawing function for GtkDrawingArea. And connects a handler
to the “resize” signal on the GtkDrawingArea instance.</li>
<li>Implements each call back function. Particularly, <code>Run</code>
signal handler is the point in this program.</li>
<li>Creates an image on <code>surface</code>.</li>
<li>Copies <code>surface</code> to the cairo surface of the
GtkDrawingArea.</li>
<li>Calls <code>gtk_widget_queue_draw (da)</code> to draw it if
necessary.</li>
</ul>
<p>The following is <code>colorapplication.c</code>.</p>
<p>They are created in the “resize” signal handler.</p>
<div class="sourceCode" id="cb8"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="pp">#include </span><span class="im">&lt;gtk/gtk.h&gt;</span></span>
<span id="cb8-2"><a href="#cb8-2"></a><span class="pp">#include </span><span class="im">&quot;../tfetextview/tfetextview.h&quot;</span></span>
<span id="cb8-3"><a href="#cb8-3"></a></span>
<span id="cb8-4"><a href="#cb8-4"></a><span class="dt">static</span> GtkWidget <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb8-5"><a href="#cb8-5"></a><span class="dt">static</span> GtkWidget <span class="op">*</span>tv<span class="op">;</span></span>
<span id="cb8-6"><a href="#cb8-6"></a><span class="dt">static</span> GtkWidget <span class="op">*</span>da<span class="op">;</span></span>
<span id="cb8-7"><a href="#cb8-7"></a></span>
<span id="cb8-8"><a href="#cb8-8"></a><span class="dt">static</span> cairo_surface_t <span class="op">*</span>surface <span class="op">=</span> NULL<span class="op">;</span></span>
<span id="cb8-9"><a href="#cb8-9"></a></span>
<span id="cb8-10"><a href="#cb8-10"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-11"><a href="#cb8-11"></a>run <span class="op">(</span><span class="dt">void</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-12"><a href="#cb8-12"></a> GtkTextBuffer <span class="op">*</span>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="cb8-13"><a href="#cb8-13"></a> GtkTextIter start_iter<span class="op">;</span></span>
<span id="cb8-14"><a href="#cb8-14"></a> GtkTextIter end_iter<span class="op">;</span></span>
<span id="cb8-15"><a href="#cb8-15"></a> <span class="dt">char</span> <span class="op">*</span>contents<span class="op">;</span></span>
<span id="cb8-16"><a href="#cb8-16"></a> cairo_t <span class="op">*</span>cr<span class="op">;</span></span>
<span id="cb8-17"><a href="#cb8-17"></a></span>
<span id="cb8-18"><a href="#cb8-18"></a> gtk_text_buffer_get_bounds <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">);</span></span>
<span id="cb8-19"><a href="#cb8-19"></a> contents <span class="op">=</span> gtk_text_buffer_get_text <span class="op">(</span>tb<span class="op">,</span> <span class="op">&amp;</span>start_iter<span class="op">,</span> <span class="op">&amp;</span>end_iter<span class="op">,</span> FALSE<span class="op">);</span></span>
<span id="cb8-20"><a href="#cb8-20"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-21"><a href="#cb8-21"></a> cr <span class="op">=</span> cairo_create <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb8-22"><a href="#cb8-22"></a> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;red&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb8-23"><a href="#cb8-23"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb8-24"><a href="#cb8-24"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;green&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb8-25"><a href="#cb8-25"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb8-26"><a href="#cb8-26"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;blue&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb8-27"><a href="#cb8-27"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb8-28"><a href="#cb8-28"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;white&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb8-29"><a href="#cb8-29"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb8-30"><a href="#cb8-30"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;black&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb8-31"><a href="#cb8-31"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb8-32"><a href="#cb8-32"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;light&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb8-33"><a href="#cb8-33"></a> cairo_set_source_rgba <span class="op">(</span>cr<span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="fl">0.5</span><span class="op">);</span></span>
<span id="cb8-34"><a href="#cb8-34"></a> <span class="cf">else</span> <span class="cf">if</span> <span class="op">(</span>g_strcmp0 <span class="op">(</span><span class="st">&quot;dark&quot;</span><span class="op">,</span> contents<span class="op">)</span> <span class="op">==</span> <span class="dv">0</span><span class="op">)</span></span>
<span id="cb8-35"><a href="#cb8-35"></a> cairo_set_source_rgba <span class="op">(</span>cr<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="fl">0.5</span><span class="op">);</span></span>
<span id="cb8-36"><a href="#cb8-36"></a> <span class="cf">else</span></span>
<span id="cb8-37"><a href="#cb8-37"></a> cairo_set_source_surface <span class="op">(</span>cr<span class="op">,</span> surface<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb8-38"><a href="#cb8-38"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb8-39"><a href="#cb8-39"></a> cairo_destroy <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb8-40"><a href="#cb8-40"></a> <span class="op">}</span></span>
<span id="cb8-41"><a href="#cb8-41"></a> g_free <span class="op">(</span>contents<span class="op">);</span></span>
<span id="cb8-42"><a href="#cb8-42"></a><span class="op">}</span></span>
<span id="cb8-43"><a href="#cb8-43"></a></span>
<span id="cb8-44"><a href="#cb8-44"></a><span class="dt">void</span></span>
<span id="cb8-45"><a href="#cb8-45"></a>run_cb <span class="op">(</span>GtkWidget <span class="op">*</span>btnr<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-46"><a href="#cb8-46"></a> run <span class="op">();</span></span>
<span id="cb8-47"><a href="#cb8-47"></a> gtk_widget_queue_draw <span class="op">(</span>GTK_WIDGET <span class="op">(</span>da<span class="op">));</span></span>
<span id="cb8-48"><a href="#cb8-48"></a><span class="op">}</span></span>
<span id="cb8-49"><a href="#cb8-49"></a></span>
<span id="cb8-50"><a href="#cb8-50"></a><span class="dt">void</span></span>
<span id="cb8-51"><a href="#cb8-51"></a>open_cb <span class="op">(</span>GtkWidget <span class="op">*</span>btno<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-52"><a href="#cb8-52"></a> tfe_text_view_open <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">),</span> GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb8-53"><a href="#cb8-53"></a><span class="op">}</span></span>
<span id="cb8-54"><a href="#cb8-54"></a></span>
<span id="cb8-55"><a href="#cb8-55"></a><span class="dt">void</span></span>
<span id="cb8-56"><a href="#cb8-56"></a>save_cb <span class="op">(</span>GtkWidget <span class="op">*</span>btns<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-57"><a href="#cb8-57"></a> tfe_text_view_save <span class="op">(</span>TFE_TEXT_VIEW <span class="op">(</span>tv<span class="op">));</span></span>
<span id="cb8-58"><a href="#cb8-58"></a><span class="op">}</span></span>
<span id="cb8-59"><a href="#cb8-59"></a></span>
<span id="cb8-60"><a href="#cb8-60"></a><span class="dt">void</span></span>
<span id="cb8-61"><a href="#cb8-61"></a>close_cb <span class="op">(</span>GtkWidget <span class="op">*</span>btnc<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-62"><a href="#cb8-62"></a> gtk_window_destroy <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb8-63"><a href="#cb8-63"></a><span class="op">}</span></span>
<span id="cb8-64"><a href="#cb8-64"></a></span>
<span id="cb8-65"><a href="#cb8-65"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-66"><a href="#cb8-66"></a>resize_cb <span class="op">(</span>GtkDrawingArea <span class="op">*</span>drawing_area<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-67"><a href="#cb8-67"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span></span>
<span id="cb8-68"><a href="#cb8-68"></a> cairo_surface_destroy <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb8-69"><a href="#cb8-69"></a> surface <span class="op">=</span> cairo_image_surface_create <span class="op">(</span>CAIRO_FORMAT_ARGB32<span class="op">,</span> width<span class="op">,</span> height<span class="op">);</span></span>
<span id="cb8-70"><a href="#cb8-70"></a> run <span class="op">();</span></span>
<span id="cb8-71"><a href="#cb8-71"></a><span class="op">}</span></span>
<span id="cb8-72"><a href="#cb8-72"></a></span>
<span id="cb8-73"><a href="#cb8-73"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-74"><a href="#cb8-74"></a>draw_func <span class="op">(</span>GtkDrawingArea <span class="op">*</span>drawing_area<span class="op">,</span> cairo_t <span class="op">*</span>cr<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-75"><a href="#cb8-75"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-76"><a href="#cb8-76"></a> cairo_set_source_surface <span class="op">(</span>cr<span class="op">,</span> surface<span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb8-77"><a href="#cb8-77"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb8-78"><a href="#cb8-78"></a> <span class="op">}</span></span>
<span id="cb8-79"><a href="#cb8-79"></a><span class="op">}</span></span>
<span id="cb8-80"><a href="#cb8-80"></a></span>
<span id="cb8-81"><a href="#cb8-81"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-82"><a href="#cb8-82"></a>app_activate <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-83"><a href="#cb8-83"></a> gtk_window_present <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">));</span></span>
<span id="cb8-84"><a href="#cb8-84"></a><span class="op">}</span></span>
<span id="cb8-85"><a href="#cb8-85"></a></span>
<span id="cb8-86"><a href="#cb8-86"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-87"><a href="#cb8-87"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-88"><a href="#cb8-88"></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="cb8-89"><a href="#cb8-89"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb8-90"><a href="#cb8-90"></a> GdkDisplay <span class="op">*</span>display<span class="op">;</span></span>
<span id="cb8-91"><a href="#cb8-91"></a></span>
<span id="cb8-92"><a href="#cb8-92"></a> build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/color/color.ui&quot;</span><span class="op">);</span></span>
<span id="cb8-93"><a href="#cb8-93"></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="cb8-94"><a href="#cb8-94"></a> gtk_window_set_application <span class="op">(</span>GTK_WINDOW <span class="op">(</span>win<span class="op">),</span> app<span class="op">);</span></span>
<span id="cb8-95"><a href="#cb8-95"></a> tv <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;tv&quot;</span><span class="op">));</span></span>
<span id="cb8-96"><a href="#cb8-96"></a> da <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;da&quot;</span><span class="op">));</span></span>
<span id="cb8-97"><a href="#cb8-97"></a> g_object_unref<span class="op">(</span>build<span class="op">);</span></span>
<span id="cb8-98"><a href="#cb8-98"></a> g_signal_connect <span class="op">(</span>GTK_DRAWING_AREA <span class="op">(</span>da<span class="op">),</span> <span class="st">&quot;resize&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>resize_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb8-99"><a href="#cb8-99"></a> gtk_drawing_area_set_draw_func <span class="op">(</span>GTK_DRAWING_AREA <span class="op">(</span>da<span class="op">),</span> draw_func<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb8-100"><a href="#cb8-100"></a></span>
<span id="cb8-101"><a href="#cb8-101"></a> display <span class="op">=</span> gdk_display_get_default <span class="op">();</span></span>
<span id="cb8-102"><a href="#cb8-102"></a> GtkCssProvider <span class="op">*</span>provider <span class="op">=</span> gtk_css_provider_new <span class="op">();</span></span>
<span id="cb8-103"><a href="#cb8-103"></a> gtk_css_provider_load_from_data <span class="op">(</span>provider<span class="op">,</span> <span class="st">&quot;textview {padding: 10px; font-family: monospace; font-size: 12pt;}&quot;</span><span class="op">,</span> <span class="op">-</span><span class="dv">1</span><span class="op">);</span></span>
<span id="cb8-104"><a href="#cb8-104"></a> gtk_style_context_add_provider_for_display <span class="op">(</span>display<span class="op">,</span> GTK_STYLE_PROVIDER <span class="op">(</span>provider<span class="op">),</span> GTK_STYLE_PROVIDER_PRIORITY_USER<span class="op">);</span></span>
<span id="cb8-105"><a href="#cb8-105"></a><span class="op">}</span></span>
<span id="cb8-106"><a href="#cb8-106"></a></span>
<span id="cb8-107"><a href="#cb8-107"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-108"><a href="#cb8-108"></a>app_shutdown <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-109"><a href="#cb8-109"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span></span>
<span id="cb8-110"><a href="#cb8-110"></a> cairo_surface_destroy <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb8-111"><a href="#cb8-111"></a><span class="op">}</span></span>
<span id="cb8-112"><a href="#cb8-112"></a></span>
<span id="cb8-113"><a href="#cb8-113"></a><span class="pp">#define APPLICATION_ID &quot;com.github.ToshioCP.color&quot;</span></span>
<span id="cb8-114"><a href="#cb8-114"></a></span>
<span id="cb8-115"><a href="#cb8-115"></a><span class="dt">int</span></span>
<span id="cb8-116"><a href="#cb8-116"></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="cb8-117"><a href="#cb8-117"></a> GtkApplication <span class="op">*</span>app<span class="op">;</span></span>
<span id="cb8-118"><a href="#cb8-118"></a> <span class="dt">int</span> stat<span class="op">;</span></span>
<span id="cb8-119"><a href="#cb8-119"></a></span>
<span id="cb8-120"><a href="#cb8-120"></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="cb8-121"><a href="#cb8-121"></a></span>
<span id="cb8-122"><a href="#cb8-122"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;startup&quot;</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="cb8-123"><a href="#cb8-123"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;shutdown&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>app_shutdown<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb8-124"><a href="#cb8-124"></a> g_signal_connect <span class="op">(</span>app<span class="op">,</span> <span class="st">&quot;activate&quot;</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="cb8-125"><a href="#cb8-125"></a></span>
<span id="cb8-126"><a href="#cb8-126"></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="cb8-127"><a href="#cb8-127"></a> g_object_unref <span class="op">(</span>app<span class="op">);</span></span>
<span id="cb8-128"><a href="#cb8-128"></a> <span class="cf">return</span> stat<span class="op">;</span></span>
<span id="cb8-129"><a href="#cb8-129"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>4-8: Win, tv, da and surface are defined as static variables.</li>
<li>10-42: Run function.</li>
<li>44-63: Handlers for button signals.</li>
<li>65-71: Resize handler.</li>
<li>73-79: Drawing function.</li>
<li>81-84: Application activate handler. It just shows the main
window.</li>
<li>86-105: Application startup handler.</li>
<li>92- 97: It builds widgets according to the ui resource. The static
variables win, tv and da are assigned instances.</li>
<li>98: Connects “resize” signal and a handler.</li>
<li>99: Drawing function is set.</li>
<li>101-104: CSS for textview padding is set.</li>
<li>107-111: Application shutdown handler. If there exists a surface
instance, it will be destroyed.</li>
<li>116-129: A function <code>main</code>. It creates a new application
instance. And connects three signals startup, shutdown and activate to
their handlers. It runs the application. It releases the reference to
the application and returns with <code>stat</code> value.</li>
</ul>
<h2 id="meson.build">Meson.build</h2>
<p>This file is almost same as before. An argument “export_dynamic:
true” is added to executable function.</p>
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb8-2"><a href="#cb8-2"></a>resize_cb <span class="op">(</span>GtkWidget <span class="op">*</span>widget<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-3"><a href="#cb8-3"></a> cairo_t <span class="op">*</span>cr<span class="op">;</span></span>
<span id="cb8-4"><a href="#cb8-4"></a></span>
<span id="cb8-5"><a href="#cb8-5"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span></span>
<span id="cb8-6"><a href="#cb8-6"></a> cairo_surface_destroy <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb8-7"><a href="#cb8-7"></a> surface <span class="op">=</span> cairo_image_surface_create <span class="op">(</span>CAIRO_FORMAT_RGB24<span class="op">,</span> width<span class="op">,</span> height<span class="op">);</span></span>
<span id="cb8-8"><a href="#cb8-8"></a> <span class="cf">if</span> <span class="op">(</span>surface_save<span class="op">)</span></span>
<span id="cb8-9"><a href="#cb8-9"></a> cairo_surface_destroy <span class="op">(</span>surface_save<span class="op">);</span></span>
<span id="cb8-10"><a href="#cb8-10"></a> surface_save <span class="op">=</span> cairo_image_surface_create <span class="op">(</span>CAIRO_FORMAT_RGB24<span class="op">,</span> width<span class="op">,</span> height<span class="op">);</span></span>
<span id="cb8-11"><a href="#cb8-11"></a> <span class="co">/* Paint the surface white. It is the background color. */</span></span>
<span id="cb8-12"><a href="#cb8-12"></a> cr <span class="op">=</span> cairo_create <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb8-13"><a href="#cb8-13"></a> cairo_set_source_rgb <span class="op">(</span>cr<span class="op">,</span> <span class="fl">1.0</span><span class="op">,</span> <span class="fl">1.0</span><span class="op">,</span> <span class="fl">1.0</span><span class="op">);</span></span>
<span id="cb8-14"><a href="#cb8-14"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb8-15"><a href="#cb8-15"></a> cairo_destroy <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb8-16"><a href="#cb8-16"></a><span class="op">}</span></span></code></pre></div>
<p>This callback is called when the GtkDrawingArea is shown. It is the
only call because the window is not resizable.</p>
<p>It creates image surfaces for <code>surface</code> and
<code>surface_save</code>. The <code>surface</code> surface is painted
white, which is the background color.</p>
<p>The drawing function copies <code>surface</code> to the
GtkDrawingArea surface.</p>
<div class="sourceCode" id="cb9"><pre
class="sourceCode numberSource numberLines"><code class="sourceCode"><span id="cb9-1"><a href="#cb9-1"></a>project(&#39;color&#39;, &#39;c&#39;)</span>
<span id="cb9-2"><a href="#cb9-2"></a></span>
<span id="cb9-3"><a href="#cb9-3"></a>gtkdep = dependency(&#39;gtk4&#39;)</span>
<span id="cb9-4"><a href="#cb9-4"></a></span>
<span id="cb9-5"><a href="#cb9-5"></a>gnome=import(&#39;gnome&#39;)</span>
<span id="cb9-6"><a href="#cb9-6"></a>resources = gnome.compile_resources(&#39;resources&#39;,&#39;color.gresource.xml&#39;)</span>
<span id="cb9-7"><a href="#cb9-7"></a></span>
<span id="cb9-8"><a href="#cb9-8"></a>sourcefiles=files(&#39;colorapplication.c&#39;, &#39;../tfetextview/tfetextview.c&#39;)</span>
<span id="cb9-9"><a href="#cb9-9"></a></span>
<span id="cb9-10"><a href="#cb9-10"></a>executable(&#39;color&#39;, sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)</span></code></pre></div>
<h2 id="build-and-try">Build and try</h2>
<p>Type the following to compile the program.</p>
<pre><code>$ meson _build
$ ninja -C _build</code></pre>
<p>The application is made in <code>_build</code> directory. Type the
following to execute it.</p>
<pre><code>$ _build/color</code></pre>
<p>Type “red”, “green”, “blue”, “white”, black”, “light” or “dark” in
the TfeTextView. No new line charactor is needed. Then, click on the
<code>Run</code> button. Make sure the color of GtkDrawingArea
changes.</p>
<p>In this program TfeTextView is used to change the color. You can use
buttons or menus instead of textview. Probably it is more appropriate.
Using textview is unnatural. It is a good practice to make such
application by yourself.</p>
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb9-2"><a href="#cb9-2"></a>draw_cb <span class="op">(</span>GtkDrawingArea <span class="op">*</span>da<span class="op">,</span> cairo_t <span class="op">*</span>cr<span class="op">,</span> <span class="dt">int</span> width<span class="op">,</span> <span class="dt">int</span> height<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-3"><a href="#cb9-3"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span> <span class="op">{</span></span>
<span id="cb9-4"><a href="#cb9-4"></a> cairo_set_source_surface <span class="op">(</span>cr<span class="op">,</span> surface<span class="op">,</span> <span class="fl">0.0</span><span class="op">,</span> <span class="fl">0.0</span><span class="op">);</span></span>
<span id="cb9-5"><a href="#cb9-5"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb9-6"><a href="#cb9-6"></a> <span class="op">}</span></span>
<span id="cb9-7"><a href="#cb9-7"></a><span class="op">}</span></span></code></pre></div>
<p>This function is called by the system when it needs to redraw the
drawing area.</p>
<p>Two surfaces <code>surface</code> and <code>surface_save</code> are
destroyed before the application quits.</p>
<div class="sourceCode" id="cb10"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb10-2"><a href="#cb10-2"></a>app_shutdown <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb10-3"><a href="#cb10-3"></a> <span class="cf">if</span> <span class="op">(</span>surface<span class="op">)</span></span>
<span id="cb10-4"><a href="#cb10-4"></a> cairo_surface_destroy <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb10-5"><a href="#cb10-5"></a> <span class="cf">if</span> <span class="op">(</span>surface_save<span class="op">)</span></span>
<span id="cb10-6"><a href="#cb10-6"></a> cairo_surface_destroy <span class="op">(</span>surface_save<span class="op">);</span></span>
<span id="cb10-7"><a href="#cb10-7"></a><span class="op">}</span></span></code></pre></div>
<h3 id="gtkgesturedrag">GtkGestureDrag</h3>
<p>Gesture class is used to recognize human gestures such as click,
drag, pan, swipe and so on. It is a subclass of GtkEventController.
GtkGesture class is abstract and there are several implementations.</p>
<ul>
<li>GtkGestureClick</li>
<li>GtkGestureDrag</li>
<li>GtkGesturePan</li>
<li>GtkGestureSwipe</li>
<li>other implementations</li>
</ul>
<p>The program <code>rect.c</code> uses GtkGestureDrag. It is the
implementation for drags. The parent-child relationship is as
follows.</p>
<pre><code>GObject -- GtkEventController -- GtkGesture -- GtkGestureSingle -- GtkGestureDrag</code></pre>
<p>GtkGestureSingle is a subclass of GtkGesture and optimized for
singe-touch and mouse gestures.</p>
<p>A GtkGestureDrag instance is created and initialized in the startup
signal handler in <code>rect.c</code>. See line 18 to 23 in the
following.</p>
<div class="sourceCode" id="cb12"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb12-1"><a href="#cb12-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb12-2"><a href="#cb12-2"></a>app_startup <span class="op">(</span>GApplication <span class="op">*</span>application<span class="op">)</span> <span class="op">{</span></span>
<span id="cb12-3"><a href="#cb12-3"></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="cb12-4"><a href="#cb12-4"></a> GtkBuilder <span class="op">*</span>build<span class="op">;</span></span>
<span id="cb12-5"><a href="#cb12-5"></a> GtkWindow <span class="op">*</span>win<span class="op">;</span></span>
<span id="cb12-6"><a href="#cb12-6"></a> GtkDrawingArea <span class="op">*</span>da<span class="op">;</span></span>
<span id="cb12-7"><a href="#cb12-7"></a> GtkGesture <span class="op">*</span>drag<span class="op">;</span></span>
<span id="cb12-8"><a href="#cb12-8"></a></span>
<span id="cb12-9"><a href="#cb12-9"></a> build <span class="op">=</span> gtk_builder_new_from_resource <span class="op">(</span><span class="st">&quot;/com/github/ToshioCP/rect/rect.ui&quot;</span><span class="op">);</span></span>
<span id="cb12-10"><a href="#cb12-10"></a> win <span class="op">=</span> GTK_WINDOW <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="cb12-11"><a href="#cb12-11"></a> da <span class="op">=</span> GTK_DRAWING_AREA <span class="op">(</span>gtk_builder_get_object <span class="op">(</span>build<span class="op">,</span> <span class="st">&quot;da&quot;</span><span class="op">));</span></span>
<span id="cb12-12"><a href="#cb12-12"></a> gtk_window_set_application <span class="op">(</span>win<span class="op">,</span> app<span class="op">);</span></span>
<span id="cb12-13"><a href="#cb12-13"></a> g_object_unref <span class="op">(</span>build<span class="op">);</span></span>
<span id="cb12-14"><a href="#cb12-14"></a></span>
<span id="cb12-15"><a href="#cb12-15"></a> gtk_drawing_area_set_draw_func <span class="op">(</span>da<span class="op">,</span> draw_cb<span class="op">,</span> NULL<span class="op">,</span> NULL<span class="op">);</span></span>
<span id="cb12-16"><a href="#cb12-16"></a> g_signal_connect_after <span class="op">(</span>da<span class="op">,</span> <span class="st">&quot;resize&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>resize_cb<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb12-17"><a href="#cb12-17"></a></span>
<span id="cb12-18"><a href="#cb12-18"></a> drag <span class="op">=</span> gtk_gesture_drag_new <span class="op">();</span></span>
<span id="cb12-19"><a href="#cb12-19"></a> gtk_gesture_single_set_button <span class="op">(</span>GTK_GESTURE_SINGLE <span class="op">(</span>drag<span class="op">),</span> GDK_BUTTON_PRIMARY<span class="op">);</span></span>
<span id="cb12-20"><a href="#cb12-20"></a> gtk_widget_add_controller <span class="op">(</span>GTK_WIDGET <span class="op">(</span>da<span class="op">),</span> GTK_EVENT_CONTROLLER <span class="op">(</span>drag<span class="op">));</span></span>
<span id="cb12-21"><a href="#cb12-21"></a> g_signal_connect <span class="op">(</span>drag<span class="op">,</span> <span class="st">&quot;drag-begin&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>drag_begin<span class="op">),</span> NULL<span class="op">);</span></span>
<span id="cb12-22"><a href="#cb12-22"></a> g_signal_connect <span class="op">(</span>drag<span class="op">,</span> <span class="st">&quot;drag-update&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>drag_update<span class="op">),</span> da<span class="op">);</span></span>
<span id="cb12-23"><a href="#cb12-23"></a> g_signal_connect <span class="op">(</span>drag<span class="op">,</span> <span class="st">&quot;drag-end&quot;</span><span class="op">,</span> G_CALLBACK <span class="op">(</span>drag_end<span class="op">),</span> da<span class="op">);</span></span>
<span id="cb12-24"><a href="#cb12-24"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>The function <code>gtk_gesture_drag_new</code> creates a new
GtkGestureDrag instance.</li>
<li>The function <code>gtk_gesture_single_set_button</code> sets the
button number to listen to. The constant <code>GDK_BUTTON_PRIMARY</code>
is the left button of a mouse.</li>
<li>The function <code>gtk_widget_add_controller</code> adds an event
controller, gestures are descendants of the event controller, to a
widget.</li>
<li>Three signals and handlers are connected.
<ul>
<li>drag-begin: Emitted when dragging starts.</li>
<li>drag-update: Emitted when the dragging point moves.</li>
<li>drag-end: Emitted when the dragging ends.</li>
</ul></li>
</ul>
<p>The process during the drag is as follows.</p>
<ul>
<li>start: save the surface and start points</li>
<li>update: restore the surface and draw a thin rectangle between the
start point and the current point of the mouse</li>
<li>end: restore the surface and draw a thick rectangle between the
start and end points.</li>
</ul>
<p>We need two global variables for the start point.</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">double</span> start_x<span class="op">;</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="dt">static</span> <span class="dt">double</span> start_y<span class="op">;</span></span></code></pre></div>
<p>The following is the handler for the “drag-begin” signal.</p>
<div class="sourceCode" id="cb14"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb14-1"><a href="#cb14-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb14-2"><a href="#cb14-2"></a>copy_surface <span class="op">(</span>cairo_surface_t <span class="op">*</span>src<span class="op">,</span> cairo_surface_t <span class="op">*</span>dst<span class="op">)</span> <span class="op">{</span></span>
<span id="cb14-3"><a href="#cb14-3"></a> <span class="cf">if</span> <span class="op">(!</span>src <span class="op">||</span> <span class="op">!</span>dst<span class="op">)</span></span>
<span id="cb14-4"><a href="#cb14-4"></a> <span class="cf">return</span><span class="op">;</span></span>
<span id="cb14-5"><a href="#cb14-5"></a> cairo_t <span class="op">*</span>cr <span class="op">=</span> cairo_create <span class="op">(</span>dst<span class="op">);</span></span>
<span id="cb14-6"><a href="#cb14-6"></a> cairo_set_source_surface <span class="op">(</span>cr<span class="op">,</span> src<span class="op">,</span> <span class="fl">0.0</span><span class="op">,</span> <span class="fl">0.0</span><span class="op">);</span></span>
<span id="cb14-7"><a href="#cb14-7"></a> cairo_paint <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb14-8"><a href="#cb14-8"></a> cairo_destroy <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb14-9"><a href="#cb14-9"></a><span class="op">}</span></span>
<span id="cb14-10"><a href="#cb14-10"></a></span>
<span id="cb14-11"><a href="#cb14-11"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb14-12"><a href="#cb14-12"></a>drag_begin <span class="op">(</span>GtkGestureDrag <span class="op">*</span>gesture<span class="op">,</span> <span class="dt">double</span> x<span class="op">,</span> <span class="dt">double</span> y<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb14-13"><a href="#cb14-13"></a> <span class="co">// save the surface and record (x, y)</span></span>
<span id="cb14-14"><a href="#cb14-14"></a> copy_surface <span class="op">(</span>surface<span class="op">,</span> surface_save<span class="op">);</span></span>
<span id="cb14-15"><a href="#cb14-15"></a> start_x <span class="op">=</span> x<span class="op">;</span></span>
<span id="cb14-16"><a href="#cb14-16"></a> start_y <span class="op">=</span> y<span class="op">;</span></span>
<span id="cb14-17"><a href="#cb14-17"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>Copies <code>surface</code> to <code>surface_save</code>, which is
an image just before the dragging.</li>
<li>Stores the points to <code>start_x</code> and
<code>start_y</code>.</li>
</ul>
<div class="sourceCode" id="cb15"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb15-1"><a href="#cb15-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb15-2"><a href="#cb15-2"></a>drag_update <span class="op">(</span>GtkGestureDrag <span class="op">*</span>gesture<span class="op">,</span> <span class="dt">double</span> offset_x<span class="op">,</span> <span class="dt">double</span> offset_y<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb15-3"><a href="#cb15-3"></a> GtkWidget <span class="op">*</span>da <span class="op">=</span> GTK_WIDGET <span class="op">(</span>user_data<span class="op">);</span></span>
<span id="cb15-4"><a href="#cb15-4"></a> cairo_t <span class="op">*</span>cr<span class="op">;</span></span>
<span id="cb15-5"><a href="#cb15-5"></a> </span>
<span id="cb15-6"><a href="#cb15-6"></a> copy_surface <span class="op">(</span>surface_save<span class="op">,</span> surface<span class="op">);</span></span>
<span id="cb15-7"><a href="#cb15-7"></a> cr <span class="op">=</span> cairo_create <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb15-8"><a href="#cb15-8"></a> cairo_rectangle <span class="op">(</span>cr<span class="op">,</span> start_x<span class="op">,</span> start_y<span class="op">,</span> offset_x<span class="op">,</span> offset_y<span class="op">);</span></span>
<span id="cb15-9"><a href="#cb15-9"></a> cairo_set_line_width <span class="op">(</span>cr<span class="op">,</span> <span class="fl">1.0</span><span class="op">);</span></span>
<span id="cb15-10"><a href="#cb15-10"></a> cairo_stroke <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb15-11"><a href="#cb15-11"></a> cairo_destroy <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb15-12"><a href="#cb15-12"></a> gtk_widget_queue_draw <span class="op">(</span>da<span class="op">);</span></span>
<span id="cb15-13"><a href="#cb15-13"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>Restores <code>surface</code> from <code>surface_save</code>.</li>
<li>Draws a rectangle with thin lines.</li>
<li>Calls <code>gtk_widget_queue_draw</code> to add the GtkDrawingArea
to the queue to redraw.</li>
</ul>
<div class="sourceCode" id="cb16"><pre
class="sourceCode numberSource C numberLines"><code class="sourceCode c"><span id="cb16-1"><a href="#cb16-1"></a><span class="dt">static</span> <span class="dt">void</span></span>
<span id="cb16-2"><a href="#cb16-2"></a>drag_end <span class="op">(</span>GtkGestureDrag <span class="op">*</span>gesture<span class="op">,</span> <span class="dt">double</span> offset_x<span class="op">,</span> <span class="dt">double</span> offset_y<span class="op">,</span> gpointer user_data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb16-3"><a href="#cb16-3"></a> GtkWidget <span class="op">*</span>da <span class="op">=</span> GTK_WIDGET <span class="op">(</span>user_data<span class="op">);</span></span>
<span id="cb16-4"><a href="#cb16-4"></a> cairo_t <span class="op">*</span>cr<span class="op">;</span></span>
<span id="cb16-5"><a href="#cb16-5"></a> </span>
<span id="cb16-6"><a href="#cb16-6"></a> copy_surface <span class="op">(</span>surface_save<span class="op">,</span> surface<span class="op">);</span></span>
<span id="cb16-7"><a href="#cb16-7"></a> cr <span class="op">=</span> cairo_create <span class="op">(</span>surface<span class="op">);</span></span>
<span id="cb16-8"><a href="#cb16-8"></a> cairo_rectangle <span class="op">(</span>cr<span class="op">,</span> start_x<span class="op">,</span> start_y<span class="op">,</span> offset_x<span class="op">,</span> offset_y<span class="op">);</span></span>
<span id="cb16-9"><a href="#cb16-9"></a> cairo_set_line_width <span class="op">(</span>cr<span class="op">,</span> <span class="fl">6.0</span><span class="op">);</span></span>
<span id="cb16-10"><a href="#cb16-10"></a> cairo_stroke <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb16-11"><a href="#cb16-11"></a> cairo_destroy <span class="op">(</span>cr<span class="op">);</span></span>
<span id="cb16-12"><a href="#cb16-12"></a> gtk_widget_queue_draw <span class="op">(</span>da<span class="op">);</span></span>
<span id="cb16-13"><a href="#cb16-13"></a><span class="op">}</span></span></code></pre></div>
<ul>
<li>Restores <code>surface</code> from <code>surface_save</code>.</li>
<li>Draws a rectangle with thick lines.</li>
<li>Calls <code>gtk_widget_queue_draw</code> to add the GtkDrawingArea
to the queue to redraw.</li>
</ul>
<h2 id="build-and-run">Build and run</h2>
<p>Download the <a
href="https://github.com/ToshioCP/Gtk4-tutorial">repository</a>. Change
your current directory to <code>src/custom_drawing</code>. Run meson and
ninja to build the program. Type <code>_build/rect</code> to run the
program. Try to draw rectangles.</p>
<pre><code>$ cd src/custom_drawing
$ meson setup _build
$ ninja -C _build
$ _build/rect</code></pre>
<figure>
<img src="image/rect.png" alt="The screen of rect program" />
<figcaption aria-hidden="true">The screen of rect program</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>

View file

@ -116,7 +116,7 @@ interpreter</h1>
<p>A program <code>turtle</code> is an example with the combination of
TfeTextView and GtkDrawingArea objects. It is a very small interpreter
but you can draw fractal curves with it. The following diagram is a Koch
curve, which is one of famous fractal curves.</p>
curve, which is one of the famous fractal curves.</p>
<figure>
<img src="image/turtle_koch.png" alt="Koch curve" />
<figcaption aria-hidden="true">Koch curve</figcaption>

View file

@ -2,27 +2,23 @@ Up: [README.md](../README.md), Prev: [Section 23](sec23.md), Next: [Section 25]
# GtkDrawingArea and Cairo
This section and following sections are *not* updated yet and the programs were checked on the older GTK version than 4.10.
They will be updated in the near future.
If you want to draw shapes or paint images dynamically on the screen, use the GtkDrawingArea widget.
If you want to draw dynamically on the screen, like an image window of gimp graphics editor, the GtkDrawingArea widget is the most suitable widget.
You can freely draw or redraw an image in this widget.
This is called custom drawing.
GtkDrawingArea provides a cairo drawing context.
You can draw images with cairo library functions.
This section describes:
GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions.
In this section, I will explain:
1. Cairo, but only briefly
1. Cairo, but briefly
2. GtkDrawingArea, with a very simple example.
## Cairo
Cairo is a set of two dimensional graphical drawing functions (or graphics library).
Cairo is a drawing library for two dimensional graphics.
There are a lot of documents on [Cairo's website](https://www.cairographics.org/).
If you aren't familiar with Cairo, it is worth reading the [tutorial](https://www.cairographics.org/tutorial/).
The following is an introduction to the Cairo library and how to use it.
First, you need to know about surfaces, sources, masks, destinations, cairo context and transformations.
The following is an introduction to the Cairo library.
First, you need to know surfaces, sources, masks, destinations, cairo context and transformations.
- A surface represents an image.
It is like a canvas.
@ -35,7 +31,7 @@ For example, `cairo_stroke` is a function to draw a path to the destination by t
- A transformation can be applied before the transfer completes.
The transformation which is applied is called affine, which is a mathematical term meaning transofrmations
that preserve straight lines.
Scaling, rotating, reflecting, shearing and translating are all examples of affine transformations.
Scaling, rotating, reflecting, shearing and translating are examples of affine transformations.
They are mathematically represented by matrix multiplication and vector addition.
In this section we don't use it, instead we will only use the identity transformation.
This means that the coordinates in the source and mask are the same as the coordinates in destination.
@ -106,28 +102,30 @@ Modern displays have this type of color depth.
Width and height are in pixels and given as integers.
- 14: Creates cairo context.
The surface given as an argument will be the destination of the context.
- 18: `cairo_set_source_rgb` creates a source pattern, which in this case is a solid white paint.
The second to fourth arguments are red, green and blue color values respectively, and they are
of type float. The values are between zero (0.0) and one (1.0), with
black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).
- 18: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
The second to fourth arguments are red, green and blue color values respectively, and they are of type float.
The values are between zero (0.0) and one (1.0).
Black is (0.0,0.0,0.0) and white is (1.0,1.0,1.0).
- 19: `cairo_paint` copies everywhere in the source to destination.
The destination is filled with white pixels with this command.
- 21: Sets the source color to black.
- 22: `cairo_set_line_width` set the width of lines.
- 22: `cairo_set_line_width` sets the width of lines.
In this case, the line width is set to be two pixels and will end up that same size.
(It is because the transformation is identity.
If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
- 23: Draws a rectangle (square) on the mask.
The square is located at the center.
- 24: `cairo_stroke` transfer the source to destination through the rectangle in the mask.
- 27: Outputs the image to a png file `rectangle.png`.
- 28: Destroys the context. At the same time the source is destroyed.
- 29: Destroys the surface.
- 24: `cairo_stroke` transfers the source to destination through the rectangle in the mask.
- 31: Outputs the image to a png file `rectangle.png`.
- 32: Destroys the context. At the same time the source is destroyed.
- 33: Destroys the surface.
To compile this, change your current directory to `src/misc` and type the following.
$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
```
$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
```
s
![rectangle.png](../image/rectangle.png)
See the [Cairo's website](https://www.cairographics.org/) for further information.
@ -187,7 +185,7 @@ The two functions `app_activate` and `draw_function` are important in this examp
- 22: Creates a GtkDrawingArea instance.
- 25: Sets a drawing function of the widget.
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
GtkDrawingArea widget uses the function `draw_function` to draw the contents of itself whenever its necessary.
For example, when a user drag a mouse pointer and resize a top-level window, GtkDrawingArea also changes the size.
Then, the whole window needs to be redrawn.
For the information of `gtk_drawing_area_set_draw_func`, see [Gtk API Reference -- gtk\_drawing\_area\_set\_draw\_func](https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html).

View file

@ -173,7 +173,7 @@ every second (or 1000ms).
13
14 gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA (clock), draw_clock, NULL, NULL);
15 g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) clock);
16 gtk_widget_show(win);
16 gtk_window_present(GTK_WINDOW (win));
17
18 }
~~~
@ -349,7 +349,7 @@ You can find the source files in the `tfc` directory. it can be compiled with `.
143
144 gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA (clock), draw_clock, NULL, NULL);
145 g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) clock);
146 gtk_widget_show(win);
146 gtk_window_present(GTK_WINDOW (win));
147
148 }
149

View file

@ -1,452 +1,382 @@
Up: [README.md](../README.md), Prev: [Section 25](sec25.md), Next: [Section 27](sec27.md)
# Combine GtkDrawingArea and TfeTextView
# Custom drawing
Now, we will make a new application which has GtkDrawingArea and TfeTextView in it.
Its name is "color".
If you write a name of a color in TfeTextView and click on the `run` button, then the color of GtkDrawingArea changes to the color given by you.
Custom drawing is to draw shapes dynamically.
This section shows an example of custom drawing.
You can draw rectangles by dragging the mouse.
![color](../image/color.png)
Down the button.
The following colors are available.
(without new line charactor)
![down the button](../image/cd0.png)
- white
- black
- red
- green
- blue
Move the mouse
In addition the following two options are also available.
![Move the mouse](../image/cd1.png)
- light: Make the color of the drawing area lighter.
- dark: Make the color of the drawing area darker.
Up the button.
This application can only do very simple things.
However, it tells us that if we add powerful parser to it, we will be able to make it more efficient.
I want to show it to you in the later section by making a turtle graphics language like Logo program language.
![Up the button](../image/cd2.png)
In this section, we focus on how to bind the two objects.
The programs are at `src/custom_drawing` directory.
Download the [repository](https://github.com/ToshioCP/Gtk4-tutorial) and see the directory.
There are four files.
## Color.ui and color.gresource.xml
- meson.build
- rect.c
- rect.gresource.xml
- rect.ui
First, We need to make the ui file of the widgets.
Title bar, four buttons in the tool bar, textview and drawing area.
The ui file is as follows.
## rect.gresource.xml
This file describes a ui file to compile.
The compiler glib-compile-resources uses it.
~~~xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <gresources>
3 <gresource prefix="/com/github/ToshioCP/rect">
4 <file>rect.ui</file>
5 </gresource>
6 </gresources>
~~~
The prefix is `/com/github/ToshioCP/rect` and the file is `rect.ui`.
Therefore, GtkBuilder reads the resource from `/com/github/ToshioCP/rect/rect.ui`.
## rect.ui
The following is the ui file that defines the widgets.
There are two widgets which are GtkApplicationWindow and GtkDrawingArea.
The ids are `win` and `da` respectively.
~~~xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <interface>
3 <object class="GtkApplicationWindow" id="win">
4 <property name="title">color changer</property>
5 <property name="default-width">600</property>
6 <property name="default-height">400</property>
7 <child>
8 <object class="GtkBox" id="boxv">
9 <property name="orientation">GTK_ORIENTATION_VERTICAL</property>
10 <child>
11 <object class="GtkBox" id="boxh1">
12 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
13 <child>
14 <object class="GtkLabel" id="dmy1">
15 <property name="width-chars">10</property>
16 </object>
17 </child>
18 <child>
19 <object class="GtkButton" id="btnr">
20 <property name="label">Run</property>
21 <signal name="clicked" handler="run_cb"></signal>
22 </object>
23 </child>
24 <child>
25 <object class="GtkButton" id="btno">
26 <property name="label">Open</property>
27 <signal name="clicked" handler="open_cb"></signal>
28 </object>
29 </child>
30 <child>
31 <object class="GtkLabel" id="dmy2">
32 <property name="hexpand">TRUE</property>
33 </object>
34 </child>
35 <child>
36 <object class="GtkButton" id="btns">
37 <property name="label">Save</property>
38 <signal name="clicked" handler="save_cb"></signal>
39 </object>
40 </child>
41 <child>
42 <object class="GtkButton" id="btnc">
43 <property name="label">Close</property>
44 <signal name="clicked" handler="close_cb"></signal>
45 </object>
46 </child>
47 <child>
48 <object class="GtkLabel" id="dmy3">
49 <property name="width-chars">10</property>
50 </object>
51 </child>
52 </object>
53 </child>
54 <child>
55 <object class="GtkBox" id="boxh2">
56 <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
57 <property name="homogeneous">TRUE</property>
58 <child>
59 <object class="GtkScrolledWindow" id="scr">
60 <property name="hexpand">TRUE</property>
61 <property name="vexpand">TRUE</property>
62 <child>
63 <object class="TfeTextView" id="tv">
64 <property name="wrap-mode">GTK_WRAP_WORD_CHAR</property>
65 </object>
66 </child>
67 </object>
68 </child>
69 <child>
70 <object class="GtkDrawingArea" id="da">
71 <property name="hexpand">TRUE</property>
72 <property name="vexpand">TRUE</property>
73 </object>
74 </child>
75 </object>
76 </child>
77 </object>
78 </child>
79 </object>
80 </interface>
4 <property name="default-width">800</property>
5 <property name="default-height">600</property>
6 <property name="resizable">FALSE</property>
7 <property name="title">Custom drawing</property>
8 <child>
9 <object class="GtkDrawingArea" id="da">
10 <property name="hexpand">TRUE</property>
11 <property name="vexpand">TRUE</property>
12 </object>
13 </child>
14 </object>
15 </interface>
16
~~~
- 10-53: The horizontal box `boxh1` makes a tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`.
This is similar to the `tfe` text editor in [Section 9](sec9.md).
There are two differences.
`Run` button replaces `New` button.
A signal element is added to each button object.
It has "name" attribute which is a signal name and "handler" attribute which is the name of its signal handler.
Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application.
You can achieve this by adding "export_dynamic: true" argument to the executable function in `meson.build`.
And be careful that the handler must be defined without 'static' class.
- 54-76: The horizontal box `boxh2` includes GtkScrolledWindow and GtkDrawingArea.
GtkBox has "homogeneous property" with TRUE value, so the two children have the same width in the box.
TfeTextView is a child of GtkScrolledWindow.
## rect.c
The xml file for the resource compiler is almost same as before.
Just substitute "color" for "tfe".
### GtkApplication
~~~xml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <gresources>
3 <gresource prefix="/com/github/ToshioCP/color">
4 <file>color.ui</file>
5 </gresource>
6 </gresources>
This program uses GtkApplication.
The application ID is `com.github.ToshioCP.rect`.
```c
#define APPLICATION_ID "com.github.ToshioCP.rect"
```
See [GNOME Developer Documentation](https://developer.gnome.org/documentation/tutorials/application-id.html) for further information.
The function `main` is called at the beginning of the application.
~~~C
1 int
2 main (int argc, char **argv) {
3 GtkApplication *app;
4 int stat;
5
6 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN);
7 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
8 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
9 g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL);
10 stat =g_application_run (G_APPLICATION (app), argc, argv);
11 g_object_unref (app);
12 return stat;
13 }
~~~
## Drawing function and surface
It connects three signals and handlers.
The main point of this program is a drawing function.
- startup: It is emitted after the application is registered to the system.
- activate: It is emitted when the application is activated.
- shutdown: It is emitted just before the application quits.
~~~C
1 static void
2 app_startup (GApplication *application) {
3 GtkApplication *app = GTK_APPLICATION (application);
4 GtkBuilder *build;
5 GtkWindow *win;
6 GtkDrawingArea *da;
7 GtkGesture *drag;
8
9 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/rect/rect.ui");
10 win = GTK_WINDOW (gtk_builder_get_object (build, "win"));
11 da = GTK_DRAWING_AREA (gtk_builder_get_object (build, "da"));
12 gtk_window_set_application (win, app);
13 g_object_unref (build);
14
15 gtk_drawing_area_set_draw_func (da, draw_cb, NULL, NULL);
16 g_signal_connect_after (da, "resize", G_CALLBACK (resize_cb), NULL);
17
18 drag = gtk_gesture_drag_new ();
19 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY);
20 gtk_widget_add_controller (GTK_WIDGET (da), GTK_EVENT_CONTROLLER (drag));
21 g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), NULL);
22 g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), da);
23 g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), da);
24 }
~~~
The startup handler does three things.
- Builds the widgets.
- Initializes the GtkDrawingArea instance.
- Sets the drawing function
- Connects the "resize" signal and the handler.
- Creates the GtkGestureDrag instance and initializes it.
Gesture will be explained in this section later.
~~~C
1 static void
2 draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data) {
2 app_activate (GApplication *application) {
3 GtkApplication *app = GTK_APPLICATION (application);
4 GtkWindow *win;
5
6 win = gtk_application_get_active_window (app);
7 gtk_window_present (win);
8 }
~~~
The activate handler just shows the window.
### GtkDrawingArea
The program has two cairo surfaces and they are pointed by the global variables.
```C
static cairo_surface_t *surface = NULL;
static cairo_surface_t *surface_save = NULL;
```
The drawing process is as follows.
- Creates an image on `surface`.
- Copies `surface` to the cairo surface of the GtkDrawingArea.
- Calls ` gtk_widget_queue_draw (da)` to draw it if necessary.
They are created in the "resize" signal handler.
~~~C
1 static void
2 resize_cb (GtkWidget *widget, int width, int height, gpointer user_data) {
3 cairo_t *cr;
4
5 if (surface)
6 cairo_surface_destroy (surface);
7 surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
8 if (surface_save)
9 cairo_surface_destroy (surface_save);
10 surface_save = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
11 /* Paint the surface white. It is the background color. */
12 cr = cairo_create (surface);
13 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
14 cairo_paint (cr);
15 cairo_destroy (cr);
16 }
~~~
This callback is called when the GtkDrawingArea is shown.
It is the only call because the window is not resizable.
It creates image surfaces for `surface` and `surface_save`.
The `surface` surface is painted white, which is the background color.
The drawing function copies `surface` to the GtkDrawingArea surface.
~~~C
1 static void
2 draw_cb (GtkDrawingArea *da, cairo_t *cr, int width, int height, gpointer user_data) {
3 if (surface) {
4 cairo_set_source_surface (cr, surface, 0, 0);
4 cairo_set_source_surface (cr, surface, 0.0, 0.0);
5 cairo_paint (cr);
6 }
7 }
~~~
The `surface` variable in line 3 is a static variable.
This function is called by the system when it needs to redraw the drawing area.
~~~C
static cairo_surface_t *surface = NULL;
~~~
The drawing function just copies the `surface` to its own surface with the `cairo_paint` function.
The surface (pointed by the static variable `surface`) is built by the `run` function.
~~~C
1 static void
2 run (void) {
3 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
4 GtkTextIter start_iter;
5 GtkTextIter end_iter;
6 char *contents;
7 cairo_t *cr;
8
9 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
10 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
11 if (surface) {
12 cr = cairo_create (surface);
13 if (g_strcmp0 ("red", contents) == 0)
14 cairo_set_source_rgb (cr, 1, 0, 0);
15 else if (g_strcmp0 ("green", contents) == 0)
16 cairo_set_source_rgb (cr, 0, 1, 0);
17 else if (g_strcmp0 ("blue", contents) == 0)
18 cairo_set_source_rgb (cr, 0, 0, 1);
19 else if (g_strcmp0 ("white", contents) == 0)
20 cairo_set_source_rgb (cr, 1, 1, 1);
21 else if (g_strcmp0 ("black", contents) == 0)
22 cairo_set_source_rgb (cr, 0, 0, 0);
23 else if (g_strcmp0 ("light", contents) == 0)
24 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
25 else if (g_strcmp0 ("dark", contents) == 0)
26 cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
27 else
28 cairo_set_source_surface (cr, surface, 0, 0);
29 cairo_paint (cr);
30 cairo_destroy (cr);
31 }
32 g_free (contents);
33 }
~~~
- 9-10: Gets the string in the GtkTextBuffer and inserts it to `contents`.
- 11: If the variable `surface` points a surface instance, it is painted as follows.
- 12- 30: The source is set based on the string `contents` and copied to the surface with `cairo_paint`.
- 24,26: Alpha channel is used in "light" and "dark" procedure.
The drawing area just reflects the `surface`.
But one problem is resizing.
If a user resizes the main window, the drawing area is also resized.
It makes size difference between the surface and the drawing area.
So, the surface needs to be resized to fit the drawing area.
It is accomplished by connecting the "resize" signal on the drawing area to a handler.
~~~C
g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL);
~~~
The handler is as follows.
Two surfaces `surface` and `surface_save` are destroyed before the application quits.
~~~C
1 static void
2 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) {
2 app_shutdown (GApplication *application) {
3 if (surface)
4 cairo_surface_destroy (surface);
5 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
6 run ();
5 if (surface_save)
6 cairo_surface_destroy (surface_save);
7 }
~~~
If the variable `surface` sets a surface instance, it is destroyed.
A new surface is created and its size fits the drawing area.
The surface is assigned to the variable `surface`.
The function `run` is called and the surface is colored.
### GtkGestureDrag
The signal is emitted when:
Gesture class is used to recognize human gestures such as click, drag, pan, swipe and so on.
It is a subclass of GtkEventController.
GtkGesture class is abstract and there are several implementations.
- The drawing area is realized (it appears on the display).
- It is changed (resized) while realized
- GtkGestureClick
- GtkGestureDrag
- GtkGesturePan
- GtkGestureSwipe
- other implementations
So, the first surface is created when it is realized.
The program `rect.c` uses GtkGestureDrag.
It is the implementation for drags.
The parent-child relationship is as follows.
## Colorapplication.c
```
GObject -- GtkEventController -- GtkGesture -- GtkGestureSingle -- GtkGestureDrag
```
This is the main file.
GtkGestureSingle is a subclass of GtkGesture and optimized for singe-touch and mouse gestures.
- Builds widgets by GtkBuilder.
- Sets a drawing function for GtkDrawingArea.
And connects a handler to the "resize" signal on the GtkDrawingArea instance.
- Implements each call back function.
Particularly, `Run` signal handler is the point in this program.
The following is `colorapplication.c`.
A GtkGestureDrag instance is created and initialized in the startup signal handler in `rect.c`.
See line 18 to 23 in the following.
~~~C
1 #include <gtk/gtk.h>
2 #include "../tfetextview/tfetextview.h"
3
4 static GtkWidget *win;
5 static GtkWidget *tv;
6 static GtkWidget *da;
7
8 static cairo_surface_t *surface = NULL;
9
10 static void
11 run (void) {
12 GtkTextBuffer *tb = gtk_text_view_get_buffer (GTK_TEXT_VIEW (tv));
13 GtkTextIter start_iter;
14 GtkTextIter end_iter;
15 char *contents;
16 cairo_t *cr;
17
18 gtk_text_buffer_get_bounds (tb, &start_iter, &end_iter);
19 contents = gtk_text_buffer_get_text (tb, &start_iter, &end_iter, FALSE);
20 if (surface) {
21 cr = cairo_create (surface);
22 if (g_strcmp0 ("red", contents) == 0)
23 cairo_set_source_rgb (cr, 1, 0, 0);
24 else if (g_strcmp0 ("green", contents) == 0)
25 cairo_set_source_rgb (cr, 0, 1, 0);
26 else if (g_strcmp0 ("blue", contents) == 0)
27 cairo_set_source_rgb (cr, 0, 0, 1);
28 else if (g_strcmp0 ("white", contents) == 0)
29 cairo_set_source_rgb (cr, 1, 1, 1);
30 else if (g_strcmp0 ("black", contents) == 0)
31 cairo_set_source_rgb (cr, 0, 0, 0);
32 else if (g_strcmp0 ("light", contents) == 0)
33 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
34 else if (g_strcmp0 ("dark", contents) == 0)
35 cairo_set_source_rgba (cr, 0, 0, 0, 0.5);
36 else
37 cairo_set_source_surface (cr, surface, 0, 0);
38 cairo_paint (cr);
39 cairo_destroy (cr);
40 }
41 g_free (contents);
42 }
43
44 void
45 run_cb (GtkWidget *btnr) {
46 run ();
47 gtk_widget_queue_draw (GTK_WIDGET (da));
48 }
49
50 void
51 open_cb (GtkWidget *btno) {
52 tfe_text_view_open (TFE_TEXT_VIEW (tv), GTK_WINDOW (win));
53 }
54
55 void
56 save_cb (GtkWidget *btns) {
57 tfe_text_view_save (TFE_TEXT_VIEW (tv));
58 }
59
60 void
61 close_cb (GtkWidget *btnc) {
62 gtk_window_destroy (GTK_WINDOW (win));
63 }
64
65 static void
66 resize_cb (GtkDrawingArea *drawing_area, int width, int height, gpointer user_data) {
67 if (surface)
68 cairo_surface_destroy (surface);
69 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
70 run ();
71 }
72
73 static void
74 draw_func (GtkDrawingArea *drawing_area, cairo_t *cr, int width, int height, gpointer user_data) {
75 if (surface) {
76 cairo_set_source_surface (cr, surface, 0, 0);
77 cairo_paint (cr);
78 }
79 }
80
81 static void
82 app_activate (GApplication *application) {
83 gtk_window_present (GTK_WINDOW (win));
84 }
85
86 static void
87 app_startup (GApplication *application) {
88 GtkApplication *app = GTK_APPLICATION (application);
89 GtkBuilder *build;
90 GdkDisplay *display;
91
92 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/color/color.ui");
93 win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
94 gtk_window_set_application (GTK_WINDOW (win), app);
95 tv = GTK_WIDGET (gtk_builder_get_object (build, "tv"));
96 da = GTK_WIDGET (gtk_builder_get_object (build, "da"));
97 g_object_unref(build);
98 g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL);
99 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (da), draw_func, NULL, NULL);
100
101 display = gdk_display_get_default ();
102 GtkCssProvider *provider = gtk_css_provider_new ();
103 gtk_css_provider_load_from_data (provider, "textview {padding: 10px; font-family: monospace; font-size: 12pt;}", -1);
104 gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
105 }
106
107 static void
108 app_shutdown (GApplication *application) {
109 if (surface)
110 cairo_surface_destroy (surface);
111 }
112
113 #define APPLICATION_ID "com.github.ToshioCP.color"
114
115 int
116 main (int argc, char **argv) {
117 GtkApplication *app;
118 int stat;
119
120 app = gtk_application_new (APPLICATION_ID, G_APPLICATION_DEFAULT_FLAGS);
121
122 g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
123 g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL);
124 g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
125
126 stat =g_application_run (G_APPLICATION (app), argc, argv);
127 g_object_unref (app);
128 return stat;
129 }
130
1 static void
2 app_startup (GApplication *application) {
3 GtkApplication *app = GTK_APPLICATION (application);
4 GtkBuilder *build;
5 GtkWindow *win;
6 GtkDrawingArea *da;
7 GtkGesture *drag;
8
9 build = gtk_builder_new_from_resource ("/com/github/ToshioCP/rect/rect.ui");
10 win = GTK_WINDOW (gtk_builder_get_object (build, "win"));
11 da = GTK_DRAWING_AREA (gtk_builder_get_object (build, "da"));
12 gtk_window_set_application (win, app);
13 g_object_unref (build);
14
15 gtk_drawing_area_set_draw_func (da, draw_cb, NULL, NULL);
16 g_signal_connect_after (da, "resize", G_CALLBACK (resize_cb), NULL);
17
18 drag = gtk_gesture_drag_new ();
19 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY);
20 gtk_widget_add_controller (GTK_WIDGET (da), GTK_EVENT_CONTROLLER (drag));
21 g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), NULL);
22 g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), da);
23 g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), da);
24 }
~~~
- 4-8: Win, tv, da and surface are defined as static variables.
- 10-42: Run function.
- 44-63: Handlers for button signals.
- 65-71: Resize handler.
- 73-79: Drawing function.
- 81-84: Application activate handler.
It just shows the main window.
- 86-105: Application startup handler.
- 92- 97: It builds widgets according to the ui resource.
The static variables win, tv and da are assigned instances.
- 98: Connects "resize" signal and a handler.
- 99: Drawing function is set.
- 101-104: CSS for textview padding is set.
- 107-111: Application shutdown handler.
If there exists a surface instance, it will be destroyed.
- 116-129: A function `main`.
It creates a new application instance.
And connects three signals startup, shutdown and activate to their handlers.
It runs the application.
It releases the reference to the application and returns with `stat` value.
- The function `gtk_gesture_drag_new` creates a new GtkGestureDrag instance.
- The function `gtk_gesture_single_set_button` sets the button number to listen to.
The constant `GDK_BUTTON_PRIMARY` is the left button of a mouse.
- The function `gtk_widget_add_controller` adds an event controller, gestures are descendants of the event controller, to a widget.
- Three signals and handlers are connected.
- drag-begin: Emitted when dragging starts.
- drag-update: Emitted when the dragging point moves.
- drag-end: Emitted when the dragging ends.
## Meson.build
The process during the drag is as follows.
This file is almost same as before.
An argument "export_dynamic: true" is added to executable function.
- start: save the surface and start points
- update: restore the surface and draw a thin rectangle between the start point and the current point of the mouse
- end: restore the surface and draw a thick rectangle between the start and end points.
~~~meson
1 project('color', 'c')
2
3 gtkdep = dependency('gtk4')
4
5 gnome=import('gnome')
6 resources = gnome.compile_resources('resources','color.gresource.xml')
7
8 sourcefiles=files('colorapplication.c', '../tfetextview/tfetextview.c')
9
10 executable('color', sourcefiles, resources, dependencies: gtkdep, export_dynamic: true)
We need two global variables for the start point.
```C
static double start_x;
static double start_y;
```
The following is the handler for the "drag-begin" signal.
~~~C
1 static void
2 copy_surface (cairo_surface_t *src, cairo_surface_t *dst) {
3 if (!src || !dst)
4 return;
5 cairo_t *cr = cairo_create (dst);
6 cairo_set_source_surface (cr, src, 0.0, 0.0);
7 cairo_paint (cr);
8 cairo_destroy (cr);
9 }
10
11 static void
12 drag_begin (GtkGestureDrag *gesture, double x, double y, gpointer user_data) {
13 // save the surface and record (x, y)
14 copy_surface (surface, surface_save);
15 start_x = x;
16 start_y = y;
17 }
~~~
## Build and try
- Copies `surface` to `surface_save`, which is an image just before the dragging.
- Stores the points to `start_x` and `start_y`.
Type the following to compile the program.
~~~C
1 static void
2 drag_update (GtkGestureDrag *gesture, double offset_x, double offset_y, gpointer user_data) {
3 GtkWidget *da = GTK_WIDGET (user_data);
4 cairo_t *cr;
5
6 copy_surface (surface_save, surface);
7 cr = cairo_create (surface);
8 cairo_rectangle (cr, start_x, start_y, offset_x, offset_y);
9 cairo_set_line_width (cr, 1.0);
10 cairo_stroke (cr);
11 cairo_destroy (cr);
12 gtk_widget_queue_draw (da);
13 }
~~~
$ meson _build
$ ninja -C _build
- Restores `surface` from `surface_save`.
- Draws a rectangle with thin lines.
- Calls `gtk_widget_queue_draw` to add the GtkDrawingArea to the queue to redraw.
The application is made in `_build` directory.
Type the following to execute it.
~~~C
1 static void
2 drag_end (GtkGestureDrag *gesture, double offset_x, double offset_y, gpointer user_data) {
3 GtkWidget *da = GTK_WIDGET (user_data);
4 cairo_t *cr;
5
6 copy_surface (surface_save, surface);
7 cr = cairo_create (surface);
8 cairo_rectangle (cr, start_x, start_y, offset_x, offset_y);
9 cairo_set_line_width (cr, 6.0);
10 cairo_stroke (cr);
11 cairo_destroy (cr);
12 gtk_widget_queue_draw (da);
13 }
~~~
$ _build/color
- Restores `surface` from `surface_save`.
- Draws a rectangle with thick lines.
- Calls `gtk_widget_queue_draw` to add the GtkDrawingArea to the queue to redraw.
Type "red", "green", "blue", "white", black", "light" or "dark" in the TfeTextView.
No new line charactor is needed.
Then, click on the `Run` button.
Make sure the color of GtkDrawingArea changes.
## Build and run
In this program TfeTextView is used to change the color.
You can use buttons or menus instead of textview.
Probably it is more appropriate.
Using textview is unnatural.
It is a good practice to make such application by yourself.
Download the [repository](https://github.com/ToshioCP/Gtk4-tutorial).
Change your current directory to `src/custom_drawing`.
Run meson and ninja to build the program.
Type `_build/rect` to run the program.
Try to draw rectangles.
```
$ cd src/custom_drawing
$ meson setup _build
$ ninja -C _build
$ _build/rect
```
![The screen of rect program](../image/rect.png)
Up: [README.md](../README.md), Prev: [Section 25](sec25.md), Next: [Section 27](sec27.md)

View file

@ -4,7 +4,7 @@ Up: [README.md](../README.md), Prev: [Section 26](sec26.md), Next: [Section 28]
A program `turtle` is an example with the combination of TfeTextView and GtkDrawingArea objects.
It is a very small interpreter but you can draw fractal curves with it.
The following diagram is a Koch curve, which is one of famous fractal curves.
The following diagram is a Koch curve, which is one of the famous fractal curves.
![Koch curve](../src/turtle/image/turtle_koch.png)

BIN
image/cd0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
image/cd1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
image/cd2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
image/rect.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -0,0 +1,8 @@
project('rect', 'c')
gtkdep = dependency('gtk4')
gnome = import('gnome')
resources = gnome.compile_resources('resources','rect.gresource.xml')
executable(meson.project_name(), 'rect.c', resources, dependencies: gtkdep, export_dynamic: true, install: false)

137
src/custom_drawing/rect.c Normal file
View file

@ -0,0 +1,137 @@
#include <gtk/gtk.h>
static cairo_surface_t *surface = NULL;
static cairo_surface_t *surface_save = NULL;
static double start_x;
static double start_y;
static void
copy_surface (cairo_surface_t *src, cairo_surface_t *dst) {
if (!src || !dst)
return;
cairo_t *cr = cairo_create (dst);
cairo_set_source_surface (cr, src, 0.0, 0.0);
cairo_paint (cr);
cairo_destroy (cr);
}
/* This callback is called when the GtkDrawingArea is shown. */
/* It is the only call because the window is not resizable. */
static void
resize_cb (GtkWidget *widget, int width, int height, gpointer user_data) {
cairo_t *cr;
if (surface)
cairo_surface_destroy (surface);
surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
if (surface_save)
cairo_surface_destroy (surface_save);
surface_save = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height);
/* Paint the surface white. It is the background color. */
cr = cairo_create (surface);
cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
cairo_paint (cr);
cairo_destroy (cr);
}
static void
draw_cb (GtkDrawingArea *da, cairo_t *cr, int width, int height, gpointer user_data) {
if (surface) {
cairo_set_source_surface (cr, surface, 0.0, 0.0);
cairo_paint (cr);
}
}
static void
drag_begin (GtkGestureDrag *gesture, double x, double y, gpointer user_data) {
// save the surface and record (x, y)
copy_surface (surface, surface_save);
start_x = x;
start_y = y;
}
static void
drag_update (GtkGestureDrag *gesture, double offset_x, double offset_y, gpointer user_data) {
GtkWidget *da = GTK_WIDGET (user_data);
cairo_t *cr;
copy_surface (surface_save, surface);
cr = cairo_create (surface);
cairo_rectangle (cr, start_x, start_y, offset_x, offset_y);
cairo_set_line_width (cr, 1.0);
cairo_stroke (cr);
cairo_destroy (cr);
gtk_widget_queue_draw (da);
}
static void
drag_end (GtkGestureDrag *gesture, double offset_x, double offset_y, gpointer user_data) {
GtkWidget *da = GTK_WIDGET (user_data);
cairo_t *cr;
copy_surface (surface_save, surface);
cr = cairo_create (surface);
cairo_rectangle (cr, start_x, start_y, offset_x, offset_y);
cairo_set_line_width (cr, 6.0);
cairo_stroke (cr);
cairo_destroy (cr);
gtk_widget_queue_draw (da);
}
static void
app_activate (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkWindow *win;
win = gtk_application_get_active_window (app);
gtk_window_present (win);
}
static void
app_shutdown (GApplication *application) {
if (surface)
cairo_surface_destroy (surface);
if (surface_save)
cairo_surface_destroy (surface_save);
}
static void
app_startup (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkBuilder *build;
GtkWindow *win;
GtkDrawingArea *da;
GtkGesture *drag;
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/rect/rect.ui");
win = GTK_WINDOW (gtk_builder_get_object (build, "win"));
da = GTK_DRAWING_AREA (gtk_builder_get_object (build, "da"));
gtk_window_set_application (win, app);
g_object_unref (build);
gtk_drawing_area_set_draw_func (da, draw_cb, NULL, NULL);
g_signal_connect_after (da, "resize", G_CALLBACK (resize_cb), NULL);
drag = gtk_gesture_drag_new ();
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY);
gtk_widget_add_controller (GTK_WIDGET (da), GTK_EVENT_CONTROLLER (drag));
g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), NULL);
g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), da);
g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), da);
}
#define APPLICATION_ID "com.github.ToshioCP.rect"
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN);
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
g_signal_connect (app, "shutdown", G_CALLBACK (app_shutdown), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/com/github/ToshioCP/rect">
<file>rect.ui</file>
</gresource>
</gresources>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkApplicationWindow" id="win">
<property name="default-width">800</property>
<property name="default-height">600</property>
<property name="resizable">FALSE</property>
<property name="title">Custom drawing</property>
<child>
<object class="GtkDrawingArea" id="da">
<property name="hexpand">TRUE</property>
<property name="vexpand">TRUE</property>
</object>
</child>
</object>
</interface>

86
src/dnd/dnd.c Normal file
View file

@ -0,0 +1,86 @@
#include <gtk/gtk.h>
static GtkCssProvider *provider = NULL;
static const char *format = "label {padding: 20px;} label#red {background: red;} "
"label#green {background: green;} label#blue {background: blue;} "
"label#canvas {color: %s; font-weight: bold; font-size: 72pt;}";
static gboolean
drop_cb (GtkDropTarget* self, const GValue* value, gdouble x, gdouble y, gpointer user_data) {
char *s;
s = g_strdup_printf (format, g_value_get_string (value));
gtk_css_provider_load_from_data (provider, s, -1);
g_free (s);
return TRUE;
}
static void
app_activate (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkWindow *win;
win = gtk_application_get_active_window (app);
gtk_window_present (win);
}
static void
app_startup (GApplication *application) {
GtkApplication *app = GTK_APPLICATION (application);
GtkBuilder *build;
GtkWindow *win;
GtkLabel *src_labels[3];
int i;
GtkLabel *canvas;
GtkDragSource *src;
GdkContentProvider* content;
GtkDropTarget *tgt;
GdkDisplay *display;
char *s;
build = gtk_builder_new_from_resource ("/com/github/ToshioCP/dnd/dnd.ui");
win = GTK_WINDOW (gtk_builder_get_object (build, "win"));
src_labels[0] = GTK_LABEL (gtk_builder_get_object (build, "red"));
src_labels[1] = GTK_LABEL (gtk_builder_get_object (build, "green"));
src_labels[2] = GTK_LABEL (gtk_builder_get_object (build, "blue"));
canvas = GTK_LABEL (gtk_builder_get_object (build, "canvas"));
gtk_window_set_application (win, app);
g_object_unref (build);
for (i=0; i<3; ++i) {
src = gtk_drag_source_new ();
content = gdk_content_provider_new_typed (G_TYPE_STRING, gtk_widget_get_name (GTK_WIDGET (src_labels[i])));
gtk_drag_source_set_content (src, content);
g_object_unref (content);
gtk_widget_add_controller (GTK_WIDGET (src_labels[i]), GTK_EVENT_CONTROLLER (src)); // The ownership of src is taken by the instance.
}
tgt = gtk_drop_target_new (G_TYPE_STRING, GDK_ACTION_COPY);
g_signal_connect (tgt, "drop", G_CALLBACK (drop_cb), NULL);
gtk_widget_add_controller (GTK_WIDGET (canvas), GTK_EVENT_CONTROLLER (tgt)); // The ownership of tgt is taken by the instance.
provider = gtk_css_provider_new ();
s = g_strdup_printf (format, "black");
gtk_css_provider_load_from_data (provider, s, -1);
g_free (s);
display = gdk_display_get_default ();
gtk_style_context_add_provider_for_display (display, GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref (provider); // The provider is still alive because the display owns it.
}
#define APPLICATION_ID "com.github.ToshioCP.dnd"
int
main (int argc, char **argv) {
GtkApplication *app;
int stat;
app = gtk_application_new (APPLICATION_ID, G_APPLICATION_HANDLES_OPEN);
g_signal_connect (app, "startup", G_CALLBACK (app_startup), NULL);
g_signal_connect (app, "activate", G_CALLBACK (app_activate), NULL);
stat =g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return stat;
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/com/github/ToshioCP/dnd">
<file>dnd.ui</file>
</gresource>
</gresources>

54
src/dnd/dnd.ui Normal file
View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<object class="GtkApplicationWindow" id="win">
<property name="default-width">800</property>
<property name="default-height">600</property>
<property name="resizable">FALSE</property>
<property name="title">Drag and Drop</property>
<child>
<object class="GtkBox">
<property name="hexpand">TRUE</property>
<property name="vexpand">TRUE</property>
<property name="orientation">GTK_ORIENTATION_VERTICAL</property>
<property name="spacing">5</property>
<child>
<object class="GtkBox">
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
<property name="homogeneous">TRUE</property>
<child>
<object class="GtkLabel" id="red">
<property name="label">RED</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
<property name="name">red</property>
</object>
</child>
<child>
<object class="GtkLabel" id="green">
<property name="label">GREEN</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
<property name="name">green</property>
</object>
</child>
<child>
<object class="GtkLabel" id="blue">
<property name="label">BLUE</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
<property name="name">blue</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkLabel" id="canvas">
<property name="label">CANVAS</property>
<property name="justify">GTK_JUSTIFY_CENTER</property>
<property name="name">canvas</property>
<property name="hexpand">TRUE</property>
<property name="vexpand">TRUE</property>
</object>
</child>
</object>
</child>
</object>
</interface>

8
src/dnd/meson.build Normal file
View file

@ -0,0 +1,8 @@
project('dnd', 'c')
gtkdep = dependency('gtk4')
gnome = import('gnome')
resources = gnome.compile_resources('resources','dnd.gresource.xml')
executable(meson.project_name(), 'dnd.c', resources, dependencies: gtkdep, export_dynamic: true, install: false)

View file

@ -1,26 +1,22 @@
# GtkDrawingArea and Cairo
This section and following sections are *not* updated yet and the programs were checked on the older GTK version than 4.10.
They will be updated in the near future.
If you want to draw shapes or paint images dynamically on the screen, use the GtkDrawingArea widget.
If you want to draw dynamically on the screen, like an image window of gimp graphics editor, the GtkDrawingArea widget is the most suitable widget.
You can freely draw or redraw an image in this widget.
This is called custom drawing.
GtkDrawingArea provides a cairo drawing context.
You can draw images with cairo library functions.
This section describes:
GtkDrawingArea provides a cairo drawing context so users can draw images by using cairo functions.
In this section, I will explain:
1. Cairo, but only briefly
1. Cairo, but briefly
2. GtkDrawingArea, with a very simple example.
## Cairo
Cairo is a set of two dimensional graphical drawing functions (or graphics library).
Cairo is a drawing library for two dimensional graphics.
There are a lot of documents on [Cairo's website](https://www.cairographics.org/).
If you aren't familiar with Cairo, it is worth reading the [tutorial](https://www.cairographics.org/tutorial/).
The following is an introduction to the Cairo library and how to use it.
First, you need to know about surfaces, sources, masks, destinations, cairo context and transformations.
The following is an introduction to the Cairo library.
First, you need to know surfaces, sources, masks, destinations, cairo context and transformations.
- A surface represents an image.
It is like a canvas.
@ -33,7 +29,7 @@ For example, `cairo_stroke` is a function to draw a path to the destination by t
- A transformation can be applied before the transfer completes.
The transformation which is applied is called affine, which is a mathematical term meaning transofrmations
that preserve straight lines.
Scaling, rotating, reflecting, shearing and translating are all examples of affine transformations.
Scaling, rotating, reflecting, shearing and translating are examples of affine transformations.
They are mathematically represented by matrix multiplication and vector addition.
In this section we don't use it, instead we will only use the identity transformation.
This means that the coordinates in the source and mask are the same as the coordinates in destination.
@ -69,28 +65,30 @@ Modern displays have this type of color depth.
Width and height are in pixels and given as integers.
- 14: Creates cairo context.
The surface given as an argument will be the destination of the context.
- 18: `cairo_set_source_rgb` creates a source pattern, which in this case is a solid white paint.
The second to fourth arguments are red, green and blue color values respectively, and they are
of type float. The values are between zero (0.0) and one (1.0), with
black being given by (0.0,0.0,0.0) and white by (1.0,1.0,1.0).
- 18: `cairo_set_source_rgb` creates a source pattern, which is a solid white paint.
The second to fourth arguments are red, green and blue color values respectively, and they are of type float.
The values are between zero (0.0) and one (1.0).
Black is (0.0,0.0,0.0) and white is (1.0,1.0,1.0).
- 19: `cairo_paint` copies everywhere in the source to destination.
The destination is filled with white pixels with this command.
- 21: Sets the source color to black.
- 22: `cairo_set_line_width` set the width of lines.
- 22: `cairo_set_line_width` sets the width of lines.
In this case, the line width is set to be two pixels and will end up that same size.
(It is because the transformation is identity.
If the transformation isn't identity, for example scaling with the factor three, the actual width in destination will be six (2x3=6) pixels.)
- 23: Draws a rectangle (square) on the mask.
The square is located at the center.
- 24: `cairo_stroke` transfer the source to destination through the rectangle in the mask.
- 27: Outputs the image to a png file `rectangle.png`.
- 28: Destroys the context. At the same time the source is destroyed.
- 29: Destroys the surface.
- 24: `cairo_stroke` transfers the source to destination through the rectangle in the mask.
- 31: Outputs the image to a png file `rectangle.png`.
- 32: Destroys the context. At the same time the source is destroyed.
- 33: Destroys the surface.
To compile this, change your current directory to `src/misc` and type the following.
$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
```
$ gcc `pkg-config --cflags cairo` cairo.c `pkg-config --libs cairo`
```
s
![rectangle.png](../image/rectangle.png)
See the [Cairo's website](https://www.cairographics.org/) for further information.
@ -108,7 +106,7 @@ The two functions `app_activate` and `draw_function` are important in this examp
- 22: Creates a GtkDrawingArea instance.
- 25: Sets a drawing function of the widget.
GtkDrawingArea widget uses the function to draw the contents of itself whenever its necessary.
GtkDrawingArea widget uses the function `draw_function` to draw the contents of itself whenever its necessary.
For example, when a user drag a mouse pointer and resize a top-level window, GtkDrawingArea also changes the size.
Then, the whole window needs to be redrawn.
For the information of `gtk_drawing_area_set_draw_func`, see [Gtk API Reference -- gtk\_drawing\_area\_set\_draw\_func](https://docs.gtk.org/gtk4/method.DrawingArea.set_draw_func.html).

View file

@ -1,182 +1,240 @@
# Combine GtkDrawingArea and TfeTextView
# Custom drawing
Now, we will make a new application which has GtkDrawingArea and TfeTextView in it.
Its name is "color".
If you write a name of a color in TfeTextView and click on the `run` button, then the color of GtkDrawingArea changes to the color given by you.
Custom drawing is to draw shapes dynamically.
This section shows an example of custom drawing.
You can draw rectangles by dragging the mouse.
![color](../image/color.png){width=7.0cm height=5.13cm}
Down the button.
The following colors are available.
(without new line charactor)
![down the button](../image/cd0.png){width=6cm height=4.83cm}
- white
- black
- red
- green
- blue
Move the mouse
In addition the following two options are also available.
![Move the mouse](../image/cd1.png){width=6cm height=4.83cm}
- light: Make the color of the drawing area lighter.
- dark: Make the color of the drawing area darker.
Up the button.
This application can only do very simple things.
However, it tells us that if we add powerful parser to it, we will be able to make it more efficient.
I want to show it to you in the later section by making a turtle graphics language like Logo program language.
![Up the button](../image/cd2.png){width=6cm height=4.83cm}
In this section, we focus on how to bind the two objects.
The programs are at `src/custom_drawing` directory.
Download the [repository](https://github.com/ToshioCP/Gtk4-tutorial) and see the directory.
There are four files.
## Color.ui and color.gresource.xml
- meson.build
- rect.c
- rect.gresource.xml
- rect.ui
First, We need to make the ui file of the widgets.
Title bar, four buttons in the tool bar, textview and drawing area.
The ui file is as follows.
## rect.gresource.xml
This file describes a ui file to compile.
The compiler glib-compile-resources uses it.
@@@include
color/color.ui
custom_drawing/rect.gresource.xml
@@@
- 10-53: The horizontal box `boxh1` makes a tool bar which has four buttons, `Run`, `Open`, `Save` and `Close`.
This is similar to the `tfe` text editor in [Section 9](sec9.src.md).
There are two differences.
`Run` button replaces `New` button.
A signal element is added to each button object.
It has "name" attribute which is a signal name and "handler" attribute which is the name of its signal handler.
Options "-WI, --export-dynamic" CFLAG is necessary when you compile the application.
You can achieve this by adding "export_dynamic: true" argument to the executable function in `meson.build`.
And be careful that the handler must be defined without 'static' class.
- 54-76: The horizontal box `boxh2` includes GtkScrolledWindow and GtkDrawingArea.
GtkBox has "homogeneous property" with TRUE value, so the two children have the same width in the box.
TfeTextView is a child of GtkScrolledWindow.
The prefix is `/com/github/ToshioCP/rect` and the file is `rect.ui`.
Therefore, GtkBuilder reads the resource from `/com/github/ToshioCP/rect/rect.ui`.
The xml file for the resource compiler is almost same as before.
Just substitute "color" for "tfe".
## rect.ui
The following is the ui file that defines the widgets.
There are two widgets which are GtkApplicationWindow and GtkDrawingArea.
The ids are `win` and `da` respectively.
@@@include
color/color.gresource.xml
custom_drawing/rect.ui
@@@
## Drawing function and surface
## rect.c
The main point of this program is a drawing function.
### GtkApplication
This program uses GtkApplication.
The application ID is `com.github.ToshioCP.rect`.
```c
#define APPLICATION_ID "com.github.ToshioCP.rect"
```
See [GNOME Developer Documentation](https://developer.gnome.org/documentation/tutorials/application-id.html) for further information.
The function `main` is called at the beginning of the application.
@@@include
color/colorapplication.c draw_func
custom_drawing/rect.c main
@@@
The `surface` variable in line 3 is a static variable.
It connects three signals and handlers.
~~~C
- startup: It is emitted after the application is registered to the system.
- activate: It is emitted when the application is activated.
- shutdown: It is emitted just before the application quits.
@@@include
custom_drawing/rect.c app_startup
@@@
The startup handler does three things.
- Builds the widgets.
- Initializes the GtkDrawingArea instance.
- Sets the drawing function
- Connects the "resize" signal and the handler.
- Creates the GtkGestureDrag instance and initializes it.
Gesture will be explained in this section later.
@@@include
custom_drawing/rect.c app_activate
@@@
The activate handler just shows the window.
### GtkDrawingArea
The program has two cairo surfaces and they are pointed by the global variables.
@@@if gfm
```C
static cairo_surface_t *surface = NULL;
~~~
static cairo_surface_t *surface_save = NULL;
```
@@@else
```{.C}
static cairo_surface_t *surface = NULL;
static cairo_surface_t *surface_save = NULL;
```
@@@end
The drawing function just copies the `surface` to its own surface with the `cairo_paint` function.
The surface (pointed by the static variable `surface`) is built by the `run` function.
The drawing process is as follows.
- Creates an image on `surface`.
- Copies `surface` to the cairo surface of the GtkDrawingArea.
- Calls ` gtk_widget_queue_draw (da)` to draw it if necessary.
They are created in the "resize" signal handler.
@@@include
color/colorapplication.c run
custom_drawing/rect.c resize_cb
@@@
- 9-10: Gets the string in the GtkTextBuffer and inserts it to `contents`.
- 11: If the variable `surface` points a surface instance, it is painted as follows.
- 12- 30: The source is set based on the string `contents` and copied to the surface with `cairo_paint`.
- 24,26: Alpha channel is used in "light" and "dark" procedure.
This callback is called when the GtkDrawingArea is shown.
It is the only call because the window is not resizable.
The drawing area just reflects the `surface`.
But one problem is resizing.
If a user resizes the main window, the drawing area is also resized.
It makes size difference between the surface and the drawing area.
So, the surface needs to be resized to fit the drawing area.
It creates image surfaces for `surface` and `surface_save`.
The `surface` surface is painted white, which is the background color.
It is accomplished by connecting the "resize" signal on the drawing area to a handler.
~~~C
g_signal_connect (GTK_DRAWING_AREA (da), "resize", G_CALLBACK (resize_cb), NULL);
~~~
The handler is as follows.
The drawing function copies `surface` to the GtkDrawingArea surface.
@@@include
color/colorapplication.c resize_cb
custom_drawing/rect.c draw_cb
@@@
If the variable `surface` sets a surface instance, it is destroyed.
A new surface is created and its size fits the drawing area.
The surface is assigned to the variable `surface`.
The function `run` is called and the surface is colored.
This function is called by the system when it needs to redraw the drawing area.
The signal is emitted when:
- The drawing area is realized (it appears on the display).
- It is changed (resized) while realized
So, the first surface is created when it is realized.
## Colorapplication.c
This is the main file.
- Builds widgets by GtkBuilder.
- Sets a drawing function for GtkDrawingArea.
And connects a handler to the "resize" signal on the GtkDrawingArea instance.
- Implements each call back function.
Particularly, `Run` signal handler is the point in this program.
The following is `colorapplication.c`.
Two surfaces `surface` and `surface_save` are destroyed before the application quits.
@@@include
color/colorapplication.c
custom_drawing/rect.c app_shutdown
@@@
- 4-8: Win, tv, da and surface are defined as static variables.
- 10-42: Run function.
- 44-63: Handlers for button signals.
- 65-71: Resize handler.
- 73-79: Drawing function.
- 81-84: Application activate handler.
It just shows the main window.
- 86-105: Application startup handler.
- 92- 97: It builds widgets according to the ui resource.
The static variables win, tv and da are assigned instances.
- 98: Connects "resize" signal and a handler.
- 99: Drawing function is set.
- 101-104: CSS for textview padding is set.
- 107-111: Application shutdown handler.
If there exists a surface instance, it will be destroyed.
- 116-129: A function `main`.
It creates a new application instance.
And connects three signals startup, shutdown and activate to their handlers.
It runs the application.
It releases the reference to the application and returns with `stat` value.
### GtkGestureDrag
## Meson.build
Gesture class is used to recognize human gestures such as click, drag, pan, swipe and so on.
It is a subclass of GtkEventController.
GtkGesture class is abstract and there are several implementations.
This file is almost same as before.
An argument "export_dynamic: true" is added to executable function.
- GtkGestureClick
- GtkGestureDrag
- GtkGesturePan
- GtkGestureSwipe
- other implementations
The program `rect.c` uses GtkGestureDrag.
It is the implementation for drags.
The parent-child relationship is as follows.
```
GObject -- GtkEventController -- GtkGesture -- GtkGestureSingle -- GtkGestureDrag
```
GtkGestureSingle is a subclass of GtkGesture and optimized for singe-touch and mouse gestures.
A GtkGestureDrag instance is created and initialized in the startup signal handler in `rect.c`.
See line 18 to 23 in the following.
@@@include
color/meson.build
custom_drawing/rect.c app_startup
@@@
## Build and try
- The function `gtk_gesture_drag_new` creates a new GtkGestureDrag instance.
- The function `gtk_gesture_single_set_button` sets the button number to listen to.
The constant `GDK_BUTTON_PRIMARY` is the left button of a mouse.
- The function `gtk_widget_add_controller` adds an event controller, gestures are descendants of the event controller, to a widget.
- Three signals and handlers are connected.
- drag-begin: Emitted when dragging starts.
- drag-update: Emitted when the dragging point moves.
- drag-end: Emitted when the dragging ends.
Type the following to compile the program.
The process during the drag is as follows.
$ meson _build
$ ninja -C _build
- start: save the surface and start points
- update: restore the surface and draw a thin rectangle between the start point and the current point of the mouse
- end: restore the surface and draw a thick rectangle between the start and end points.
The application is made in `_build` directory.
Type the following to execute it.
We need two global variables for the start point.
$ _build/color
@@@if gfm
```C
static double start_x;
static double start_y;
```
@@@else
```{.C}
static double start_x;
static double start_y;
```
@@@end
Type "red", "green", "blue", "white", black", "light" or "dark" in the TfeTextView.
No new line charactor is needed.
Then, click on the `Run` button.
Make sure the color of GtkDrawingArea changes.
The following is the handler for the "drag-begin" signal.
In this program TfeTextView is used to change the color.
You can use buttons or menus instead of textview.
Probably it is more appropriate.
Using textview is unnatural.
It is a good practice to make such application by yourself.
@@@include
custom_drawing/rect.c copy_surface drag_begin
@@@
- Copies `surface` to `surface_save`, which is an image just before the dragging.
- Stores the points to `start_x` and `start_y`.
@@@include
custom_drawing/rect.c drag_update
@@@
- Restores `surface` from `surface_save`.
- Draws a rectangle with thin lines.
- Calls `gtk_widget_queue_draw` to add the GtkDrawingArea to the queue to redraw.
@@@include
custom_drawing/rect.c drag_end
@@@
- Restores `surface` from `surface_save`.
- Draws a rectangle with thick lines.
- Calls `gtk_widget_queue_draw` to add the GtkDrawingArea to the queue to redraw.
## Build and run
Download the [repository](https://github.com/ToshioCP/Gtk4-tutorial).
Change your current directory to `src/custom_drawing`.
Run meson and ninja to build the program.
Type `_build/rect` to run the program.
Try to draw rectangles.
```
$ cd src/custom_drawing
$ meson setup _build
$ ninja -C _build
$ _build/rect
```
![The screen of rect program](../image/rect.png){width=12.4cm height=10cm}

View file

@ -2,7 +2,7 @@
A program `turtle` is an example with the combination of TfeTextView and GtkDrawingArea objects.
It is a very small interpreter but you can draw fractal curves with it.
The following diagram is a Koch curve, which is one of famous fractal curves.
The following diagram is a Koch curve, which is one of the famous fractal curves.
![Koch curve](turtle/image/turtle_koch.png){width=8cm height=5.11cm}

View file

@ -143,7 +143,7 @@ app_activate (GApplication *app, gpointer user_data) {
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA (clock), draw_clock, NULL, NULL);
g_timeout_add(1000, (GSourceFunc) time_handler, (gpointer) clock);
gtk_widget_show(win);
gtk_window_present(GTK_WINDOW (win));
}

251
src/tfc/tfc.gresource.c Normal file
View file

@ -0,0 +1,251 @@
#include <gio/gio.h>
#if defined (__ELF__) && ( __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 6))
# define SECTION __attribute__ ((section (".gresource.tfc"), aligned (8)))
#else
# define SECTION
#endif
static const SECTION union { const guint8 data[739]; const double alignment; void * const ptr;} tfc_resource_data = {
"\107\126\141\162\151\141\156\164\000\000\000\000\000\000\000\000"
"\030\000\000\000\310\000\000\000\000\000\000\050\006\000\000\000"
"\000\000\000\000\002\000\000\000\002\000\000\000\003\000\000\000"
"\003\000\000\000\005\000\000\000\130\345\125\000\004\000\000\000"
"\310\000\000\000\004\000\114\000\314\000\000\000\320\000\000\000"
"\224\135\334\227\003\000\000\000\320\000\000\000\007\000\114\000"
"\330\000\000\000\334\000\000\000\324\265\002\000\377\377\377\377"
"\334\000\000\000\001\000\114\000\340\000\000\000\344\000\000\000"
"\302\257\211\013\002\000\000\000\344\000\000\000\004\000\114\000"
"\350\000\000\000\354\000\000\000\214\044\075\224\001\000\000\000"
"\354\000\000\000\011\000\114\000\370\000\000\000\374\000\000\000"
"\141\320\165\220\000\000\000\000\374\000\000\000\006\000\166\000"
"\010\001\000\000\342\002\000\000\164\146\143\057\005\000\000\000"
"\147\151\164\150\165\142\057\000\004\000\000\000\057\000\000\000"
"\003\000\000\000\143\157\155\057\001\000\000\000\124\157\163\150"
"\151\157\103\120\057\000\000\000\000\000\000\000\164\146\143\056"
"\165\151\000\000\000\000\000\000\312\001\000\000\000\000\000\000"
"\074\077\170\155\154\040\166\145\162\163\151\157\156\075\042\061"
"\056\060\042\040\145\156\143\157\144\151\156\147\075\042\125\124"
"\106\055\070\042\077\076\012\074\151\156\164\145\162\146\141\143"
"\145\076\012\040\040\074\157\142\152\145\143\164\040\143\154\141"
"\163\163\075\042\107\164\153\101\160\160\154\151\143\141\164\151"
"\157\156\127\151\156\144\157\167\042\040\151\144\075\042\167\151"
"\156\042\076\012\040\040\040\040\074\160\162\157\160\145\162\164"
"\171\040\156\141\155\145\075\042\164\151\164\154\145\042\076\103"
"\154\157\143\153\074\057\160\162\157\160\145\162\164\171\076\012"
"\040\040\040\040\074\160\162\157\160\145\162\164\171\040\156\141"
"\155\145\075\042\144\145\146\141\165\154\164\055\167\151\144\164"
"\150\042\076\062\060\060\074\057\160\162\157\160\145\162\164\171"
"\076\012\040\040\040\040\074\160\162\157\160\145\162\164\171\040"
"\156\141\155\145\075\042\144\145\146\141\165\154\164\055\150\145"
"\151\147\150\164\042\076\062\060\060\074\057\160\162\157\160\145"
"\162\164\171\076\012\040\040\040\040\074\143\150\151\154\144\076"
"\012\040\040\040\040\040\040\074\157\142\152\145\143\164\040\143"
"\154\141\163\163\075\042\107\164\153\104\162\141\167\151\156\147"
"\101\162\145\141\042\040\151\144\075\042\143\154\157\143\153\042"
"\076\012\040\040\040\040\040\040\040\040\074\160\162\157\160\145"
"\162\164\171\040\156\141\155\145\075\042\150\145\170\160\141\156"
"\144\042\076\124\122\125\105\074\057\160\162\157\160\145\162\164"
"\171\076\012\040\040\040\040\040\040\040\040\074\160\162\157\160"
"\145\162\164\171\040\156\141\155\145\075\042\166\145\170\160\141"
"\156\144\042\076\124\122\125\105\074\057\160\162\157\160\145\162"
"\164\171\076\012\040\040\040\040\040\040\074\057\157\142\152\145"
"\143\164\076\012\040\040\040\040\074\057\143\150\151\154\144\076"
"\012\040\040\074\057\157\142\152\145\143\164\076\012\074\057\151"
"\156\164\145\162\146\141\143\145\076\012\000\000\050\165\165\141"
"\171\051" };
static GStaticResource static_resource = { tfc_resource_data.data, sizeof (tfc_resource_data.data) - 1 /* nul terminator */, NULL, NULL, NULL };
G_MODULE_EXPORT
GResource *tfc_get_resource (void);
GResource *tfc_get_resource (void)
{
return g_static_resource_get_resource (&static_resource);
}
/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#ifndef __G_CONSTRUCTOR_H__
#define __G_CONSTRUCTOR_H__
/*
If G_HAS_CONSTRUCTORS is true then the compiler support *both* constructors and
destructors, in a usable way, including e.g. on library unload. If not you're on
your own.
Some compilers need #pragma to handle this, which does not work with macros,
so the way you need to use this is (for constructors):
#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(my_constructor)
#endif
G_DEFINE_CONSTRUCTOR(my_constructor)
static void my_constructor(void) {
...
}
*/
#ifndef __GTK_DOC_IGNORE__
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7)
#define G_HAS_CONSTRUCTORS 1
#define G_DEFINE_CONSTRUCTOR(_func) static void __attribute__((constructor)) _func (void);
#define G_DEFINE_DESTRUCTOR(_func) static void __attribute__((destructor)) _func (void);
#elif defined (_MSC_VER) && (_MSC_VER >= 1500)
/* Visual studio 2008 and later has _Pragma */
/*
* Only try to include gslist.h if not already included via glib.h,
* so that items using gconstructor.h outside of GLib (such as
* GResources) continue to build properly.
*/
#ifndef __G_LIB_H__
#include "gslist.h"
#endif
#include <stdlib.h>
#define G_HAS_CONSTRUCTORS 1
/* We do some weird things to avoid the constructors being optimized
* away on VS2015 if WholeProgramOptimization is enabled. First we
* make a reference to the array from the wrapper to make sure its
* references. Then we use a pragma to make sure the wrapper function
* symbol is always included at the link stage. Also, the symbols
* need to be extern (but not dllexport), even though they are not
* really used from another object file.
*/
/* We need to account for differences between the mangling of symbols
* for x86 and x64/ARM/ARM64 programs, as symbols on x86 are prefixed
* with an underscore but symbols on x64/ARM/ARM64 are not.
*/
#ifdef _M_IX86
#define G_MSVC_SYMBOL_PREFIX "_"
#else
#define G_MSVC_SYMBOL_PREFIX ""
#endif
#define G_DEFINE_CONSTRUCTOR(_func) G_MSVC_CTOR (_func, G_MSVC_SYMBOL_PREFIX)
#define G_DEFINE_DESTRUCTOR(_func) G_MSVC_DTOR (_func, G_MSVC_SYMBOL_PREFIX)
#define G_MSVC_CTOR(_func,_sym_prefix) \
static void _func(void); \
extern int (* _array ## _func)(void); \
int _func ## _wrapper(void) { _func(); g_slist_find (NULL, _array ## _func); return 0; } \
__pragma(comment(linker,"/include:" _sym_prefix # _func "_wrapper")) \
__pragma(section(".CRT$XCU",read)) \
__declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _wrapper;
#define G_MSVC_DTOR(_func,_sym_prefix) \
static void _func(void); \
extern int (* _array ## _func)(void); \
int _func ## _constructor(void) { atexit (_func); g_slist_find (NULL, _array ## _func); return 0; } \
__pragma(comment(linker,"/include:" _sym_prefix # _func "_constructor")) \
__pragma(section(".CRT$XCU",read)) \
__declspec(allocate(".CRT$XCU")) int (* _array ## _func)(void) = _func ## _constructor;
#elif defined (_MSC_VER)
#define G_HAS_CONSTRUCTORS 1
/* Pre Visual studio 2008 must use #pragma section */
#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1
#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1
#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \
section(".CRT$XCU",read)
#define G_DEFINE_CONSTRUCTOR(_func) \
static void _func(void); \
static int _func ## _wrapper(void) { _func(); return 0; } \
__declspec(allocate(".CRT$XCU")) static int (*p)(void) = _func ## _wrapper;
#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \
section(".CRT$XCU",read)
#define G_DEFINE_DESTRUCTOR(_func) \
static void _func(void); \
static int _func ## _constructor(void) { atexit (_func); return 0; } \
__declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor;
#elif defined(__SUNPRO_C)
/* This is not tested, but i believe it should work, based on:
* http://opensource.apple.com/source/OpenSSL098/OpenSSL098-35/src/fips/fips_premain.c
*/
#define G_HAS_CONSTRUCTORS 1
#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1
#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1
#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \
init(_func)
#define G_DEFINE_CONSTRUCTOR(_func) \
static void _func(void);
#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \
fini(_func)
#define G_DEFINE_DESTRUCTOR(_func) \
static void _func(void);
#else
/* constructors not supported for this compiler */
#endif
#endif /* __GTK_DOC_IGNORE__ */
#endif /* __G_CONSTRUCTOR_H__ */
#ifdef G_HAS_CONSTRUCTORS
#ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA
#pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(tfcresource_constructor)
#endif
G_DEFINE_CONSTRUCTOR(tfcresource_constructor)
#ifdef G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA
#pragma G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(tfcresource_destructor)
#endif
G_DEFINE_DESTRUCTOR(tfcresource_destructor)
#else
#warning "Constructor not supported on this compiler, linking in resources will not work"
#endif
static void tfcresource_constructor (void)
{
g_static_resource_init (&static_resource);
}
static void tfcresource_destructor (void)
{
g_static_resource_fini (&static_resource);
}