-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgetting_started.html
More file actions
852 lines (758 loc) · 36.8 KB
/
getting_started.html
File metadata and controls
852 lines (758 loc) · 36.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
<!DOCTYPE html>
<html lang="en">
<head>
<base href=".">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Getting Started with GTK+</title>
<link rel="stylesheet" href="assets/css/custom_bootstrap.css" type="text/css">
<link rel="stylesheet" href="assets/css/bootstrap-toc.min.css" type="text/css">
<link rel="stylesheet" href="assets/css/frontend.css" type="text/css">
<link rel="stylesheet" href="assets/css/jquery.mCustomScrollbar.min.css">
<link rel="stylesheet" href="assets/css/prism.css" type="text/css">
<script src="assets/js/mustache.min.js"></script>
<script src="assets/js/jquery.js"></script>
<script src="assets/js/scrollspy.js"></script>
<script src="assets/js/bootstrap.js"></script>
<script src="assets/js/typeahead.jquery.min.js"></script>
<script src="assets/js/search.js"></script>
<script src="assets/js/isotope.pkgd.min.js"></script>
<script src="assets/js/compare-versions.js"></script>
<script src="assets/js/jquery.mCustomScrollbar.concat.min.js"></script>
<script src="assets/js/bootstrap-toc.min.js"></script>
<script src="assets/js/jquery.touchSwipe.min.js"></script>
<script src="assets/js/anchor.min.js"></script>
<script src="assets/js/tag_filtering.js"></script>
<script src="assets/js/language_switching.js"></script>
<script src="assets/js/lines_around_headings.js"></script>
<script src="assets/js/prism-core.js"></script>
<script src="assets/js/prism-autoloader.js"></script>
<script src="assets/js/prism_autoloader_path_override.js"></script>
<script src="assets/js/trie.js"></script>
</head>
<body class="no-script
" data-spy="scroll" data-target="#toc" data-offset="70">
<script>
$('body').removeClass('no-script');
</script>
<nav class="navbar navbar-fixed-top navbar-default" id="topnav">
<div class="container-fluid">
<div class="navbar-right">
<a id="toc-toggle">
<span class="glyphicon glyphicon-menu-right"></span>
<span class="glyphicon glyphicon-menu-left"></span>
</a>
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-wrapper" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<form action="" class="navbar-form pull-right" id="navbar-search-form">
<div class="form-group has-feedback">
<input type="text" class="form-control input-sm" name="search" id="sidenav-lookup-field" placeholder="search" disabled>
<span class="glyphicon glyphicon-search form-control-feedback" id="search-mgn-glass"></span>
</div>
</form>
</div>
<div class="navbar-header">
<a id="sidenav-toggle">
<span class="glyphicon glyphicon-menu-right"></span>
<span class="glyphicon glyphicon-menu-left"></span>
</a>
<a id="home-link" href="index.html" class="hotdoc-navbar-brand">
<img src="assets/images/home.svg" alt="Home">
</a>
</div>
<div class="navbar-collapse collapse" id="navbar-wrapper">
<ul class="nav navbar-nav" id="menu">
</ul>
<div class="hidden-xs hidden-sm navbar-text navbar-center">
</div>
</div>
</div>
</nav>
<main>
<div data-extension="gi-extension" data-hotdoc-in-toplevel="True" data-hotdoc-project="gtk-4.0.0" data-hotdoc-ref="getting_started.html" class="page_container" id="page-wrapper" data-hotdoc-meta-gi-languages="['csharp']" data-hotdoc-meta-gi-language="c">
<script src="assets/js/utils.js"></script>
<div class="panel panel-collapse oc-collapsed" id="sidenav" data-hotdoc-role="navigation">
<script src="assets/js/navigation.js"></script>
<script src="assets/js/sitemap.js"></script>
</div>
<div id="body">
<div id="main">
<div id="page-description" data-hotdoc-source="getting_started.markdown" data-hotdoc-role="main">
<h1 id="getting-started-with-gtk">Getting Started with GTK+</h1>
<p>GTK+ is a <a href="http://en.wikipedia.org/wiki/Widget_toolkit">widget toolkit</a>.
Each user interface created by GTK+ consists of widgets. This is
implemented in C using <a href="getting_started.html#gobject">GObject</a>, an object-oriented framework
for C. Widgets are organized in a hierachy. The window widget is the
main container. The user interface is then built by adding buttons,
drop-down menus, input fields, and other widgets to the window. If you
are creating complex user interfaces it is recommended to use
#GtkBuilder and its GTK-specific markup description language, instead
of assembling the interface manually. You can also use a visual user
interface editor, like <a href="https://glade.gnome.org/">Glade</a>.</p>
<p>GTK+ is event-driven. The toolkit listens for events such as a click on
a button, and passes the event to your application.</p>
<p>This chapter contains some tutorial information to get you started with
GTK+ programming. It assumes that you have GTK+, its dependencies and a
C compiler installed and ready to use. If you need to build GTK+ itself
first, refer to the <a href="compiling.html">Compiling the GTK+ libraries</a>
section in this reference.</p>
<h1 id="basics">Basics</h1>
<p>To begin our introduction to GTK, we'll start with a simple signal-based
Gtk application. This program will create an empty 200 × 200 pixel
window.</p>
<p><img src="window-default.png" alt="" id></p>
<p>Create a new file with the following content named <code>example-0.c.</code></p>
<pre><code></code></pre>
<p>You can compile the program above with GCC using: <code>gcc `pkg-config --cflags gtk+-3.0` -o example-0 example-0.c `pkg-config --libs gtk+-3.0`</code></p>
<blockquote>
<p><strong>Note</strong></p>
<p>For more information on how to compile a GTK+ application, please
refer to the <a href="compiling.html">Compiling GTK+ Applications</a> section
in this reference.</p>
</blockquote>
<p>All GTK+ applications will, of course, include <code>gtk/gtk.h</code>, which
declares functions, types and macros required by GTK+ applications.</p>
<blockquote>
<p><strong>Warning</strong></p>
<p>Even if GTK+ installs multiple header files, only the top-level
<code>gtk/gtk.h</code> header can be directly included by third party code. The
compiler will abort with an error if any other header is directly
included.</p>
</blockquote>
<p>In a GTK+ application, the purpose of the main() function is to create a
#GtkApplication object and run it. In this example a #GtkApplication
pointer named <code>app</code> is called and then initialized using
gtk_application_new().</p>
<p>When creating a #GtkApplication you need to pick an application
identifier (a name) and input to gtk_application_new() as parameter.
For this example <code>org.gtk.example</code> is used but for choosing an
identifier for your application see <a href="https://wiki.gnome.org/HowDoI/ChooseApplicationID">this
guide</a>. Lastly
gtk_application_new() takes a GApplicationFlags as input for your
application, if your application would have special needs.</p>
<p>Next the <a href="https://wiki.gnome.org/HowDoI/GtkApplication">activate signal</a>
is connected to the activate() function above the main() functions. The
<code>activate</code> signal will be sent when your application is launched with
g_application_run() on the line below. The gtk_application_run()
also takes as arguments the pointers to the command line arguments
counter and string array; this allows GTK+ to parse specific command
line arguments that control the behavior of GTK+ itself. The parsed
arguments will be removed from the array, leaving the unrecognized ones
for your application to parse.</p>
<p>Within g_application_run the activate() signal is sent and we then
proceed into the <code>activate</code>() function of the application. Inside the
activate() function we want to construct our GTK window, so that a
window is shown when the application is launched. The call to
gtk_application_window_new() will create a new #GtkWindow and store
it inside the <code>window</code> pointer. The window will have a frame, a title
bar, and window controls depending on the platform.</p>
<p>A window title is set using gtk_window_set_title(). This function
takes a GtkWindow* pointer and a string as input. As our <code>window</code>
pointer is a GtkWidget pointer, we need to cast it to GtkWindow*. But
instead of casting <code>window</code> via <code>(GtkWindow*)</code>, <code>window</code> can be cast
using the macro <code>GTK_WINDOW()</code>. <code>GTK_WINDOW()</code> will check if the pointer
is an instance of the GtkWindow class, before casting, and emit a
warning if the check fails. More information about this convention can
be found
<a href="https://developer.gnome.org/gobject/stable/gtype-conventions.html">here</a>.</p>
<p>Finally the window size is set using gtk_window_set_default_size and
the window is then shown by GTK via gtk_widget_show_all().</p>
<p>When you exit the window, by for example pressing the X, the
g_application_run() in the main loop returns with a number which is
saved inside an integer named "status". Afterwards, the #GtkApplication
object is freed from memory with g_object_unref(). Finally the status
integer is returned and the GTK application exits.</p>
<p>While the program is running, GTK+ is receiving events. These are
typically input events caused by the user interacting with your program,
but also things like messages from the window manager or other
applications. GTK+ processes these and as a result, signals may be
emitted on your widgets. Connecting handlers for these signals is how
you normally make your program do something in response to user input.</p>
<p>The following example is slightly more complex, and tries to showcase
some of the capabilities of GTK+.</p>
<p>In the long tradition of programming languages and libraries, it is
called <em>Hello, World</em>.</p>
<p><img src="hello-world.png" alt="" id></p>
<p>Create a new file with the following content named example-1.c.</p>
<pre><code></code></pre>
<p>You can compile the program above with GCC using: <code>gcc `pkg-config --cflags gtk+-3.0` -o example-1 example-1.c `pkg-config --libs gtk+-3.0`</code></p>
<p>As seen above, example-1.c builds further upon example-0.c by adding a
button to our window, with the label "Hello World". Two new GtkWidget
pointers are declared to accomplish this, <code>button</code> and <code>button_box</code>. The
button_box variable is created to store a #GtkButtonBox which is
GTK+'s way of controlling the size and layout of buttons. The
#GtkButtonBox is created and assigned to gtk_button_box_new() which
takes a #GtkOrientation enum as parameter. The buttons which this box
will contain can either be stored horizontally or vertically but this
does not matter in this particular case as we are dealing with only one
button. After initializing button_box with horizontal orientation, the
code adds the button_box widget to the window widget using
gtk_container_add().</p>
<p>Next the <code>button</code> variable is initialized in similar manner.
gtk_button_new_with_label() is called which returns a GtkButton to
be stored inside <code>button</code>. Afterwards <code>button</code> is added to our
<code>button_box</code>. Using g_signal_connect the button is connected to a
function in our app called print_hello(), so that when the button is
clicked, GTK will call this function. As the print_hello() function
does not use any data as input, NULL is passed to it. print_hello()
calls g_print() with the string "Hello World" which will print Hello
World in a terminal if the GTK application was started from one.</p>
<p>After connecting print_hello(), another signal is connected to the
"clicked" state of the button using g_signal_connect_swapped(). This
functions is similar to a g_signal_connect() with the difference lying
in how the callback function is treated. g_signal_connect_swapped()
allow you to specify what the callback function should take as parameter
by letting you pass it as data. In this case the function being called
back is gtk_widget_destroy() and the <code>window</code> pointer is passed to it.
This has the effect that when the button is clicked, the whole GTK
window is destroyed. In contrast if a normal g_signal_connect() were
used to connect the "clicked" signal with gtk_widget_destroy(), then
the <code>button</code> itself would have been destroyed, not the window. More
information about creating buttons can be found
<a href="https://wiki.gnome.org/HowDoI/Buttons">here</a>.</p>
<p>The rest of the code in example-1.c is identical to example-0.c. Next
section will elaborate further on how to add several GtkWidgets to your
GTK application.</p>
<h1 id="packing">Packing</h1>
<p>When creating an application, you'll want to put more than one widget
inside a window. When you want to put more than one widget into a
window, it becomes important to control how each widget is positioned
and sized. This is where packing comes in.</p>
<p>GTK+ comes with a large variety of layout containers whose purpose it is
to control the layout of the child widgets that are added to them. See
<a href="gtk4-docs.markdown#layout-containers">Layout Containers</a> for an
overview.</p>
<p>The following example shows how the GtkGrid container lets you arrange
several buttons:</p>
<p><img src="grid-packing.png" alt="" id></p>
<p>Create a new file with the following content named example-2.c.</p>
<pre><code></code></pre>
<p>You can compile the program above with GCC using: <code>gcc `pkg-config --cflags gtk+-3.0` -o example-2 example-2.c `pkg-config --libs gtk+-3.0`</code></p>
<h1 id="building-user-interfaces">Building user interfaces</h1>
<p>When construcing a more complicated user interface, with dozens or
hundreds of widgets, doing all the setup work in C code is cumbersome,
and making changes becomes next to impossible.</p>
<p>Thankfully, GTK+ supports the separation of user interface layout from
your business logic, by using UI descriptions in an XML format that can
be parsed by the #GtkBuilder class.</p>
<p>Create a new file with the following content named example-3.c.</p>
<pre><code></code></pre>
<p>Create a new file with the following content named builder.ui.</p>
<pre><code></code></pre>
<p>You can compile the program above with GCC using: <code>gcc `pkg-config --cflags gtk+-3.0` -o example-3 example-3.c `pkg-config --libs gtk+-3.0`</code></p>
<p>Note that GtkBuilder can also be used to construct objects that are not
widgets, such as tree models, adjustments, etc. That is the reason the
method we use here is called gtk_builder_get_object() and returns a
GObject* instead of a GtkWidget*.</p>
<p>Normally, you would pass a full path to gtk_builder_add_from_file()
to make the execution of your program independent of the current
directory. A common location to install UI descriptions and similar data
is <code>/usr/share/appname</code>.</p>
<p>It is also possible to embed the UI description in the source code as a
string and use gtk_builder_add_from_string() to load it. But keeping
the UI description in a separate file has several advantages: It is then
possible to make minor adjustments to the UI without recompiling your
program, and, more importantly, graphical UI editors such as
<a href="http://glade.gnome.org">glade</a> can load the file and allow you to
create and modify your UI by point-and-click.</p>
<h1 id="building-applications">Building applications</h1>
<p>An application consists of a number of files:</p>
<ul>
<li>The binary<br>
This gets installed in
/usr/bin
.</li>
<li>A desktop file<br>
The desktop file provides important information about the
application to the desktop shell, such as its name, icon, D-Bus
name, commandline to launch it, etc. It is installed in
/usr/share/applications
.</li>
<li>An icon<br>
The icon gets installed in
/usr/share/icons/hicolor/48x48/apps
, where it will be found regardless of the current theme.</li>
<li>A settings schema<br>
If the application uses GSettings, it will install its schema in
/usr/share/glib-2.0/schemas
, so that tools like dconf-editor can find it.</li>
<li>Other resources<br>
Other files, such as GtkBuilder ui files, are best loaded from
resources stored in the application binary itself. This eliminates
the need for most of the files that would traditionally be installed
in an application-specific location in
/usr/share
.</li>
</ul>
<p>GTK+ includes application support that is built on top of
#GApplication. In this tutorial we'll build a simple application by
starting from scratch, adding more and more pieces over time. Along the
way, we'll learn about #GtkApplication, templates, resources,
application menus, settings, #GtkHeaderBar, #GtkStack, #GtkSearchBar,
#GtkListBox, and more.</p>
<p>The full, buildable sources for these examples can be found in the
examples/ directory of the GTK+ source distribution, or
<a href="https://git.gnome.org/browse/gtk+/tree/examples">online</a> in the GTK+
git repository. You can build each example separately by using make with
the <code>Makefile.example</code> file. For more information, see the <code>README</code>
included in the examples directory.</p>
<h2 id="a-trivial-application">A trivial application</h2>
<p>When using #GtkApplication, the main() function can be very simple. We
just call g_application_run() and give it an instance of our
application class.</p>
<pre><code></code></pre>
<p>All the application logic is in the application class, which is a
subclass of #GtkApplication. Our example does not yet have any
interesting functionality. All it does is open a window when it is
activated without arguments, and open the files it is given, if it is
started with arguments.</p>
<p>To handle these two cases, we override the activate() vfunc, which gets
called when the application is launched without commandline arguments,
and the open() vfunc, which gets called when the application is launched
with commandline arguments.</p>
<p>To learn more about GApplication entry points, consult the GIO
<a href="https://developer.gnome.org/gio/2.36/GApplication.html#GApplication.description">documentation</a>.</p>
<pre><code></code></pre>
<p>Another important class that is part of the application support in GTK+
is #GtkApplicationWindow. It is typically subclassed as well. Our
subclass does not do anything yet, so we will just get an empty window.</p>
<pre><code></code></pre>
<p>As part of the initial setup of our application, we also create an icon
and a desktop file.</p>
<p><img src="exampleapp.png" alt="" id></p>
<pre><code></code></pre>
<p>Note that @bindir@ needs to be replaced with the actual path to the
binary before this desktop file can be used.</p>
<p>Here is what we've achieved so far:</p>
<p><img src="getting-started-app1.png" alt="" id></p>
<p>This does not look very impressive yet, but our application is already
presenting itself on the session bus, it has single-instance semantics,
and it accepts files as commandline arguments.</p>
<h2 id="populating-the-window">Populating the window</h2>
<p>In this step, we use a #GtkBuilder template to associate a #GtkBuilder
ui file with our application window class.</p>
<p>Our simple ui file puts a #GtkHeaderBar on top of a #GtkStack widget.
The header bar contains a #GtkStackSwitcher, which is a standalone
widget to show a row of 'tabs' for the pages of a #GtkStack.</p>
<pre><code></code></pre>
<p>To make use of this file in our application, we revisit our
#GtkApplicationWindow subclass, and call
gtk_widget_class_set_template_from_resource() from the class init
function to set the ui file as template for this class. We also add a
call to gtk_widget_init_template() in the instance init function to
instantiate the template for each instance of our class.</p>
<pre><code> ...
static void
example_app_window_init (ExampleAppWindow *win)
{
gtk_widget_init_template (GTK_WIDGET (win));
}
static void
example_app_window_class_init (ExampleAppWindowClass *class)
{
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
"/org/gtk/exampleapp/window.ui");
}
...
</code></pre>
<p>(<a href="https://git.gnome.org/browse/gtk+/tree/examples/application2/exampleappwin.c">full
source</a>)</p>
<p>You may have noticed that we used the <code>_from_resource()</code> variant of the
function that sets a template. Now we need to use GLib's resource
functionality to include the ui file in the binary. This is commonly
done by listing all resources in a .gresource.xml file, such as this:</p>
<pre><code></code></pre>
<p>This file has to be converted into a C source file that will be compiled
and linked into the application together with the other source files. To
do so, we use the glib-compile-resources
utility:</p>
<pre><code> glib-compile-resources exampleapp.gresource.xml --target=resources.c --generate-source
</code></pre>
<p>Our application now looks like this:</p>
<p><img src="getting-started-app2.png" alt="" id></p>
<h2 id="opening-files">Opening files</h2>
<p>In this step, we make our application show the content of all the files
that it is given on the commandline.</p>
<p>To this end, we add a private struct to our application window subclass
and keep a reference to the #GtkStack there. The
gtk_widget_class_bind_template_child_private() function arranges
things so that after instantiating the template, the @stack member of
the private struct will point to the widget of the same name from the
template.</p>
<pre><code>...
struct _ExampleAppWindowPrivate
{
GtkWidget *stack;
};
G_DEFINE_TYPE_WITH_PRIVATE(ExampleAppWindow, example_app_window, GTK_TYPE_APPLICATION_WINDOW);
...
static void
example_app_window_class_init (ExampleAppWindowClass *class)
{
gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
"/org/gtk/exampleapp/window.ui");
gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (class), ExampleAppWindow, stack);
}
...
</code></pre>
<p>(<a href="https://git.gnome.org/browse/gtk+/tree/examples/application3/exampleappwin.c">full
source</a>)</p>
<p>Now we revisit the example_app_window_open() function that is called
for each commandline argument, and construct a GtkTextView that we then
add as a page to the stack:</p>
<pre><code>...
void
example_app_window_open (ExampleAppWindow *win,
GFile *file)
{
ExampleAppWindowPrivate *priv;
gchar *basename;
GtkWidget *scrolled, *view;
gchar *contents;
gsize length;
priv = example_app_window_get_instance_private (win);
basename = g_file_get_basename (file);
scrolled = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_show (scrolled);
gtk_widget_set_hexpand (scrolled, TRUE);
gtk_widget_set_vexpand (scrolled, TRUE);
view = gtk_text_view_new ();
gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE);
gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE);
gtk_widget_show (view);
gtk_container_add (GTK_CONTAINER (scrolled), view);
gtk_stack_add_titled (GTK_STACK (priv->stack), scrolled, basename, basename);
if (g_file_load_contents (file, NULL, &contents, &length, NULL, NULL))
{
GtkTextBuffer *buffer;
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
gtk_text_buffer_set_text (buffer, contents, length);
g_free (contents);
}
g_free (basename);
}
...
</code></pre>
<p>(<a href="https://git.gnome.org/browse/gtk+/tree/examples/application3/exampleappwin.c">full
source</a>)</p>
<p>Note that we did not have to touch the stack switcher at all. It gets
all its information from the stack that it belongs to. Here, we are
passing the label to show for each file as the last argument to the
gtk_stack_add_titled() function.</p>
<p>Our application is beginning to take shape:</p>
<p><img src="getting-started-app3.png" alt="" id></p>
<h2 id="an-application-menu">An application menu</h2>
<p>An application menu is shown by GNOME shell at the top of the screen. It
is meant to collect infrequently used actions that affect the whole
application.</p>
<p>Just like the window template, we specify our application menu in a ui
file, and add it as a resource to our binary.</p>
<pre><code></code></pre>
<p>To associate the app menu with the application, we have to call
gtk_application_set_app_menu(). Since app menus work by activating
#GActions, we also have to add a suitable set of actions to our
application.</p>
<p>Both of these tasks are best done in the startup() vfunc, which is
guaranteed to be called once for each primary application instance:</p>
<pre><code>...
static void
preferences_activated (GSimpleAction *action,
GVariant *parameter,
gpointer app)
{
}
static void
quit_activated (GSimpleAction *action,
GVariant *parameter,
gpointer app)
{
g_application_quit (G_APPLICATION (app));
}
static GActionEntry app_entries[] =
{
{ "preferences", preferences_activated, NULL, NULL, NULL },
{ "quit", quit_activated, NULL, NULL, NULL }
};
static void
example_app_startup (GApplication *app)
{
GtkBuilder *builder;
GMenuModel *app_menu;
const gchar *quit_accels[2] = { "<Ctrl>Q", NULL };
G_APPLICATION_CLASS (example_app_parent_class)->startup (app);
g_action_map_add_action_entries (G_ACTION_MAP (app),
app_entries, G_N_ELEMENTS (app_entries),
app);
gtk_application_set_accels_for_action (GTK_APPLICATION (app),
"app.quit",
quit_accels);
builder = gtk_builder_new_from_resource ("/org/gtk/exampleapp/app-menu.ui");
app_menu = G_MENU_MODEL (gtk_builder_get_object (builder, "appmenu"));
gtk_application_set_app_menu (GTK_APPLICATION (app), app_menu);
g_object_unref (builder);
}
static void
example_app_class_init (ExampleAppClass *class)
{
G_APPLICATION_CLASS (class)->startup = example_app_startup;
...
}
...
</code></pre>
<p>(<a href="https://git.gnome.org/browse/gtk+/tree/examples/application4/exampleapp.c">full
source</a>)</p>
<p>Our preferences menu item does not do anything yet, but the Quit menu
item is fully functional. Note that it can also be activated by the
usual Ctrl-Q shortcut. The shortcut was added with
gtk_application_set_accels_for_action().</p>
<p>The application menu looks like this:</p>
<p><img src="getting-started-app4.png" alt="" id></p>
<h2 id="a-preference-dialog">A preference dialog</h2>
<p>A typical application will have a some preferences that should be
remembered from one run to the next. Even for our simple example
application, we may want to change the font that is used for the
content.</p>
<p>We are going to use GSettings to store our preferences. GSettings
requires a schema that describes our settings:</p>
<pre><code></code></pre>
<p>Before we can make use of this schema in our application, we need to
compile it into the binary form that GSettings expects. GIO provides
<a href="https://developer.gnome.org/gio/2.36/ch31s06.html">macros</a> to do this
in autotools-based projects.</p>
<p>Next, we need to connect our settings to the widgets that they are
supposed to control. One convenient way to do this is to use GSettings
bind functionality to bind settings keys to object properties, as we do
here for the transition setting.</p>
<pre><code>...
static void
example_app_window_init (ExampleAppWindow *win)
{
ExampleAppWindowPrivate *priv;
priv = example_app_window_get_instance_private (win);
gtk_widget_init_template (GTK_WIDGET (win));
priv->settings = g_settings_new ("org.gtk.exampleapp");
g_settings_bind (priv->settings, "transition",
priv->stack, "transition-type",
G_SETTINGS_BIND_DEFAULT);
}
...
</code></pre>
<p>(<a href="https://git.gnome.org/browse/gtk+/tree/examples/application5/exampleappwin.c">full
source</a>)</p>
<p>The code to connect the font setting is a little more involved, since
there is no simple object property that it corresponds to, so we are not
going to go into that here.</p>
<p>At this point, the application will already react if you change one of
the settings, e.g. using the gsettings commandline tool. Of course, we
expect the application to provide a preference dialog for these. So lets
do that now. Our preference dialog will be a subclass of GtkDialog, and
we'll use the same techniques that we've already seen: templates,
private structs, settings bindings.</p>
<p>Lets start with the template.</p>
<pre><code></code></pre>
<p>Next comes the dialog subclass.</p>
<pre><code></code></pre>
<p>Now we revisit the <code>preferences_activated()</code> function in our application
class, and make it open a new preference dialog.</p>
<pre><code>...
static void
preferences_activated (GSimpleAction *action,
GVariant *parameter,
gpointer app)
{
ExampleAppPrefs *prefs;
GtkWindow *win;
win = gtk_application_get_active_window (GTK_APPLICATION (app));
prefs = example_app_prefs_new (EXAMPLE_APP_WINDOW (win));
gtk_window_present (GTK_WINDOW (prefs));
}
...
</code></pre>
<p>(<a href="https://git.gnome.org/browse/gtk+/tree/examples/application6/exampleapp.c">full
source</a>)</p>
<p>After all this work, our application can now show a preference dialog
like this:</p>
<p><img src="getting-started-app6.png" alt="" id></p>
<h2 id="adding-a-search-bar">Adding a search bar</h2>
<p>We continue to flesh out the functionality of our application. For now,
we add search. GTK+ supports this with #GtkSearchEntry and
#GtkSearchBar. The search bar is a widget that can slide in from the
top to present a search entry.</p>
<p>We add a toggle button to the header bar, which can be used to slide out
the search bar below the header bar.</p>
<pre><code></code></pre>
<p>Implementing the search needs quite a few code changes that we are not
going to completely go over here. The central piece of the search
implementation is a signal handler that listens for text changes in the
search entry.</p>
<pre><code>...
static void
search_text_changed (GtkEntry *entry,
ExampleAppWindow *win)
{
ExampleAppWindowPrivate *priv;
const gchar *text;
GtkWidget *tab;
GtkWidget *view;
GtkTextBuffer *buffer;
GtkTextIter start, match_start, match_end;
text = gtk_entry_get_text (entry);
if (text[0] == '\0')
return;
priv = example_app_window_get_instance_private (win);
tab = gtk_stack_get_visible_child (GTK_STACK (priv->stack));
view = gtk_bin_get_child (GTK_BIN (tab));
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
/* Very simple-minded search implementation */
gtk_text_buffer_get_start_iter (buffer, &start);
if (gtk_text_iter_forward_search (&start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE,
&match_start, &match_end, NULL))
{
gtk_text_buffer_select_range (buffer, &match_start, &match_end);
gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (view), &match_start,
0.0, FALSE, 0.0, 0.0);
}
}
static void
example_app_window_init (ExampleAppWindow *win)
{
...
gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), search_text_changed);
...
}
...
</code></pre>
<p>(<a href="https://git.gnome.org/browse/gtk+/tree/examples/application7/exampleappwin.c">full
source</a>)</p>
<p>With the search bar, our application now looks like this:</p>
<p><img src="getting-started-app7.png" alt="" id></p>
<h2 id="adding-a-side-bar">Adding a side bar</h2>
<p>As another piece of functionality, we are adding a sidebar, which
demonstrates #GtkMenuButton, #GtkRevealer and #GtkListBox.</p>
<pre><code></code></pre>
<p>The code to populate the sidebar with buttons for the words found in
each file is a little too involved to go into here. But we'll look at
the code to add the gears menu.</p>
<p>As expected by now, the gears menu is specified in a GtkBuilder ui file.</p>
<pre><code></code></pre>
<p>To connect the menuitem to the show-words setting, we use a #GAction
corresponding to the given #GSettings key.</p>
<pre><code>...
static void
example_app_window_init (ExampleAppWindow *win)
{
...
builder = gtk_builder_new_from_resource ("/org/gtk/exampleapp/gears-menu.ui");
menu = G_MENU_MODEL (gtk_builder_get_object (builder, "menu"));
gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (priv->gears), menu);
g_object_unref (builder);
action = g_settings_create_action (priv->settings, "show-words");
g_action_map_add_action (G_ACTION_MAP (win), action);
g_object_unref (action);
}
...
</code></pre>
<p>(<a href="https://git.gnome.org/browse/gtk+/tree/examples/application8/exampleappwin.c">full
source</a>)</p>
<p>What our application looks like now:</p>
<p><img src="getting-started-app8.png" alt="" id></p>
<h2 id="properties">Properties</h2>
<p>Widgets and other objects have many useful properties.</p>
<p>Here we show some ways to use them in new and flexible ways, by wrapping
them in actions with #GPropertyAction or by binding them with
#GBinding.</p>
<p>To set this up, we add two labels to the header bar in our window
template, named @lines_label and @lines, and bind them to struct
members in the private struct, as we've seen a couple of times by now.</p>
<p>We add a new "Lines" menu item to the gears menu, which triggers the
show-lines action:</p>
<pre><code></code></pre>
<p>To make this menu item do something, we create a property action for the
visible property of the @lines label, and add it to the actions of the
window. The effect of this is that the visibility of the label gets
toggled every time the action is activated.</p>
<p>Since we want both labels to appear and disappear together, we bind the
visible property of the @lines_label widget to the same property of the
@lines widget.</p>
<pre><code>...
static void
example_app_window_init (ExampleAppWindow *win)
{
...
action = (GAction*) g_property_action_new ("show-lines", priv->lines, "visible");
g_action_map_add_action (G_ACTION_MAP (win), action);
g_object_unref (action);
g_object_bind_property (priv->lines, "visible",
priv->lines_label, "visible",
G_BINDING_DEFAULT);
}
...
</code></pre>
<p>(<a href="https://git.gnome.org/browse/gtk+/tree/examples/application9/exampleappwin.c">full
source</a>)</p>
<p>We also need a function that counts the lines of the currently active
tab, and updates the @lines label. See the <a href="https://git.gnome.org/browse/gtk+/tree/examples/application9/exampleappwin.c">full
source</a>
if you are interested in the details.</p>
<p>This brings our example application to this appearance:</p>
<p><img src="getting-started-app9.png" alt="" id></p>
<h2 id="header-bar">Header bar</h2>
<p>Our application already uses a GtkHeaderBar, but so far it still gets a
'normal' window titlebar on top of that. This is a bit redundant, and we
will now tell GTK+ to use the header bar as replacement for the
titlebar. To do so, we move it around to be a direct child of the
window, and set its type to be titlebar.</p>
<pre><code></code></pre>
<p>A small extra bonus of using a header bar is that we get a fallback
application menu for free. Here is how the application now looks, if
this fallback is used.</p>
<p><img src="getting-started-app10.png" alt="" id></p>
<p>If we set up the window icon for our window, the menu button will use
that instead of the generic placeholder icon you see here.</p>
<h1 id="custom-drawing">Custom Drawing</h1>
<p>Many widgets, like buttons, do all their drawing themselves. You just
tell them the label you want to see, and they figure out what font to
use, draw the button outline and focus rectangle, etc. Sometimes, it is
necessary to do some custom drawing. In that case, a #GtkDrawingArea
might be the right widget to use. It offers a canvas on which you can
draw by connecting to the #GtkWidget::draw signal.</p>
<p>The contents of a widget often need to be partially or fully redrawn,
e.g. when another window is moved and uncovers part of the widget, or
when the window containing it is resized. It is also possible to
explicitly cause part or all of the widget to be redrawn, by calling
gtk_widget_queue_draw() or its variants. GTK+ takes care of most of
the details by providing a ready-to-use cairo context to the ::draw
signal handler.</p>
<p>The following example shows a ::draw signal handler. It is a bit more
complicated than the previous examples, since it also demonstrates input
event handling by means of ::button-press and ::motion-notify handlers.</p>
<p><img src="drawing.png" alt="" id></p>
<p>Create a new file with the following content named example-4.c.</p>
<pre><code></code></pre>
<p>You can compile the program above with GCC using: <code>gcc `pkg-config --cflags gtk+-3.0` -o example-4 example-4.c `pkg-config --libs gtk+-3.0`</code></p>
</div>
</div>
<div id="search_results">
<p>The results of the search are</p>
</div>
<hr>
<div id="footer">
</div>
</div>
<div id="toc-column">
<div class="edit-button">
</div>
<div id="toc-wrapper">
<nav id="toc"></nav>
</div>
</div>
</div>
</main>
</body>
<script src="assets/js/navbar_offset_scroller.js"></script>
</html>