1Title: Getting Started with GTK 2Slug: gtk-getting-started 3 4GTK is a [widget toolkit](http://en.wikipedia.org/wiki/Widget_toolkit). 5Each user interface created by GTK consists of widgets. This is implemented 6in C using [GObject](#gobject), an object-oriented framework for C. Widgets 7are organized in a hierarchy. The window widget is the main container. 8The user interface is then built by adding buttons, drop-down menus, input 9fields, and other widgets to the window. If you are creating complex user 10interfaces it is recommended to use GtkBuilder and its GTK-specific markup 11description language, instead of assembling the interface manually. You can 12also use a visual user interface editor, like [glade](https://glade.gnome.org/). 13 14GTK is event-driven. The toolkit listens for events such as a click 15on a button, and passes the event to your application. 16 17This chapter contains some tutorial information to get you started with 18GTK programming. It assumes that you have GTK, its dependencies and a C 19compiler installed and ready to use. If you need to build GTK itself first, 20refer to the [Compiling the GTK libraries](#gtk-compiling) section in this 21reference. 22 23## Basics 24 25To begin our introduction to GTK, we'll start with a very simple 26application. This program will create an empty 200 × 200 pixel 27window. 28 29![A window](window-default.png) 30 31Create a new file with the following content named `example-0.c`. 32 33```c 34#include <gtk/gtk.h> 35 36static void 37activate (GtkApplication* app, 38 gpointer user_data) 39{ 40 GtkWidget *window; 41 42 window = gtk_application_window_new (app); 43 gtk_window_set_title (GTK_WINDOW (window), "Window"); 44 gtk_window_set_default_size (GTK_WINDOW (window), 200, 200); 45 gtk_widget_show (window); 46} 47 48int 49main (int argc, 50 char **argv) 51{ 52 GtkApplication *app; 53 int status; 54 55 app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); 56 g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); 57 status = g_application_run (G_APPLICATION (app), argc, argv); 58 g_object_unref (app); 59 60 return status; 61} 62``` 63 64You can compile the program above with GCC using: 65 66``` 67gcc $( pkg-config --cflags gtk4 ) -o example-0 example-0.c $( pkg-config --libs gtk4 ) 68``` 69 70For more information on how to compile a GTK application, please 71refer to the [Compiling GTK Applications](#gtk-compiling) 72section in this reference. 73 74All GTK applications will, of course, include `gtk/gtk.h`, which declares 75functions, types and macros required by GTK applications. 76 77Even if GTK installs multiple header files, only the top-level `gtk/gtk.h` 78header can be directly included by third-party code. The compiler will abort 79with an error if any other header is directly included. 80 81In a GTK application, the purpose of the `main()` function is to create a 82[class@Gtk.Application] object and run it. In this example a 83[class@Gtk.Application] pointer named `app` is declared and then initialized 84using `gtk_application_new()`. 85 86When creating a [class@Gtk.Application], you need to pick an application 87identifier (a name) and pass it to [ctor@Gtk.Application.new] as parameter. For 88this example `org.gtk.example` is used. For choosing an identifier for your 89application, see [this guide](https://wiki.gnome.org/HowDoI/ChooseApplicationID). 90Lastly, [ctor@Gtk.Application.new] takes `GApplicationFlags` as input 91for your application, if your application would have special needs. 92 93Next the [activate signal](https://wiki.gnome.org/HowDoI/GtkApplication) is 94connected to the activate() function above the `main()` function. The `activate` 95signal will be emitted when your application is launched with `g_application_run()` 96on the line below. The `g_application_run()` call also takes as arguments the 97command line arguments (the `argc` count and the `argv` string array). 98Your application can override the command line handling, e.g. to open 99files passed on the commandline. 100 101Within `g_application_run()` the activate signal is sent and we then proceed 102into the activate() function of the application. This is where we construct 103our GTK window, so that a window is shown when the application is launched. 104The call to [ctor@Gtk.ApplicationWindow.new] will create a new 105[class@Gtk.ApplicationWindow] and store it inside the `window` pointer. The 106window will have a frame, a title bar, and window controls depending on the 107platform. 108 109A window title is set using [method@Gtk.Window.set_title]. This function 110takes a `GtkWindow` pointer and a string as input. As our `window` pointer 111is a `GtkWidget` pointer, we need to cast it to `GtkWindow`; instead of 112casting `window` via a typical C cast like `(GtkWindow*)`, `window` can be 113cast using the macro `GTK_WINDOW()`. `GTK_WINDOW()` will check if the 114pointer is an instance of the `GtkWindow` class, before casting, and emit a 115warning if the check fails. More information about this convention can be 116found [here](https://developer.gnome.org/gobject/stable/gtype-conventions.html). 117 118Finally the window size is set using [method@Gtk.Window.set_default_size] 119and the window is then shown by GTK via [method@Gtk.Widget.show]. 120 121When you close the window, by (for example) pressing the X button, the 122`g_application_run()` call returns with a number which is saved inside an 123integer variable named `status`. Afterwards, the `GtkApplication` object is 124freed from memory with `g_object_unref()`. Finally the status integer is 125returned and the application exits. 126 127While the program is running, GTK is receiving _events_. These are typically 128input events caused by the user interacting with your program, but also things 129like messages from the window manager or other applications. GTK processes 130these and as a result, _signals_ may be emitted on your widgets. Connecting 131handlers for these signals is how you normally make your program do something 132in response to user input. 133 134The following example is slightly more complex, and tries to 135showcase some of the capabilities of GTK. 136 137## Hello, World 138 139In the long tradition of programming languages and libraries, 140this example is called *Hello, World*. 141 142![Hello, world](hello-world.png) 143 144### Hello World in C 145 146Create a new file with the following content named `example-1.c`. 147 148```c 149#include <gtk/gtk.h> 150 151static void 152print_hello (GtkWidget *widget, 153 gpointer data) 154{ 155 g_print ("Hello World\n"); 156} 157 158static void 159activate (GtkApplication *app, 160 gpointer user_data) 161{ 162 GtkWidget *window; 163 GtkWidget *button; 164 165 window = gtk_application_window_new (app); 166 gtk_window_set_title (GTK_WINDOW (window), "Window"); 167 gtk_window_set_default_size (GTK_WINDOW (window), 200, 200); 168 169 button = gtk_button_new_with_label ("Hello World"); 170 gtk_widget_set_halign (button, GTK_ALIGN_CENTER); 171 gtk_widget_set_valign (button, GTK_ALIGN_CENTER); 172 173 g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); 174 g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_destroy), window); 175 176 gtk_window_set_child (GTK_WINDOW (window), button); 177 178 gtk_widget_show (window); 179} 180 181int 182main (int argc, 183 char **argv) 184{ 185 GtkApplication *app; 186 int status; 187 188 app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); 189 g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); 190 status = g_application_run (G_APPLICATION (app), argc, argv); 191 g_object_unref (app); 192 193 return status; 194} 195 196``` 197 198You can compile the program above with GCC using: 199 200``` 201gcc $( pkg-config --cflags gtk4 ) -o example-1 example-1.c $( pkg-config --libs gtk4 ) 202``` 203 204As seen above, `example-1.c` builds further upon `example-0.c` by adding a 205button to our window, with the label "Hello World". Two new `GtkWidget` 206pointers are declared to accomplish this, `button` and `box`. The box 207variable is created to store a [class@Gtk.Box], which is GTK's way of 208controlling the size and layout of buttons. 209 210The `GtkBox` widget is created with [ctor@Gtk.Box.new], which takes a 211[enum@Gtk.Orientation] enumeration value as parameter. The buttons which 212this box will contain can either be laid out horizontally or vertically. 213This does not matter in this particular case, as we are dealing with only 214one button. After initializing box with the newly created `GtkBox`, the code 215adds the box widget to the window widget using [method@Gtk.Window.set_child]. 216 217Next the `button` variable is initialized in similar manner. 218[ctor@Gtk.Button.new_with_label] is called which returns a 219[class@Gtk.Button] to be stored in `button`. Afterwards `button` is added to 220our `box`. 221 222Using `g_signal_connect()`, the button is connected to a function in our app called 223`print_hello()`, so that when the button is clicked, GTK will call this function. 224As the `print_hello()` function does not use any data as input, `NULL` is passed 225to it. `print_hello()` calls `g_print()` with the string "Hello World" which will 226print Hello World in a terminal if the GTK application was started from one. 227 228After connecting `print_hello()`, another signal is connected to the "clicked" 229state of the button using `g_signal_connect_swapped()`. This functions is similar 230to a `g_signal_connect()`, with the difference lying in how the callback function 231is treated; `g_signal_connect_swapped()` allows you to specify what the callback 232function should take as parameter by letting you pass it as data. In this case 233the function being called back is [method@Gtk.Window.destroy] and the `window` pointer 234is passed to it. This has the effect that when the button is clicked, the whole 235GTK window is destroyed. In contrast if a normal `g_signal_connect()` were used 236to connect the "clicked" signal with [method@Gtk.Window.destroy], then the function 237would be called on `button` (which would not go well, since the function expects 238a `GtkWindow` as argument). 239 240More information about creating buttons can be found 241[here](https://wiki.gnome.org/HowDoI/Buttons). 242 243The rest of the code in `example-1.c` is identical to `example-0.c`. The next 244section will elaborate further on how to add several GtkWidgets to your GTK 245application. 246 247## Packing 248 249When creating an application, you'll want to put more than one widget inside 250a window. When you do so, it becomes important to control how each widget is 251positioned and sized. This is where packing comes in. 252 253GTK comes with a large variety of _layout containers_ whose purpose it 254is to control the layout of the child widgets that are added to them. 255See [Layout containers](#LayoutContainers) for an overview. 256 257The following example shows how the GtkGrid container lets you 258arrange several buttons: 259 260![Grid packing](grid-packing.png) 261 262### Packing buttons 263 264Create a new file with the following content named `example-2.c`. 265 266```c 267#include <gtk/gtk.h> 268 269static void 270print_hello (GtkWidget *widget, 271 gpointer data) 272{ 273 g_print ("Hello World\n"); 274} 275 276static void 277activate (GtkApplication *app, 278 gpointer user_data) 279{ 280 GtkWidget *window; 281 GtkWidget *grid; 282 GtkWidget *button; 283 284 /* create a new window, and set its title */ 285 window = gtk_application_window_new (app); 286 gtk_window_set_title (GTK_WINDOW (window), "Window"); 287 288 /* Here we construct the container that is going pack our buttons */ 289 grid = gtk_grid_new (); 290 291 /* Pack the container in the window */ 292 gtk_window_set_child (GTK_WINDOW (window), grid); 293 294 button = gtk_button_new_with_label ("Button 1"); 295 g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); 296 297 /* Place the first button in the grid cell (0, 0), and make it fill 298 * just 1 cell horizontally and vertically (ie no spanning) 299 */ 300 gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 1, 1); 301 302 button = gtk_button_new_with_label ("Button 2"); 303 g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); 304 305 /* Place the second button in the grid cell (1, 0), and make it fill 306 * just 1 cell horizontally and vertically (ie no spanning) 307 */ 308 gtk_grid_attach (GTK_GRID (grid), button, 1, 0, 1, 1); 309 310 button = gtk_button_new_with_label ("Quit"); 311 g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_destroy), window); 312 313 /* Place the Quit button in the grid cell (0, 1), and make it 314 * span 2 columns. 315 */ 316 gtk_grid_attach (GTK_GRID (grid), button, 0, 1, 2, 1); 317 318 gtk_widget_show (window); 319 320} 321 322int 323main (int argc, 324 char **argv) 325{ 326 GtkApplication *app; 327 int status; 328 329 app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); 330 g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); 331 status = g_application_run (G_APPLICATION (app), argc, argv); 332 g_object_unref (app); 333 334 return status; 335} 336``` 337 338You can compile the program above with GCC using: 339 340``` 341gcc $( pkg-config --cflags gtk4 ) -o example-2 example-2.c $( pkg-config --libs gtk4 ) 342``` 343 344## Custom Drawing 345 346Many widgets, like buttons, do all their drawing themselves. You just tell 347them the label you want to see, and they figure out what font to use, draw 348the button outline and focus rectangle, etc. Sometimes, it is necessary to 349do some custom drawing. In that case, a [class@Gtk.DrawingArea] might be the right 350widget to use. It offers a canvas on which you can draw by setting its 351draw function. 352 353The contents of a widget often need to be partially or fully redrawn, e.g. 354when another window is moved and uncovers part of the widget, or when the 355window containing it is resized. It is also possible to explicitly cause a 356widget to be redrawn, by calling [method@Gtk.Widget.queue_draw]. GTK takes 357care of most of the details by providing a ready-to-use cairo context to the 358draw function. 359 360The following example shows how to use a draw function with GtkDrawingArea. 361It is a bit more complicated than the previous examples, since it also 362demonstrates input event handling with event controllers. 363 364![Drawing](drawing.png) 365 366### Drawing in response to input 367 368Create a new file with the following content named `example-4.c`. 369 370```c 371#include <gtk/gtk.h> 372 373/* Surface to store current scribbles */ 374static cairo_surface_t *surface = NULL; 375 376static void 377clear_surface (void) 378{ 379 cairo_t *cr; 380 381 cr = cairo_create (surface); 382 383 cairo_set_source_rgb (cr, 1, 1, 1); 384 cairo_paint (cr); 385 386 cairo_destroy (cr); 387} 388 389/* Create a new surface of the appropriate size to store our scribbles */ 390static void 391resize_cb (GtkWidget *widget, 392 int width, 393 int height, 394 gpointer data) 395{ 396 if (surface) 397 { 398 cairo_surface_destroy (surface); 399 surface = NULL; 400 } 401 402 if (gtk_native_get_surface (gtk_widget_get_native (widget))) 403 { 404 surface = gdk_surface_create_similar_surface (gtk_native_get_surface (gtk_widget_get_native (widget)), 405 CAIRO_CONTENT_COLOR, 406 gtk_widget_get_width (widget), 407 gtk_widget_get_height (widget)); 408 409 /* Initialize the surface to white */ 410 clear_surface (); 411 } 412} 413 414/* Redraw the screen from the surface. Note that the draw 415 * callback receives a ready-to-be-used cairo_t that is already 416 * clipped to only draw the exposed areas of the widget 417 */ 418static void 419draw_cb (GtkDrawingArea *drawing_area, 420 cairo_t *cr, 421 int width, 422 int height, 423 gpointer data) 424{ 425 cairo_set_source_surface (cr, surface, 0, 0); 426 cairo_paint (cr); 427} 428 429/* Draw a rectangle on the surface at the given position */ 430static void 431draw_brush (GtkWidget *widget, 432 double x, 433 double y) 434{ 435 cairo_t *cr; 436 437 /* Paint to the surface, where we store our state */ 438 cr = cairo_create (surface); 439 440 cairo_rectangle (cr, x - 3, y - 3, 6, 6); 441 cairo_fill (cr); 442 443 cairo_destroy (cr); 444 445 /* Now invalidate the drawing area. */ 446 gtk_widget_queue_draw (widget); 447} 448 449static double start_x; 450static double start_y; 451 452static void 453drag_begin (GtkGestureDrag *gesture, 454 double x, 455 double y, 456 GtkWidget *area) 457{ 458 start_x = x; 459 start_y = y; 460 461 draw_brush (area, x, y); 462} 463 464static void 465drag_update (GtkGestureDrag *gesture, 466 double x, 467 double y, 468 GtkWidget *area) 469{ 470 draw_brush (area, start_x + x, start_y + y); 471} 472 473static void 474drag_end (GtkGestureDrag *gesture, 475 double x, 476 double y, 477 GtkWidget *area) 478{ 479 draw_brush (area, start_x + x, start_y + y); 480} 481 482static void 483pressed (GtkGestureClick *gesture, 484 int n_press, 485 double x, 486 double y, 487 GtkWidget *area) 488{ 489 clear_surface (); 490 gtk_widget_queue_draw (area); 491} 492 493static void 494close_window (void) 495{ 496 if (surface) 497 cairo_surface_destroy (surface); 498} 499 500static void 501activate (GtkApplication *app, 502 gpointer user_data) 503{ 504 GtkWidget *window; 505 GtkWidget *frame; 506 GtkWidget *drawing_area; 507 GtkGesture *drag; 508 GtkGesture *press; 509 510 window = gtk_application_window_new (app); 511 gtk_window_set_title (GTK_WINDOW (window), "Drawing Area"); 512 513 g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL); 514 515 frame = gtk_frame_new (NULL); 516 gtk_window_set_child (GTK_WINDOW (window), frame); 517 518 drawing_area = gtk_drawing_area_new (); 519 /* set a minimum size */ 520 gtk_widget_set_size_request (drawing_area, 100, 100); 521 522 gtk_frame_set_child (GTK_FRAME (frame), drawing_area); 523 524 gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (drawing_area), draw_cb, NULL, NULL); 525 526 g_signal_connect_after (drawing_area, "resize", G_CALLBACK (resize_cb), NULL); 527 528 drag = gtk_gesture_drag_new (); 529 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY); 530 gtk_widget_add_controller (drawing_area, GTK_EVENT_CONTROLLER (drag)); 531 g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), drawing_area); 532 g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), drawing_area); 533 g_signal_connect (drag, "drag-end", G_CALLBACK (drag_end), drawing_area); 534 535 press = gtk_gesture_click_new (); 536 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (press), GDK_BUTTON_SECONDARY); 537 gtk_widget_add_controller (drawing_area, GTK_EVENT_CONTROLLER (press)); 538 539 g_signal_connect (press, "pressed", G_CALLBACK (pressed), drawing_area); 540 541 gtk_widget_show (window); 542} 543 544int 545main (int argc, 546 char **argv) 547{ 548 GtkApplication *app; 549 int status; 550 551 app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); 552 g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); 553 status = g_application_run (G_APPLICATION (app), argc, argv); 554 g_object_unref (app); 555 556 return status; 557} 558``` 559 560You can compile the program above with GCC using: 561 562``` 563gcc $( pkg-config --cflags gtk4 ) -o example-4 example-4.c $( pkg-config --libs gtk4 ) 564``` 565 566## Building user interfaces 567 568When constructing a more complicated user interface, with dozens 569or hundreds of widgets, doing all the setup work in C code is 570cumbersome, and making changes becomes next to impossible. 571 572Thankfully, GTK supports the separation of user interface 573layout from your business logic, by using UI descriptions in an 574XML format that can be parsed by the [class@Gtk.Builder] class. 575 576### Packing buttons with GtkBuilder 577 578Create a new file with the following content named `example-3.c`. 579 580```c 581#include <gtk/gtk.h> 582#include <glib/gstdio.h> 583 584static void 585print_hello (GtkWidget *widget, 586 gpointer data) 587{ 588 g_print ("Hello World\n"); 589} 590 591static void 592quit_cb (GtkWindow *window) 593{ 594 gtk_window_close (window); 595} 596 597static void 598activate (GtkApplication *app, 599 gpointer user_data) 600{ 601 /* Construct a GtkBuilder instance and load our UI description */ 602 GtkBuilder *builder = gtk_builder_new (); 603 gtk_builder_add_from_file (builder, "builder.ui", NULL); 604 605 /* Connect signal handlers to the constructed widgets. */ 606 GObject *window = gtk_builder_get_object (builder, "window"); 607 gtk_window_set_application (GTK_WINDOW (window), app); 608 609 GObject *button = gtk_builder_get_object (builder, "button1"); 610 g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); 611 612 button = gtk_builder_get_object (builder, "button2"); 613 g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL); 614 615 button = gtk_builder_get_object (builder, "quit"); 616 g_signal_connect_swapped (button, "clicked", G_CALLBACK (quit_cb), window); 617 618 gtk_widget_show (GTK_WIDGET (window)); 619 620 /* We do not need the builder any more */ 621 g_object_unref (builder); 622} 623 624int 625main (int argc, 626 char *argv[]) 627{ 628#ifdef GTK_SRCDIR 629 g_chdir (GTK_SRCDIR); 630#endif 631 632 GtkApplication *app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); 633 g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); 634 635 int status = g_application_run (G_APPLICATION (app), argc, argv); 636 g_object_unref (app); 637 638 return status; 639} 640``` 641 642Create a new file with the following content named `builder.ui`. 643 644```xml 645<?xml version="1.0" encoding="UTF-8"?> 646<interface> 647 <object id="window" class="GtkWindow"> 648 <property name="title">Grid</property> 649 <child> 650 <object id="grid" class="GtkGrid"> 651 <child> 652 <object id="button1" class="GtkButton"> 653 <property name="label">Button 1</property> 654 <layout> 655 <property name="column">0</property> 656 <property name="row">0</property> 657 </layout> 658 </object> 659 </child> 660 <child> 661 <object id="button2" class="GtkButton"> 662 <property name="label">Button 2</property> 663 <layout> 664 <property name="column">1</property> 665 <property name="row">0</property> 666 </layout> 667 </object> 668 </child> 669 <child> 670 <object id="quit" class="GtkButton"> 671 <property name="label">Quit</property> 672 <layout> 673 <property name="column">0</property> 674 <property name="row">1</property> 675 <property name="column-span">2</property> 676 </layout> 677 </object> 678 </child> 679 </object> 680 </child> 681 </object> 682</interface> 683``` 684 685You can compile the program above with GCC using: 686 687``` 688gcc $( pkg-config --cflags gtk4 ) -o example-3 example-3.c $( pkg-config --libs gtk4 ) 689``` 690 691Note that `GtkBuilder` can also be used to construct objects that are 692not widgets, such as tree models, adjustments, etc. That is the reason 693the method we use here is called [method@Gtk.Builder.get_object] and returns 694a `GObject` instead of a `GtkWidget`. 695 696Normally, you would pass a full path to [method@Gtk.Builder.add_from_file] to 697make the execution of your program independent of the current directory. 698A common location to install UI descriptions and similar data is 699`/usr/share/appname`. 700 701It is also possible to embed the UI description in the source code as a 702string and use [method@Gtk.Builder.add_from_string] to load it. But keeping the 703UI description in a separate file has several advantages: It is then possible 704to make minor adjustments to the UI without recompiling your program, and, 705more importantly, graphical UI editors such as [Glade](http://glade.gnome.org) 706can load the file and allow you to create and modify your UI by point-and-click. 707 708## Building applications 709 710An application consists of a number of files: 711 712The binary 713 : This gets installed in `/usr/bin`. 714 715A desktop file 716 : The desktop file provides important information about the application to 717 the desktop shell, such as its name, icon, D-Bus name, commandline to launch 718 it, etc. It is installed in `/usr/share/applications`. 719 720An icon 721 : The icon gets installed in `/usr/share/icons/hicolor/48x48/apps`, where it 722 will be found regardless of the current theme. 723 724A settings schema 725 : If the application uses GSettings, it will install its schema in 726 `/usr/share/glib-2.0/schemas`, so that tools like dconf-editor can find it. 727 728Other resources 729 : Other files, such as GtkBuilder ui files, are best loaded from 730 resources stored in the application binary itself. This eliminates the 731 need for most of the files that would traditionally be installed in 732 an application-specific location in `/usr/share`. 733 734GTK includes application support that is built on top of `GApplication`. In this 735tutorial we'll build a simple application by starting from scratch, adding more 736and more pieces over time. Along the way, we'll learn about [class@Gtk.Application], 737templates, resources, application menus, settings, [class@Gtk.HeaderBar], [class@Gtk.Stack], 738[class@Gtk.SearchBar], [class@Gtk.ListBox], and more. 739 740The full, buildable sources for these examples can be found in the 741`examples` directory of the GTK source distribution, or 742[online](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples) in the GTK 743source code repository. You can build each example separately by using make 744with the `Makefile.example` file. For more information, see the `README` 745included in the examples directory. 746 747### A trivial application 748 749When using `GtkApplication`, the `main()` function can be very simple. We just call 750`g_application_run()` and give it an instance of our application class. 751 752```c 753#include <gtk/gtk.h> 754 755#include "exampleapp.h" 756 757int 758main (int argc, char *argv[]) 759{ 760 return g_application_run (G_APPLICATION (example_app_new ()), argc, argv); 761} 762``` 763 764All the application logic is in the application class, which is a subclass of 765GtkApplication. Our example does not yet have any interesting functionality. 766All it does is open a window when it is activated without arguments, and open 767the files it is given, if it is started with arguments. 768 769To handle these two cases, we override the activate() vfunc, which gets 770called when the application is launched without commandline arguments, and 771the `open()` virtual function, which gets called when the application is 772launched with commandline arguments. 773 774To learn more about `GApplication` entry points, consult the GIO 775[documentation](https://developer.gnome.org/gio/stable/GApplication.html#GApplication.description). 776 777```c 778#include <gtk/gtk.h> 779 780#include "exampleapp.h" 781#include "exampleappwin.h" 782 783struct _ExampleApp 784{ 785 GtkApplication parent; 786}; 787 788G_DEFINE_TYPE(ExampleApp, example_app, GTK_TYPE_APPLICATION); 789 790static void 791example_app_init (ExampleApp *app) 792{ 793} 794 795static void 796example_app_activate (GApplication *app) 797{ 798 ExampleAppWindow *win; 799 800 win = example_app_window_new (EXAMPLE_APP (app)); 801 gtk_window_present (GTK_WINDOW (win)); 802} 803 804static void 805example_app_open (GApplication *app, 806 GFile **files, 807 int n_files, 808 const char *hint) 809{ 810 GList *windows; 811 ExampleAppWindow *win; 812 int i; 813 814 windows = gtk_application_get_windows (GTK_APPLICATION (app)); 815 if (windows) 816 win = EXAMPLE_APP_WINDOW (windows->data); 817 else 818 win = example_app_window_new (EXAMPLE_APP (app)); 819 820 for (i = 0; i < n_files; i++) 821 example_app_window_open (win, files[i]); 822 823 gtk_window_present (GTK_WINDOW (win)); 824} 825 826static void 827example_app_class_init (ExampleAppClass *class) 828{ 829 G_APPLICATION_CLASS (class)->activate = example_app_activate; 830 G_APPLICATION_CLASS (class)->open = example_app_open; 831} 832 833ExampleApp * 834example_app_new (void) 835{ 836 return g_object_new (EXAMPLE_APP_TYPE, 837 "application-id", "org.gtk.exampleapp", 838 "flags", G_APPLICATION_HANDLES_OPEN, 839 NULL); 840} 841``` 842 843Another important class that is part of the application support in GTK is 844`GtkApplicationWindow`. It is typically subclassed as well. Our subclass does 845not do anything yet, so we will just get an empty window. 846 847```c 848#include <gtk/gtk.h> 849 850#include "exampleapp.h" 851#include "exampleappwin.h" 852 853struct _ExampleAppWindow 854{ 855 GtkApplicationWindow parent; 856}; 857 858G_DEFINE_TYPE(ExampleAppWindow, example_app_window, GTK_TYPE_APPLICATION_WINDOW); 859 860static void 861example_app_window_init (ExampleAppWindow *app) 862{ 863} 864 865static void 866example_app_window_class_init (ExampleAppWindowClass *class) 867{ 868} 869 870ExampleAppWindow * 871example_app_window_new (ExampleApp *app) 872{ 873 return g_object_new (EXAMPLE_APP_WINDOW_TYPE, "application", app, NULL); 874} 875 876void 877example_app_window_open (ExampleAppWindow *win, 878 GFile *file) 879{ 880} 881``` 882 883As part of the initial setup of our application, we also 884create an icon and a desktop file. 885 886![An icon](exampleapp.png) 887 888``` 889[Desktop Entry] 890Type=Application 891Name=Example 892Icon=exampleapp 893StartupNotify=true 894Exec=@bindir@/exampleapp 895``` 896 897Note that `@bindir@` needs to be replaced with the actual path to the binary 898before this desktop file can be used. 899 900Here is what we've achieved so far: 901 902![An application](getting-started-app1.png) 903 904This does not look very impressive yet, but our application is already 905presenting itself on the session bus, it has single-instance semantics, 906and it accepts files as commandline arguments. 907 908### Populating the window 909 910In this step, we use a GtkBuilder template to associate a 911GtkBuilder ui file with our application window class. 912 913Our simple ui file gives the window a title, and puts a GtkStack 914widget as the main content. 915 916```xml 917<?xml version="1.0" encoding="UTF-8"?> 918<interface> 919 <template class="ExampleAppWindow" parent="GtkApplicationWindow"> 920 <property name="title" translatable="yes">Example Application</property> 921 <property name="default-width">600</property> 922 <property name="default-height">400</property> 923 <child> 924 <object class="GtkBox" id="content_box"> 925 <property name="orientation">vertical</property> 926 <child> 927 <object class="GtkStack" id="stack"/> 928 </child> 929 </object> 930 </child> 931 </template> 932</interface> 933``` 934 935To make use of this file in our application, we revisit our 936`GtkApplicationWindow` subclass, and call 937`gtk_widget_class_set_template_from_resource()` from the class init 938function to set the ui file as template for this class. We also 939add a call to `gtk_widget_init_template()` in the instance init 940function to instantiate the template for each instance of our 941class. 942 943```c 944 ... 945 946static void 947example_app_window_init (ExampleAppWindow *win) 948{ 949 gtk_widget_init_template (GTK_WIDGET (win)); 950} 951 952static void 953example_app_window_class_init (ExampleAppWindowClass *class) 954{ 955 gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), 956 "/org/gtk/exampleapp/window.ui"); 957} 958 959 ... 960``` 961 962([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application2/exampleappwin.c)) 963 964You may have noticed that we used the `_from_resource()` variant of the function 965that sets a template. Now we need to use 966[GLib's resource functionality](https://developer.gnome.org/gio/stable/GResource.html) 967to include the ui file in the binary. This is commonly done by listing all resources 968in a `.gresource.xml` file, such as this: 969 970```c 971<?xml version="1.0" encoding="UTF-8"?> 972<gresources> 973 <gresource prefix="/org/gtk/exampleapp"> 974 <file preprocess="xml-stripblanks">window.ui</file> 975 </gresource> 976</gresources> 977``` 978 979This file has to be converted into a C source file that will be compiled and linked 980into the application together with the other source files. To do so, we use the 981`glib-compile-resources` utility: 982 983``` 984glib-compile-resources exampleapp.gresource.xml --target=resources.c --generate-source 985``` 986 987The gnome module of the [Meson build system](https://mesonbuild.com) 988provides the [gnome.compile_resources()](https://mesonbuild.com/Gnome-module.html#gnomecompile_resources) 989method for this task. 990 991Our application now looks like this: 992 993![The application](getting-started-app2.png) 994 995### Opening files 996 997In this step, we make our application show the content of all the files 998that it is given on the commandline. 999 1000To this end, we add a member to the struct of our application window subclass 1001and keep a reference to the `GtkStack` there. The first member of the struct 1002should be the parent type from which the class is derived. Here, 1003`ExampleAppWindow` is derived from `GtkApplicationWindow`. The 1004`gtk_widget_class_bind_template_child()` function arranges things so that after 1005instantiating the template, the `stack` member of the struct will point to the 1006widget of the same name from the template. 1007 1008```c 1009... 1010 1011struct _ExampleAppWindow 1012{ 1013 GtkApplicationWindow parent; 1014 1015 GtkWidget *stack; 1016}; 1017 1018G_DEFINE_TYPE (ExampleAppWindow, example_app_window, GTK_TYPE_APPLICATION_WINDOW) 1019 1020... 1021 1022static void 1023example_app_window_class_init (ExampleAppWindowClass *class) 1024{ 1025 gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), 1026 "/org/gtk/exampleapp/window.ui"); 1027 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppWindow, stack); 1028} 1029 1030... 1031``` 1032 1033([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application3/exampleappwin.c)) 1034 1035Now we revisit the `example_app_window_open()` function that is called for each 1036commandline argument, and construct a GtkTextView that we then add as a page 1037to the stack: 1038 1039```c 1040... 1041 1042void 1043example_app_window_open (ExampleAppWindow *win, 1044 GFile *file) 1045{ 1046 char *basename; 1047 GtkWidget *scrolled, *view; 1048 char *contents; 1049 gsize length; 1050 1051 basename = g_file_get_basename (file); 1052 1053 scrolled = gtk_scrolled_window_new (); 1054 gtk_widget_set_hexpand (scrolled, TRUE); 1055 gtk_widget_set_vexpand (scrolled, TRUE); 1056 view = gtk_text_view_new (); 1057 gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE); 1058 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), FALSE); 1059 gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled), view); 1060 gtk_stack_add_titled (GTK_STACK (win->stack), scrolled, basename, basename); 1061 1062 if (g_file_load_contents (file, NULL, &contents, &length, NULL, NULL)) 1063 { 1064 GtkTextBuffer *buffer; 1065 1066 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); 1067 gtk_text_buffer_set_text (buffer, contents, length); 1068 g_free (contents); 1069 } 1070 1071 g_free (basename); 1072} 1073 1074... 1075``` 1076 1077([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application3/exampleappwin.c)) 1078 1079Lastly, we add a [class@Gtk.StackSwitcher] to the titlebar area in the UI file, and we 1080tell it to display information about our stack. 1081 1082The stack switcher gets all its information it needs to display tabs from 1083the stack that it belongs to. Here, we are passing the label to show for 1084each file as the last argument to the [method@Gtk.Stack.add_titled] 1085function. 1086 1087Our application is beginning to take shape: 1088 1089![Application window](getting-started-app3.png) 1090 1091### A menu 1092 1093The menu is shown at the right side of the headerbar. It is meant to collect 1094infrequently used actions that affect the whole application. 1095 1096Just like the window template, we specify our menu in a ui file, and add it 1097as a resource to our binary. 1098 1099```xml 1100<?xml version="1.0" encoding="UTF-8"?> 1101<interface> 1102 <menu id="menu"> 1103 <section> 1104 <item> 1105 <attribute name="label" translatable="yes">_Preferences</attribute> 1106 <attribute name="action">app.preferences</attribute> 1107 </item> 1108 </section> 1109 <section> 1110 <item> 1111 <attribute name="label" translatable="yes">_Quit</attribute> 1112 <attribute name="action">app.quit</attribute> 1113 </item> 1114 </section> 1115 </menu> 1116</interface> 1117``` 1118 1119To make the menu appear, we have to load the ui file and associate the 1120resulting menu model with the menu button that we've added to the headerbar. 1121Since menus work by activating GActions, we also have to add a suitable set 1122of actions to our application. 1123 1124Adding the actions is best done in the startup() vfunc, which is guaranteed 1125to be called once for each primary application instance: 1126 1127```c 1128... 1129 1130static void 1131preferences_activated (GSimpleAction *action, 1132 GVariant *parameter, 1133 gpointer app) 1134{ 1135} 1136 1137static void 1138quit_activated (GSimpleAction *action, 1139 GVariant *parameter, 1140 gpointer app) 1141{ 1142 g_application_quit (G_APPLICATION (app)); 1143} 1144 1145static GActionEntry app_entries[] = 1146{ 1147 { "preferences", preferences_activated, NULL, NULL, NULL }, 1148 { "quit", quit_activated, NULL, NULL, NULL } 1149}; 1150 1151static void 1152example_app_startup (GApplication *app) 1153{ 1154 GtkBuilder *builder; 1155 GMenuModel *app_menu; 1156 const char *quit_accels[2] = { "<Ctrl>Q", NULL }; 1157 1158 G_APPLICATION_CLASS (example_app_parent_class)->startup (app); 1159 1160 g_action_map_add_action_entries (G_ACTION_MAP (app), 1161 app_entries, G_N_ELEMENTS (app_entries), 1162 app); 1163 gtk_application_set_accels_for_action (GTK_APPLICATION (app), 1164 "app.quit", 1165 quit_accels); 1166} 1167 1168static void 1169example_app_class_init (ExampleAppClass *class) 1170{ 1171 G_APPLICATION_CLASS (class)->startup = example_app_startup; 1172 ... 1173} 1174 1175... 1176``` 1177 1178([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application4/exampleapp.c)) 1179 1180Our preferences menu item does not do anything yet, but the Quit menu item 1181is fully functional. Note that it can also be activated by the usual Ctrl-Q 1182shortcut. The shortcut was added with `gtk_application_set_accels_for_action()`. 1183 1184The application menu looks like this: 1185 1186![Application window](getting-started-app4.png) 1187 1188### A preference dialog 1189 1190A typical application will have a some preferences that should be remembered 1191from one run to the next. Even for our simple example application, we may 1192want to change the font that is used for the content. 1193 1194We are going to use `GSettings` to store our preferences. `GSettings` requires 1195a schema that describes our settings: 1196 1197```xml 1198<?xml version="1.0" encoding="UTF-8"?> 1199<schemalist> 1200 <schema path="/org/gtk/exampleapp/" id="org.gtk.exampleapp"> 1201 <key name="font" type="s"> 1202 <default>'Monospace 12'</default> 1203 <summary>Font</summary> 1204 <description>The font to be used for content.</description> 1205 </key> 1206 <key name="transition" type="s"> 1207 <choices> 1208 <choice value='none'/> 1209 <choice value='crossfade'/> 1210 <choice value='slide-left-right'/> 1211 </choices> 1212 <default>'none'</default> 1213 <summary>Transition</summary> 1214 <description>The transition to use when switching tabs.</description> 1215 </key> 1216 </schema> 1217</schemalist> 1218``` 1219 1220Before we can make use of this schema in our application, we need to compile 1221it into the binary form that GSettings expects. GIO provides 1222[macros](https://developer.gnome.org/gio/2.36/ch31s06.html) to do this in 1223autotools-based projects, and the gnome module of the Meson build system 1224provides the [gnome.compile_schemas()](https://mesonbuild.com/Gnome-module.html#gnomecompile_schemas) 1225method for this task. 1226 1227Next, we need to connect our settings to the widgets that they are supposed 1228to control. One convenient way to do this is to use `GSettings` bind 1229functionality to bind settings keys to object properties, as we do here 1230for the transition setting. 1231 1232```c 1233... 1234 1235static void 1236example_app_window_init (ExampleAppWindow *win) 1237{ 1238 gtk_widget_init_template (GTK_WIDGET (win)); 1239 win->settings = g_settings_new ("org.gtk.exampleapp"); 1240 1241 g_settings_bind (win->settings, "transition", 1242 win->stack, "transition-type", 1243 G_SETTINGS_BIND_DEFAULT); 1244} 1245 1246... 1247``` 1248 1249([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application5/exampleappwin.c)) 1250 1251The code to connect the font setting is a little more involved, since there 1252is no simple object property that it corresponds to, so we are not going to 1253go into that here. 1254 1255At this point, the application will already react if you change one of the 1256settings, e.g. using the `gsettings` command line tool. Of course, we expect 1257the application to provide a preference dialog for these. So lets do that 1258now. Our preference dialog will be a subclass of [class@Gtk.Dialog], and 1259we'll use the same techniques that we've already seen: templates, private 1260structs, settings bindings. 1261 1262Lets start with the template. 1263 1264```xml 1265<?xml version="1.0" encoding="UTF-8"?> 1266<interface> 1267 <template class="ExampleAppPrefs" parent="GtkDialog"> 1268 <property name="title" translatable="yes">Preferences</property> 1269 <property name="resizable">0</property> 1270 <property name="modal">1</property> 1271 <child internal-child="content_area"> 1272 <object class="GtkBox" id="content_area"> 1273 <child> 1274 <object class="GtkGrid" id="grid"> 1275 <property name="margin-start">12</property> 1276 <property name="margin-end">12</property> 1277 <property name="margin-top">12</property> 1278 <property name="margin-bottom">12</property> 1279 <property name="row-spacing">12</property> 1280 <property name="column-spacing">12</property> 1281 <child> 1282 <object class="GtkLabel" id="fontlabel"> 1283 <property name="label">_Font:</property> 1284 <property name="use-underline">1</property> 1285 <property name="mnemonic-widget">font</property> 1286 <property name="xalign">1</property> 1287 <layout> 1288 <property name="column">0</property> 1289 <property name="row">0</property> 1290 </layout> 1291 </object> 1292 </child> 1293 <child> 1294 <object class="GtkFontButton" id="font"> 1295 <layout> 1296 <property name="column">1</property> 1297 <property name="row">0</property> 1298 </layout> 1299 </object> 1300 </child> 1301 <child> 1302 <object class="GtkLabel" id="transitionlabel"> 1303 <property name="label">_Transition:</property> 1304 <property name="use-underline">1</property> 1305 <property name="mnemonic-widget">transition</property> 1306 <property name="xalign">1</property> 1307 <layout> 1308 <property name="column">0</property> 1309 <property name="row">1</property> 1310 </layout> 1311 </object> 1312 </child> 1313 <child> 1314 <object class="GtkComboBoxText" id="transition"> 1315 <items> 1316 <item translatable="yes" id="none">None</item> 1317 <item translatable="yes" id="crossfade">Fade</item> 1318 <item translatable="yes" id="slide-left-right">Slide</item> 1319 </items> 1320 <layout> 1321 <property name="column">1</property> 1322 <property name="row">1</property> 1323 </layout> 1324 </object> 1325 </child> 1326 </object> 1327 </child> 1328 </object> 1329 </child> 1330 </template> 1331</interface> 1332``` 1333 1334Next comes the dialog subclass. 1335 1336```c 1337#include <gtk/gtk.h> 1338 1339#include "exampleapp.h" 1340#include "exampleappwin.h" 1341#include "exampleappprefs.h" 1342 1343struct _ExampleAppPrefs 1344{ 1345 GtkDialog parent; 1346 1347 GSettings *settings; 1348 GtkWidget *font; 1349 GtkWidget *transition; 1350}; 1351 1352G_DEFINE_TYPE (ExampleAppPrefs, example_app_prefs, GTK_TYPE_DIALOG) 1353 1354static void 1355example_app_prefs_init (ExampleAppPrefs *prefs) 1356{ 1357 gtk_widget_init_template (GTK_WIDGET (prefs)); 1358 prefs->settings = g_settings_new ("org.gtk.exampleapp"); 1359 1360 g_settings_bind (prefs->settings, "font", 1361 prefs->font, "font", 1362 G_SETTINGS_BIND_DEFAULT); 1363 g_settings_bind (prefs->settings, "transition", 1364 prefs->transition, "active-id", 1365 G_SETTINGS_BIND_DEFAULT); 1366} 1367 1368static void 1369example_app_prefs_dispose (GObject *object) 1370{ 1371 ExampleAppPrefs *prefs; 1372 1373 prefs = EXAMPLE_APP_PREFS (object); 1374 1375 g_clear_object (&prefs->settings); 1376 1377 G_OBJECT_CLASS (example_app_prefs_parent_class)->dispose (object); 1378} 1379 1380static void 1381example_app_prefs_class_init (ExampleAppPrefsClass *class) 1382{ 1383 G_OBJECT_CLASS (class)->dispose = example_app_prefs_dispose; 1384 1385 gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class), 1386 "/org/gtk/exampleapp/prefs.ui"); 1387 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppPrefs, font); 1388 gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), ExampleAppPrefs, transition); 1389} 1390 1391ExampleAppPrefs * 1392example_app_prefs_new (ExampleAppWindow *win) 1393{ 1394 return g_object_new (EXAMPLE_APP_PREFS_TYPE, "transient-for", win, "use-header-bar", TRUE, NULL); 1395} 1396``` 1397 1398Now we revisit the `preferences_activated()` function in our application 1399class, and make it open a new preference dialog. 1400 1401```c 1402... 1403 1404static void 1405preferences_activated (GSimpleAction *action, 1406 GVariant *parameter, 1407 gpointer app) 1408{ 1409 ExampleAppPrefs *prefs; 1410 GtkWindow *win; 1411 1412 win = gtk_application_get_active_window (GTK_APPLICATION (app)); 1413 prefs = example_app_prefs_new (EXAMPLE_APP_WINDOW (win)); 1414 gtk_window_present (GTK_WINDOW (prefs)); 1415} 1416 1417... 1418``` 1419 1420([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application6/exampleapp.c)) 1421 1422After all this work, our application can now show a preference dialog 1423like this: 1424 1425![Preference dialog](getting-started-app6.png) 1426 1427### Adding a search bar 1428 1429We continue to flesh out the functionality of our application. For now, we 1430add search. GTK supports this with [class@Gtk.SearchEntry] and 1431[class@Gtk.SearchBar]. The search bar is a widget that can slide in from the 1432top to present a search entry. 1433 1434We add a toggle button to the header bar, which can be used to slide out 1435the search bar below the header bar. 1436 1437```xml 1438<?xml version="1.0" encoding="UTF-8"?> 1439<interface> 1440 <template class="ExampleAppWindow" parent="GtkApplicationWindow"> 1441 <property name="title" translatable="yes">Example Application</property> 1442 <property name="default-width">600</property> 1443 <property name="default-height">400</property> 1444 <child type="titlebar"> 1445 <object class="GtkHeaderBar" id="header"> 1446 <child type="title"> 1447 <object class="GtkStackSwitcher" id="tabs"> 1448 <property name="stack">stack</property> 1449 </object> 1450 </child> 1451 <child type="end"> 1452 <object class="GtkMenuButton" id="gears"> 1453 <property name="direction">none</property> 1454 </object> 1455 </child> 1456 <child type="end"> 1457 <object class="GtkToggleButton" id="search"> 1458 <property name="sensitive">0</property> 1459 <property name="icon-name">edit-find-symbolic</property> 1460 </object> 1461 </child> 1462 </object> 1463 </child> 1464 <child> 1465 <object class="GtkBox" id="content_box"> 1466 <property name="orientation">vertical</property> 1467 <child> 1468 <object class="GtkSearchBar" id="searchbar"> 1469 <child> 1470 <object class="GtkSearchEntry" id="searchentry"> 1471 <signal name="search-changed" handler="search_text_changed"/> 1472 </object> 1473 </child> 1474 </object> 1475 </child> 1476 <child> 1477 <object class="GtkStack" id="stack"> 1478 <signal name="notify::visible-child" handler="visible_child_changed"/> 1479 </object> 1480 </child> 1481 </object> 1482 </child> 1483 </template> 1484</interface> 1485``` 1486 1487Implementing the search needs quite a few code changes that we are not 1488going to completely go over here. The central piece of the search 1489implementation is a signal handler that listens for text changes in 1490the search entry. 1491 1492```c 1493... 1494 1495static void 1496search_text_changed (GtkEntry *entry, 1497 ExampleAppWindow *win) 1498{ 1499 const char *text; 1500 GtkWidget *tab; 1501 GtkWidget *view; 1502 GtkTextBuffer *buffer; 1503 GtkTextIter start, match_start, match_end; 1504 1505 text = gtk_editable_get_text (GTK_EDITABLE (entry)); 1506 1507 if (text[0] == '\0') 1508 return; 1509 1510 tab = gtk_stack_get_visible_child (GTK_STACK (win->stack)); 1511 view = gtk_scrolled_window_get_child (GTK_SCROLLED_WINDOW (tab)); 1512 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); 1513 1514 /* Very simple-minded search implementation */ 1515 gtk_text_buffer_get_start_iter (buffer, &start); 1516 if (gtk_text_iter_forward_search (&start, text, GTK_TEXT_SEARCH_CASE_INSENSITIVE, 1517 &match_start, &match_end, NULL)) 1518 { 1519 gtk_text_buffer_select_range (buffer, &match_start, &match_end); 1520 gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (view), &match_start, 1521 0.0, FALSE, 0.0, 0.0); 1522 } 1523} 1524 1525static void 1526example_app_window_init (ExampleAppWindow *win) 1527{ 1528 1529... 1530 1531 gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (class), search_text_changed); 1532 1533... 1534 1535} 1536 1537... 1538``` 1539 1540([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application7/exampleappwin.c)) 1541 1542With the search bar, our application now looks like this: 1543 1544![A search bar](getting-started-app7.png) 1545 1546### Adding a side bar 1547 1548As another piece of functionality, we are adding a sidebar, which demonstrates 1549[class@Gtk.MenuButton], [class@Gtk.Revealer] and [class@Gtk.ListBox]. 1550 1551```xml 1552<?xml version="1.0" encoding="UTF-8"?> 1553<interface> 1554 <template class="ExampleAppWindow" parent="GtkApplicationWindow"> 1555 <property name="title" translatable="yes">Example Application</property> 1556 <property name="default-width">600</property> 1557 <property name="default-height">400</property> 1558 <child type="titlebar"> 1559 <object class="GtkHeaderBar" id="header"> 1560 <child type="title"> 1561 <object class="GtkStackSwitcher" id="tabs"> 1562 <property name="stack">stack</property> 1563 </object> 1564 </child> 1565 <child type="end"> 1566 <object class="GtkToggleButton" id="search"> 1567 <property name="sensitive">0</property> 1568 <property name="icon-name">edit-find-symbolic</property> 1569 </object> 1570 </child> 1571 <child type="end"> 1572 <object class="GtkMenuButton" id="gears"> 1573 <property name="direction">none</property> 1574 </object> 1575 </child> 1576 </object> 1577 </child> 1578 <child> 1579 <object class="GtkBox" id="content_box"> 1580 <property name="orientation">vertical</property> 1581 <child> 1582 <object class="GtkSearchBar" id="searchbar"> 1583 <child> 1584 <object class="GtkSearchEntry" id="searchentry"> 1585 <signal name="search-changed" handler="search_text_changed"/> 1586 </object> 1587 </child> 1588 </object> 1589 </child> 1590 <child> 1591 <object class="GtkBox" id="hbox"> 1592 <child> 1593 <object class="GtkRevealer" id="sidebar"> 1594 <property name="transition-type">slide-right</property> 1595 <child> 1596 <object class="GtkScrolledWindow" id="sidebar-sw"> 1597 <property name="hscrollbar-policy">never</property> 1598 <child> 1599 <object class="GtkListBox" id="words"> 1600 <property name="selection-mode">none</property> 1601 </object> 1602 </child> 1603 </object> 1604 </child> 1605 </object> 1606 </child> 1607 <child> 1608 <object class="GtkStack" id="stack"> 1609 <signal name="notify::visible-child" handler="visible_child_changed"/> 1610 </object> 1611 </child> 1612 </object> 1613 </child> 1614 </object> 1615 </child> 1616 </template> 1617</interface> 1618``` 1619 1620The code to populate the sidebar with buttons for the words found in each 1621file is a little too involved to go into here. But we'll look at the code 1622to add a checkbutton for the new feature to the menu. 1623 1624```xml 1625<?xml version="1.0" encoding="UTF-8"?> 1626<interface> 1627 <menu id="menu"> 1628 <section> 1629 <item> 1630 <attribute name="label" translatable="yes">_Words</attribute> 1631 <attribute name="action">win.show-words</attribute> 1632 </item> 1633 <item> 1634 <attribute name="label" translatable="yes">_Preferences</attribute> 1635 <attribute name="action">app.preferences</attribute> 1636 </item> 1637 </section> 1638 <section> 1639 <item> 1640 <attribute name="label" translatable="yes">_Quit</attribute> 1641 <attribute name="action">app.quit</attribute> 1642 </item> 1643 </section> 1644 </menu> 1645</interface> 1646``` 1647 1648To connect the menuitem to the show-words setting, we use 1649a `GAction` corresponding to the given `GSettings` key. 1650 1651```c 1652... 1653 1654static void 1655example_app_window_init (ExampleAppWindow *win) 1656{ 1657 1658... 1659 1660 builder = gtk_builder_new_from_resource ("/org/gtk/exampleapp/gears-menu.ui"); 1661 menu = G_MENU_MODEL (gtk_builder_get_object (builder, "menu")); 1662 gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (priv->gears), menu); 1663 g_object_unref (builder); 1664 1665 action = g_settings_create_action (priv->settings, "show-words"); 1666 g_action_map_add_action (G_ACTION_MAP (win), action); 1667 g_object_unref (action); 1668} 1669 1670... 1671``` 1672 1673([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application8/exampleappwin.c)) 1674 1675What our application looks like now: 1676 1677![A sidebar](getting-started-app8.png) 1678 1679### Properties 1680 1681Widgets and other objects have many useful properties. 1682 1683Here we show some ways to use them in new and flexible ways, by wrapping 1684them in actions with `GPropertyAction` or by binding them with `GBinding`. 1685 1686To set this up, we add two labels to the header bar in our window template, 1687named `lines_label` and `lines`, and bind them to struct members in the 1688private struct, as we've seen a couple of times by now. 1689 1690We add a new "Lines" menu item to the gears menu, which triggers the 1691show-lines action: 1692 1693```xml 1694<?xml version="1.0" encoding="UTF-8"?> 1695<interface> 1696 <menu id="menu"> 1697 <section> 1698 <item> 1699 <attribute name="label" translatable="yes">_Words</attribute> 1700 <attribute name="action">win.show-words</attribute> 1701 </item> 1702 <item> 1703 <attribute name="label" translatable="yes">_Lines</attribute> 1704 <attribute name="action">win.show-lines</attribute> 1705 </item> 1706 <item> 1707 <attribute name="label" translatable="yes">_Preferences</attribute> 1708 <attribute name="action">app.preferences</attribute> 1709 </item> 1710 </section> 1711 <section> 1712 <item> 1713 <attribute name="label" translatable="yes">_Quit</attribute> 1714 <attribute name="action">app.quit</attribute> 1715 </item> 1716 </section> 1717 </menu> 1718</interface> 1719``` 1720 1721To make this menu item do something, we create a property action for the 1722visible property of the `lines` label, and add it to the actions of the 1723window. The effect of this is that the visibility of the label gets toggled 1724every time the action is activated. 1725 1726Since we want both labels to appear and disappear together, we bind 1727the visible property of the `lines_label` widget to the same property 1728of the `lines` widget. 1729 1730```c 1731... 1732 1733static void 1734example_app_window_init (ExampleAppWindow *win) 1735{ 1736 ... 1737 1738 action = (GAction*) g_property_action_new ("show-lines", win->lines, "visible"); 1739 g_action_map_add_action (G_ACTION_MAP (win), action); 1740 g_object_unref (action); 1741 1742 g_object_bind_property (win->lines, "visible", 1743 win->lines_label, "visible", 1744 G_BINDING_DEFAULT); 1745} 1746 1747... 1748``` 1749 1750([full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application9/exampleappwin.c)) 1751 1752We also need a function that counts the lines of the currently active tab, 1753and updates the `lines` label. See the [full source](https://gitlab.gnome.org/GNOME/gtk/blob/master/examples/application9/exampleappwin.c) 1754if you are interested in the details. 1755 1756This brings our example application to this appearance: 1757 1758![Full application](getting-started-app9.png) 1759