├── .gitignore
├── COPYING
├── README.md
└── content
├── chapter-01-introduction
└── 01-introduction.md
├── chapter-02-getting-started
└── 02-getting-started.md
├── chapter-03-first-programs
├── 03-first-programs.md
├── code
│ ├── 01SimpleWindow.vala
│ └── 02HelloWorld.vala
└── screenshots
│ ├── 01SimpleWindow.png
│ └── 02HelloWorld.png
├── chapter-04-moving-on
├── 04-moving-on.md
├── code
│ └── 01_helloworld_2.vala
└── screenshots
│ └── 01HelloWorld2.png
├── chapter-05-layout-widgets
├── 05-layout-widgets.md
├── code
│ ├── 01Packbox.vala
│ ├── 02Grid.vala
│ └── 03Stack.vala
└── screenshots
│ ├── 01Packbox_1.png
│ ├── 01Packbox_2.png
│ ├── 01Packbox_3.png
│ ├── 02Grid.png
│ └── 03Stack.png
├── chapter-06-widget-overview
└── 06-widget-overview.md
├── chapter-07-button-widgets
├── 07-button-widgets.md
├── code
│ ├── 01Button.vala
│ ├── 02ToggleButton.vala
│ ├── 03RadioButton.vala
│ ├── 04LinkButton.vala
│ └── img.png
└── screenshots
│ ├── 01Button.png
│ ├── 02ToggleButton.png
│ └── 03LinkButton.png.png
├── chapter-08-numeric-and-text-entry
├── 08-numeric-and-text-entry.md
├── code
│ ├── 01GtkEntry.vala
│ ├── 02GtkSpinButton1.vala
│ └── 03GtkSpinButton2.vala
└── screenshots
│ ├── 01GtkEntry.png
│ └── 02SpinButton.png
└── chapter-xx-adjustments
└── xx-adjustments.md
/.gitignore:
--------------------------------------------------------------------------------
1 | exec
2 | example
3 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 |
2 | GNU Free Documentation License
3 | Version 1.3, 3 November 2008
4 |
5 |
6 | Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
7 |
# apt-get install libgtk-3-dev
29 |
30 | On Fedora, it would be:
31 |
32 | # yum install libgtk-3-dev
33 |
34 | You may also use your distribution's graphical software management
35 | tools like the Ubuntu Software Centre, Synaptic, or PackageKit.
36 |
37 |
38 | ### Installing Vala
39 |
40 | You can install Vala by building from source. First, one obtains
41 | the vala source code by downloading a tarball from the vala releases page
42 | ([https://wiki.gnome.org/Projects/Vala/Release](https://wiki.gnome.org/Projects/Vala/Release))
43 | and extracting it and running `./configure`, `make` and `make install`
44 | in the source folder.
45 |
46 | Alternatively, you may install vala from your distribution's repositories.
47 |
48 | On Debian and its derivatives, you would run:
49 | # apt-get install valac
50 |
51 | And on Fedora, one would run:
52 |
53 | # yum install valac
54 |
55 | You may also use your distribution's graphical software management
56 | tools like the Ubuntu Software Centre or Synaptic.
57 |
58 |
59 |
60 | ## References and Further Reading
61 |
62 | * Compiling the GTK+ libraries. [Online] Available from:
63 | [https://developer.gnome.org/gtk3/stable/gtk-building.html](https://developer.gnome.org/gtk3/stable/gtk-building.html)
64 | [Accessed 16 September 2014]
65 |
66 | * Vala Tools. [Online] Available from:
67 | [https://wiki.gnome.org/Projects/Vala/Tools](https://wiki.gnome.org/Projects/Vala/Tools)
68 | [Accessed 16 September 2014]
--------------------------------------------------------------------------------
/content/chapter-03-first-programs/03-first-programs.md:
--------------------------------------------------------------------------------
1 | # GTK Programming Using Vala: First Programs
2 |
3 | ## A First Program
4 |
5 | We will start with the simplest program possible: a program that will create a
6 | 200px by 200px window and has no way of exiting except to be killed using the
7 | shell.
8 |
9 | /* examples/chapter_03/01_simplewindow.vala */
10 |
11 | int main(string[] args) {
12 | Gtk.init (ref args);
13 |
14 | Gtk.Window window = new Window();
15 | window.show_all();
16 |
17 | Gtk.main();
18 |
19 | return 0;
20 | }
21 |
22 |
23 | Vala code is written in files with `.vala` extensions. The source files for
24 | the program are supplied as command line parameters to the Vala compiler `valac`,
25 | along with compiler flags.
26 |
27 | This code can also be found as a vala file in the
28 | [example code](https://github.com/abenga/valagtk3tutorial/tree/master/examples),
29 | in the file `examples/chapter_03/01_simplewindow.vala`. You can compile the
30 | program above by using
31 |
32 | $ valac --pkg gtk+-3.0 01_simplewindow.vala -o simplewindow
33 |
34 | `valac` is the name of the vala compiler. `--pkg gtk+3.0` tells the vala
35 | compiler to include the Gtk+ header files in the compilation. The `-o` flag
36 | tells the compiler what to call the compiled executable, in this case
37 | `simplewindow`. If this is omitted, the binary will have the same base name
38 | as the vala source file (`01_simplewindow`, in this case). The final argument
39 | is the name of the vala source file we are compiling.
40 |
41 | When compilation succeeds, one may execute the program by typing
42 |
43 | $ ./simplewindow
44 |
45 | into the console.
46 |
47 | A window similar the figure below should pop up on your display:
48 |
49 |
51 | Gtk.init(ref args)
61 |
62 | calls `Gtk.init()`, the initialization function for GTK. This function will set
63 | up GTK, the type system, the connection to the windowing environment, etc.
64 | `Gtk.init()` takes as arguments a reference to the command line arguments that
65 | were passed to the program. They are passed as a reference so that `Gtk.init()`
66 | is able to modify them.
67 |
68 | `Gtk.init()` sets up things such as the default visual and color map and calls
69 | `Gdk.init()`.It initializes the library for use, sets up default signal handlers,
70 | and checks the arguments passed to the application, looking for specific command
71 | line arguments that control the behavious of GTK itself, i.e.:
72 |
73 | * `--gtk-module`
74 |
75 | * `--g-fatal-warnings`
76 |
77 | * `--gtk-debug`
78 |
79 | * `--gtk-no-debug`
80 |
81 | * `--gdk-debug`
82 |
83 | * `--gdk-no-debug`
84 |
85 | * `--display`
86 |
87 | * `--sync`
88 |
89 | * `--name`
90 |
91 | * `--class`
92 |
93 | It removes these from the argument list, leaving anything it does not recognize
94 | for your application to parse or ignore.
95 |
96 | The next two lines:
97 |
98 | Gtk.Window window = new Window();
99 | window.show_all();
100 |
101 | create and display a window. The window constructor `Window()` takes a window
102 | type (one of `Gtk.WindowType.TOPLEVEL` and `Gtk.WindowType.POPUP`) as an
103 | argument that defines how the window will be drawn. The default value is
104 | `Gtk.WindowType.TOPLEVEL` which specifies that we want the window to undergo
105 | window manager decoration and placement. Rather than create a window of 0x0
106 | size, a window without children is set to 200x200 by default so you can still
107 | manipulate it.
108 |
109 | The line
110 |
111 | Gtk.main();
112 |
113 | enters the GTK main processing loop. This is a call you will see in every GTK
114 | application. When control reaches this point, GTK will sleep waiting for the
115 | user to interact with the application through events such as button or key
116 | presses, etc or for timeouts, or file input and output notifications to occur.
117 | In our simple example, however, these events are ignored.
118 |
119 |
120 | ## Hello World
121 |
122 | Here we will create a program with a widget (a button). It is the classic
123 | Hello World program. It will print out *"Hello World"* when the button is
124 | pressed and exit the program.
125 |
126 |
128 | /** examples/chapter_03/02_helloworld.vala */
132 |
133 | /* We define a HelloWorld class as a subclass Gtk.Window. */
134 | class HelloWorld : Gtk.Window {
135 |
136 | private Gtk.Button button;
137 |
138 | /* This is a callback function. The data arguments are ignored
139 | in this example. More on callbacks below. */
140 | public void hello () {
141 | stdout.printf("Hello World\n");
142 | }
143 |
144 | public bool on_delete_event () {
145 | /* If you return FALSE in the "delete_event" signal handler,
146 | GTK will emit the "destroy" signal. Returning TRUE means
147 | you don’t want the window to be destroyed.
148 | This is useful for popping up ’are you sure you want to quit?’
149 | type dialogs. */
150 | stdout.printf("delete event occurred\n");
151 | /* Change true to false and the main window will be destroyed with
152 | a "delete_event". */
153 | return false;
154 | }
155 |
156 | /* Another callback. */
157 | public void on_destroy() {
158 | Gtk.main_quit();
159 | }
160 |
161 | public HelloWorld () {
162 |
163 | /* When the window is given the "delete_event" signal (this is given
164 | by the window manager, usually by the "close" option, or on the
165 | titlebar), we ask it to call the on_delete_event() function as
166 | defined above. The data passed to the callback function is NULL
167 | and is ignored in the callback function. */
168 | this.delete_event.connect(this.on_delete_event);
169 |
170 | /* Here we connect the "destroy" event to a signal handler.
171 | This event occurs when we call gtk_widget_destroy() on the window,
172 | or if we return FALSE in the "on_delete_event" callback. */
173 | this.destroy.connect(this.on_destroy);
174 |
175 | /* Sets the border width of the window. */
176 | this.set_border_width(10);
177 |
178 | /* Creates a new button with the label "Hello World". */
179 | this.button = new Gtk.Button.with_label("Hello World");
180 |
181 | /* When the button receives the "clicked" signal, it will call the
182 | function hello() passing it None as its argument. The hello()
183 | function is defined above. */
184 | this.button.clicked.connect(this.hello);
185 |
186 | /* This will cause the window to be destroyed by calling
187 | Gtk.Widget.destroy(window) when "clicked". Again, the destroy
188 | signal could come from here, or the window manager. */
189 | GLib.Signal.connect_swapped(this.button, "clicked", (GLib.Callback)this.on_destroy, this);
190 |
191 | /* This packs the button into the window (a GTK container). */
192 | this.add(this.button);
193 |
194 | }
195 |
196 | public static int main(string[] args){
197 | Gtk.init(ref args);
198 |
199 | var hello = new HelloWorld();
200 |
201 | /* Show all the window and all the widgets contained therein. */
202 | hello.show_all();
203 | /* All Vala GTK applications must have a Gtk.main(). Control ends here
204 | and waits for an event (like a key press or mouse event) to occur. */
205 | Gtk.main();
206 |
207 | return 0;
208 | }
209 | }
210 |
211 |
212 |
213 | ## Compiling Hello World
214 |
215 | To compile *Hello World* above, you invoke `valac` using the command:
216 |
217 | $ valac --pkg gtk+-3.0 -o helloworld 02_helloworld.vala
218 |
219 | If you did this instead:
220 |
221 | $ valac --pkg gtk+-3.0 -C 02_helloworld.vala
222 |
223 | i.e. if you give `valac` the `-C` switch, it won't compile your program into a
224 | binary file. Instead it will output the intermediate C code for each of your
225 | Vala source files into a corresponding C source file, in this case
226 | `02_helloworld.c`. If you look at the content of these files you can see that
227 | programming a class in Vala is equivalent to the same task in C, but a whole lot
228 | more succinct.
229 |
230 |
231 | ## Theory of Signals and Callbacks
232 |
233 | Before we look in detail at *Hello World*, we'll discuss signals and callbacks.
234 | GTK is an event driven toolkit, which means it will sleep in `Gtk.main()` until
235 | an event occurs and control is passed to the appropriate function.
236 |
237 | This passing of control is done using the idea of *"signals"*. Signals are a
238 | system allowing a objects to emit events which can be received by arbitrary
239 | listeners. They form a convenient way for objects to inform each other about
240 | events.
241 |
242 | In Vala, only instances of classes descended from `GLib.Object` can emit
243 | signals. These signals are not the same as the Unix system signals, and are not
244 | implemented using them, although the terminology is almost identical. Through
245 | these signals, we can connect arbitrary application-specific events with any
246 | number of listeners.
247 |
248 | ### Signals and Callbacks in Vala
249 |
250 | Signals are usually defined in a class and interested parties register their
251 | callback functions to these signals of an instance of this class. The instance
252 | can emit the signal in the style of a method call and each callback function
253 | (referred to as a *handler*) connected to the signal will get called.
254 |
255 | For example,
256 |
257 | class Foo : Glib.Object {
258 | public signal void some_event ();// definition of the signal
259 |
260 | public void method () {
261 | some_event(); // emitting the signal (callbacks get invoked)
262 | }
263 | }
264 |
265 | void callback_a () {
266 | stdout.printf("Callback A\n");
267 | }
268 |
269 | void callback_b () {
270 | stdout.printf("Callback B\n");
271 | }
272 |
273 | void main () {
274 | var foo = new Foo ();
275 | foo.some_event.connect(callback_a); // connecting the callback functions
276 | foo.some_event.connect(callback_b);
277 | foo.method();
278 | }
279 |
280 | You may disconnect signal callbacks in one of two ways. The first (and simplest)
281 | is by calling `myobject.mysignal.disconnect(callback)`. In our example above,
282 | `callback_a` may be disconnected by calling
283 |
284 | foo.some_event.disconnect(callback_a);
285 |
286 | The second way is to store the return value of the `connect()` callback (it
287 | usually returns a `ulong` handler id) and then pass this signal id to
288 | `my_object.disconnect()`. Note that you have to invoke `disconnect()` on the
289 | object, not the signal. This is particularly useful when you connect closures
290 | (anonymous functions, also known as *lambda expressions*) as callbacks, for
291 | example:
292 |
293 | ulong handlerId = foo.some_event.connect (() => { /* Closure code here. */ });
294 | foo.disconnect(handlerId);
295 |
296 | You can also temporarily disable and reenable signal handlers with the
297 | `GLib.SignalHandler.block()` and `GLib.SignalHandler.unblock()` family of
298 | functions.
299 |
300 | void GLib.SignalHandler.block(void* instance,
301 | ulong handler_id);
302 |
303 | void GLib.SignalHandler.block_by_func (void* instance,
304 | void* func,
305 | void* data);
306 |
307 | void GLib.SignalHandler.unblock(void* object,
308 | ulong id );
309 |
310 | void GLib.SignalHandler.unblock_by_func(void* object,
311 | void* func,
312 | void* data );
313 |
314 |
315 | ### GTK Signals
316 |
317 | In GTK, every user event (keystroke or mouse move) is received from the
318 | [X](http://en.wikipedia.org/wiki/X_Window_System) server and generates a GTK
319 | event. When an event occurs, such as the press of a mouse button, the
320 | appropriate signal will be emitted by the widget that was pressed. This is how
321 | GTK does most of its useful work. There are signals that all widgets inherit,
322 | such as *"destroy"*, and there are signals that are widget specific, such as
323 | *"toggled"* on a toggle button.
324 |
325 | To make a button perform an action, we set up a signal handler to catch these
326 | signals and call the functions connected to this signal.
327 |
328 | handlerID = object.signal.connect(func)
329 |
330 | where `object` is the `Gtk.Widget` instance which will be emitting the signal,
331 | and the argument `func` is the "callback function" you wish to be called when it
332 | is caught. The method returns a handler id that can be used to disconnect or
333 | block the handler. `func` is called a "callback function" and is ordinarily a
334 | member function of a class that subclasses `Gtk.Widget`.
335 |
336 |
337 | ## Events
338 |
339 | In addition to the signal mechanism described above, there is a set of *events*
340 | that reflect the X event mechanism. Callbacks may also be attached to these
341 | events.
342 |
343 | These events are not the same as the signals that GTK widgets emit. Although
344 | many of these events result in corresponding signals being emitted, the events
345 | are often transformed or filtered along the way.
346 |
347 | * `event`
348 |
349 | * `button_press_event`
350 |
351 | * `button_release_event`
352 |
353 | * `scroll_event`
354 |
355 | * `motion_notify_event`
356 |
357 | * `delete_event`
358 |
359 | * `destroy_event`
360 |
361 | * `expose_event`
362 |
363 | * `key_press_event`
364 |
365 | * `key_release_event`
366 |
367 | * `enter_notify_event`
368 |
369 | * `leave_notify_event`
370 |
371 | * `configure_event`
372 |
373 | * `focus_in_event`
374 |
375 | * `focus_out_event`
376 |
377 | * `map_event`
378 |
379 | * `unmap_event`
380 |
381 | * `property_notify_event`
382 |
383 | * `selection_clear_event`
384 |
385 | * `selection_request_event`
386 |
387 | * `selection_notify_event`
388 |
389 | * `proximity_in_event`
390 |
391 | * `proximity_out_event`
392 |
393 | * `visibility_notify_event`
394 |
395 | * `client_event`
396 |
397 | * `no_expose_event`
398 |
399 | * `window_state_event`
400 |
401 | In order to connect a callback function to one of these events you use the
402 | method `object.signal.connect()`, as described above, where signal is one of the
403 | above events.
404 |
405 | `Gdk.Event` is a class whose type depends upon which of the above events has
406 | occurred. Possible values for the `Gdk.EventType` are:
407 |
408 | * `NOTHING`: a special code to indicate a null event.
409 |
410 | * `DELETE`: the window manager has requested that the toplevel window be
411 | hidden or destroyed, usually when the user clicks on a special icon in the
412 | title bar.
413 |
414 | * `DESTROY`: the window has been destroyed.
415 |
416 | * `EXPOSE`: all or part of the window has become visible and needs to be redrawn.
417 |
418 | * `MOTION_NOTIFY`: the pointer (usually a mouse) has moved.
419 |
420 | * `BUTTON_PRESS`: a mouse button has been pressed.
421 |
422 | * `2BUTTON_PRESS`: a mouse button has been double-clicked (clicked twice within
423 | a short period of time). Note that each click also generates a `BUTTON_PRESS`
424 | event.
425 |
426 | * `3BUTTON_PRESS`: a mouse button has been clicked 3 times in a short period of
427 | time. Note that each click also generates a `BUTTON_PRESS` event.
428 |
429 | * `BUTTON_RELEASE`: a mouse button has been released.
430 |
431 | * `KEY_PRESS`: a key has been pressed.
432 |
433 | * `KEY_RELEASE`: a key has been released.
434 |
435 | * `ENTER_NOTIFY`: the pointer has entered the window.
436 |
437 | * `LEAVE_NOTIFY`: the pointer has left the window.
438 |
439 | * `FOCUS_CHANGE`: the keyboard focus has entered or left the window.
440 |
441 | * `CONFIGURE`: the size, position or stacking order of the window has changed.
442 | Note that GTK discards these events for `WINDOW_CHILD` windows.
443 |
444 | * `MAP`: the window has been mapped.
445 |
446 | * `UNMAP`: the window has been unmapped.
447 |
448 | * `PROPERTY_NOTIFY`: a property on the window has been changed or deleted.
449 |
450 | * `SELECTION_CLEAR`: the application has lost ownership of a selection.
451 |
452 | * `SELECTION_REQUEST`: another application has requested a selection.
453 |
454 | * `SELECTION_NOTIFY`: a selection has been received.
455 |
456 | * `PROXIMITY_IN`: an input device has moved into contact with a sensing surface
457 | (e.g. a touchscreen or graphics tablet).
458 |
459 | * `PROXIMITY_OUT`: an input device has moved out of contact with a sensing
460 | surface.
461 |
462 | * `DRAG_ENTER`: the mouse has entered the window while a drag is in progress.
463 |
464 | * `DRAG_LEAVE`: the mouse has left the window while a drag is in progress.
465 |
466 | * `DRAG_MOTION`: the mouse has moved in the window while a drag is in progress.
467 |
468 | * `DRAG_STATUS`: the status of the drag operation initiated by the window has
469 | changed.
470 |
471 | * `DROP_START`: a drop operation onto the window has started.
472 |
473 | * `DROP_FINISHED`: the drop operation initiated by the window has completed.
474 |
475 | * `CLIENT_EVENT`: a message has been received from another application.
476 |
477 | * `VISIBILITY_NOTIFY`: the window visibility status has changed.
478 |
479 | * `SCROLL`: the scroll wheel was turned
480 |
481 | * `WINDOW_STATE`: the state of a window has changed. See
482 | [`Gdk.WindowState`](http://valadoc.org/#!api=gdk-3.0/Gdk.WindowState) for
483 | the possible window states
484 |
485 | * `SETTING`: a setting has been modified.
486 |
487 | * `OWNER_CHANGE`: the owner of a selection has changed.
488 |
489 | * `GRAB_BROKEN`: a pointer or keyboard grab was broken.
490 |
491 | * `DAMAGE`: the content of the window has been changed.
492 |
493 | * `EVENT_LAST`: marks the end of the GdkEventType enumeration.
494 |
495 |
496 | In order to connect a callback function to one of these events you use the
497 | function `object.signal.connect`, for example
498 |
499 | button.button_press_event.connect(func)
500 |
501 |
502 | ## Stepping Through Hello World
503 |
504 | Now that we know the theory behind this, let's clarify by walking through the
505 | example `02_helloworld.vala` program.
506 |
507 | The code
508 |
509 | class HelloWorld : Gtk.Window {
510 | ...
511 | }
512 |
513 | defines a class called `HelloWorld` that is a subclass of `Gtk.Window`, which
514 | means that it inherits all the public attributes and methods of the `Gtk.Window`
515 | class. While in the class, reference to the `HelloWorld` instance can be
516 | obtained using the keyword `this`.
517 |
518 | The `HelloWorld` class contains a single member: `button`, an instance of
519 | `Gtk.Button`.
520 |
521 | private Gtk.Button button;
522 |
523 | Now let's examine the callback methods.
524 |
525 | The following lines define the `hello()` callback method that will be called
526 | when `button` is "clicked".
527 |
528 | public void hello () {
529 | stdout.printf("Hello World\n");
530 | }
531 |
532 | When called the method prints *"Hello World"* to the console. In this case the
533 | data parameter is left out since the `hello()` method will never called with
534 | user data. An example in the next chapter will use the data argument to tell us
535 | which button was pressed.
536 |
537 | The next callback is a bit special. It will be called when the *"delete_event"*
538 | occurs and the window manager sends this event to the application. This happens,
539 | for example, when the user clicks the close button on the window. We have a
540 | choice here as to what to do about these events. We can ignore them, ask the
541 | user for additional confirmation, or simply quit the application.
542 |
543 | The value you return in this callback lets GTK know what action to take. By
544 | returning `true`, we let it know that we don't want to have the *"destroy"*
545 | signal emitted, keeping our application running. By returning `false`, we ask
546 | that *"destroy"* be emitted, which in turn will call our *"destroy"* signal
547 | handler (`on_destroy`). Note the comments have been removed for clarity.
548 |
549 | public bool on_delete_event () {
550 | stdout.printf("delete event occurred\n");
551 | return true;
552 | }
553 |
554 | The `on_destroy()` callback method causes the program to quit by calling
555 | `Gtk.main_quit()`. This function tells GTK that it is to exit from `Gtk.main()`
556 | when control is returned to it.
557 |
558 | public void on_destroy() {
559 | Gtk.main_quit();
560 | }
561 |
562 | The `HelloWorld` constructor `HelloWorld()` creates the window and widgets used
563 | by the program. The window (and its contents) is not displayed until we direct
564 | GTK to show the window near the end of our program.
565 |
566 | The next two lines illustrate two examples of connecting a signal handler to an
567 | object, in this case, the window. Here, the *"delete_event"* and *"destroy"*
568 | signals are caught. The first is emitted when we use the window manager to close
569 | the window, or when we use the `GtkWidget` `destroy()` method call. The second
570 | is emitted when, in the `on_delete_event` handler, we return `false`.
571 |
572 | this.delete_event.connect(this.on_delete_event);
573 |
574 | this.destroy.connect(this.on_destroy);
575 |
576 | The next line sets an attribute of a container object (in this case the window)
577 | to have a blank area along the inside of it 10 pixels wide where no widgets will
578 | be placed. There are other similar methods that we will look at in a later
579 | tutorial chapter.
580 |
581 | this.set_border_width(10);
582 |
583 | The next line
584 |
585 | this.button = new Gtk.Button.with_label("Hello World");
586 |
587 | creates a new button and saves a reference to it in `this.button`. The button
588 | will have the label *"Hello World"* when displayed.
589 |
590 | The line
591 |
592 | this.button.clicked.connect(this.hello);
593 |
594 | we attach a signal handler to the button so when it emits the *"clicked"*
595 | signal, our `hello()` callback method is called. We are not passing any data to
596 | `hello()` so we don't pass any arguments. The "clicked" signal is emitted when
597 | we click the button with our mouse pointer.
598 |
599 | We are also going to use this button to exit our program. This will illustrate
600 | how the *"destroy"* signal may come from either the window manager, or our
601 | program. When the button is *"clicked"*, same as above, it calls the first
602 | `hello()` callback function, and then causes *"destroy"* signal to be emitted.
603 | (It does these two in the order they are set up). You may connect as many
604 | callback functions as you need, and all will be executed in the order you
605 | connected them.
606 |
607 | Since we want to use the `GtkWidget` `destroy()` method that accepts one
608 | argument (the widget to be destroyed - in this case the window), we use the
609 | `GLib.Signal.connect_swapped()` method and pass it the object to which we are
610 | connecting it to, the signal to watch out for, the callback to be run and a
611 | reference to the the window to be destroyed.
612 |
613 | When the `Gtk.Widget destroy()` method is called it will cause the *"destroy"*
614 | signal to be emitted from the window which will in turn cause the `HelloWorld`
615 | `on_destroy()` method to be called to end the program.
616 |
617 | The next line
618 |
619 | this.add(this.button);
620 |
621 | is a packing call, which will be explained in depth later on in a later
622 | chapter on Packing Widgets. But it is fairly easy to understand. It simply tells
623 | GTK that the button is to be placed in the window where it will be displayed.
624 | Note that a GTK container can only contain one widget. There are other widgets,
625 | described later, that are designed to lay out multiple widgets in various ways.
626 |
627 | Now we have everything set up the way we want it to be. All the signal handlers
628 | are in place, and the button has been placed in the window.
629 |
630 |
631 | We now define the `main()` method. This is the point at which execution of
632 | our program begins.
633 |
634 | The line
635 |
636 | var hello = new HelloWorld();
637 |
638 | creates an instance of the `HelloWorld` class and saves a reference to
639 | it in the `hello` variable.
640 |
641 | and in the line
642 |
643 | hello.show_all();
644 |
645 | weask GTK to "show" the widgets on the screen using the line.
646 |
647 | We then call the `Gtk.main()` function which sleeps and waits for the user to
648 | interact with the program interface.
649 |
650 | Gtk.main();
651 |
652 | Now, when we click the mouse button on a GTK button, the widget emits a
653 | *"clicked"* signal. In order for us to use this information, our program sets up
654 | a signal handler to catch that signal, which dispatches the function of our
655 | choice. In our example, when the button we created is clicked, the `hello()`
656 | method is called with no arguments, and then the next handler for this signal is
657 | called. The next handler calls the widget `destroy()` function with the window
658 | as its argument thereby causing the window to emit the *"destroy"* signal, which
659 | is caught, and calls our `HelloWorld` `destroy()` method.
660 |
661 | Another course of events is to use the window manager to kill the window, which
662 | will cause the *"delete_event"* to be emitted. This will call our *"delete_event"*
663 | handler. If we return `true` here, the window will be left as is and nothing
664 | will happen. Returning `false` will cause GTK to emit the *"destroy"* signal
665 | that causes the `HelloWorld` *"destroy"* callback to be called, exiting GTK.
666 |
667 |
668 | ## References and Further Reading
669 |
670 | * The GTK+ Tutorial: Getting Started. [Online] Available from:
671 | [https://developer.gnome.org/gtk-tutorial/2.90/c39.html](https://developer.gnome.org/gtk-tutorial/2.90/c39.html)
672 | [Accessed 16 September 2014]
673 |
674 | * Vala Documentation: Signals and Callbacks. [Online] Available from:
675 | [https://wiki.gnome.org/Projects/Vala/SignalsAndCallbacks](https://wiki.gnome.org/Projects/Vala/SignalsAndCallbacks)
676 | [Accessed 16 September 2014]
677 |
678 | * Valadoc (Vala online package binding reference documentation) [Online] Available from:
679 | [http://valadoc.org/#!api=gobject-2.0/GLib.SignalHandler](http://valadoc.org/#!api=gobject-2.0/GLib.SignalHandler)
680 | [Accessed 16 September 2014]
681 |
--------------------------------------------------------------------------------
/content/chapter-03-first-programs/code/01SimpleWindow.vala:
--------------------------------------------------------------------------------
1 |
2 | int main(string[] args) {
3 |
4 | Gtk.init (ref args);
5 |
6 | Gtk.Window window = new Window();
7 | window.show_all();
8 |
9 | Gtk.main ();
10 |
11 | return 0;
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/content/chapter-03-first-programs/code/02HelloWorld.vala:
--------------------------------------------------------------------------------
1 |
2 |
3 | /* We define a HelloWorld class as a subclass Gtk.Window. */
4 | class HelloWorld : Gtk.Window {
5 |
6 | private Gtk.Button button;
7 |
8 | /* This is a callback function. The data arguments are ignored
9 | in this example. More on callbacks below. */
10 | public void hello () {
11 | stdout.printf("Hello World\n");
12 | }
13 |
14 | public bool on_delete_event () {
15 | /* If you return FALSE in the "delete_event" signal handler,
16 | GTK will emit the "destroy" signal. Returning TRUE means
17 | you don’t want the window to be destroyed.
18 | This is useful for popping up ’are you sure you want to quit?’
19 | type dialogs. */
20 | stdout.printf("delete event occurred\n");
21 | /* Change true to false and the main window will be destroyed with
22 | a "delete_event". */
23 | return true;
24 | }
25 |
26 | /* Another callback. */
27 | public void on_destroy() {
28 | Gtk.main_quit();
29 | }
30 |
31 | public HelloWorld () {
32 |
33 | /* When the window is given the "delete_event" signal (this is given
34 | by the window manager, usually by the "close" option, or on the
35 | titlebar), we ask it to call the on_delete_event() function as
36 | defined above. The data passed to the callback function is NULL
37 | and is ignored in the callback function. */
38 | this.delete_event.connect(this.on_delete_event);
39 |
40 | /* Here we connect the "destroy" event to a signal handler.
41 | This event occurs when we call gtk_widget_destroy() on the window,
42 | or if we return FALSE in the "on_delete_event" callback. */
43 | this.destroy.connect(this.on_destroy);
44 |
45 | /* Sets the border width of the window. */
46 | this.set_border_width(10);
47 |
48 | /* Creates a new button with the label "Hello World". */
49 | this.button = new Gtk.Button.with_label("Hello World");
50 |
51 | /* When the button receives the "clicked" signal, it will call the
52 | function hello() passing it None as its argument. The hello()
53 | function is defined above. */
54 | this.button.clicked.connect(this.hello);
55 |
56 | /* This will cause the window to be destroyed by calling
57 | this.on_destroy() when "clicked". Again, the destroy
58 | signal could come from here, or the window manager. */
59 | GLib.Signal.connect_swapped(this.button, "clicked", (GLib.Callback) this.on_destroy, this);
60 |
61 | /* This packs the button into the window (a GTK container). */
62 | this.add(this.button);
63 |
64 | }
65 |
66 | public static int main(string[] args){
67 | Gtk.init(ref args);
68 |
69 | var hello = new HelloWorld();
70 |
71 | /* Show all the window and all the widgets contained therein. */
72 | hello.show_all();
73 | /* All Vala GTK applications must have a Gtk.main(). Control ends here
74 | and waits for an event (like a key press or mouse event) to occur. */
75 | Gtk.main();
76 |
77 | return 0;
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/content/chapter-03-first-programs/screenshots/01SimpleWindow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-03-first-programs/screenshots/01SimpleWindow.png
--------------------------------------------------------------------------------
/content/chapter-03-first-programs/screenshots/02HelloWorld.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-03-first-programs/screenshots/02HelloWorld.png
--------------------------------------------------------------------------------
/content/chapter-04-moving-on/04-moving-on.md:
--------------------------------------------------------------------------------
1 | # Moving On
2 |
3 | ## Vala Data Types
4 |
5 | Vala supports three kinds of data types: value types, reference types,
6 | and meta types. Value types include simple types (e.g. `char`, `int`, and
7 | `float`), enum types, and struct types. Reference types include object types,
8 | array types, delegate types, and error types. Meta types are created from other
9 | types, and so may have either reference or value type semantics. They include
10 | parametrized types, nullable types, and pointer types
11 |
12 | ### Value Types
13 |
14 | Value types differ from reference types in that instances of value types are
15 | stored directly in variables or fields that represent them. Whenever a value
16 | type instance is assigned to another variable or field, the default action is to
17 | duplicate the value, such that each identifier refers to a unique copy of the
18 | data, over which it has ownership. When a value type is instantiated in a
19 | method, the instance is created on the stack.
20 |
21 | Value types include the boolean type, integral types, the floating-point types,
22 | and enumerated types.
23 |
24 | The boolean type, `bool`, can have values of `true` or `false`.
25 |
26 | Integral types can contain only integers. They are either signed or unsigned,
27 | each of which is considered a different type, though it is possible to cast
28 | between them when needed. Some types define exactly how many bits of storage are
29 | used to represent the integer e.g. `uint8`, `int64`, etc. Others depend on the
30 | environment, for example `long`, `int`, `short` map to C data types and therefore
31 | depend on the machine architecture. A `char` is 1 byte wide and can represent
32 | one of 256 values. A `unichar` is 4 bytes wide, i.e. large enough to store any
33 | [UTF-8](http://en.wikipedia.org/wiki/UTF-8) character.
34 |
35 | Floating point types are used to represent contain irrational floating point
36 | numbers in a fixed number of bits. There are two floating point types: `float`
37 | and `double`.
38 |
39 | An enumerated type is one in which all possible values that instances of the
40 | type can hold are declared with the type.
41 |
42 | ### Reference Types
43 |
44 | Variables of reference types contain references to the instances, rather than
45 | the instances themselves. Assinging an instance of a reference type to a
46 | variable or field will not make a copy of the data, instead only the reference
47 | to the data is copied. This means that both variables will refer to the same
48 | data, and so changes made to that data using one of the references will be
49 | visible when using the other. Instances of reference types are always stored on
50 | the heap (the part of memory that is dynamically allocated during a program's
51 | run time).
52 |
53 | When a variable that is an instance of a reference variable goes out of scope,
54 | the fact that a reference to the instance has been removed is also recorded.
55 | This means that a reference variable can be automatically removed from memory
56 | when it is no longer needed.
57 |
58 | Reference types include classes, arrays, delegates, errors and strings.
59 |
60 | A **class** definition introduces a new reference type - this is the most common
61 | way of creating a new type in Vala. A class is definition of a new data type. A
62 | class can contain fields, constants, methods, properties, and signals. Class
63 | types support *inheritance*, a mechanism whereby a derived class can extend and
64 | specialize a base class. Vala supports three different types of classes, namely:
65 |
66 | * GObject subclasses, which inherit directly from `GLib.Object`, and are the
67 | most powerful type of class.
68 |
69 | * Fundamental GType classes are those either without any superclass or that
70 | don't inherit at any level from `GLib.Object`. These classes support
71 | inheritence, interfaces, virtual methods, reference counting, unmanaged
72 | properties, and private fields. They are instantiated faster than GObject
73 | subclasses but are less powerful.
74 |
75 | * Compact classes, so called because they use less memory per instance, are the
76 | least featured of all class types. They are not registered with the GType
77 | system and do not support reference counting, virtual methods, or private
78 | fields. Such classes are very fast to instantiate but not massively useful
79 | except when dealing with existing libraries. They are declared using the
80 | `Compact` attribute on the class.
81 |
82 |
83 | An **array** is a data structure that can contains zero or more elements of the
84 | same type, up to a limit defined by the type.
85 |
86 | A **delegate** is a data structure that refers to a method. A method executes in
87 | a given scope which is also stored, meaning that for instance methods a delegate
88 | will contain also a reference to the instance.
89 |
90 | Instances of **error** types represent recoverable runtime errors. All errors
91 | are described using error domains, a type of enumerated value, but errors
92 | themselves are not enumerated types.
93 |
94 | Vala has built in support for Unicode **strings**, via the fundamental `string`
95 | type. This is the only fundamental type that is a reference type. Like other
96 | fundamental types, it can be instantiated with a literal expression. Strings are
97 | UTF-8 encoded which means that they cannot be accessed like character arrays in
98 | C since it is not guaranteed that each Unicode character will be stored in just
99 | one byte. Instead, the string fundamental struct type (which all strings are
100 | instances of) provides access methods along with other tools.
101 |
102 |
103 | ### Meta Types
104 |
105 | * **Parameterized Types**
106 |
107 | Vala allows definitions of types that can be customised at runtime with type
108 | parameters. For example, a list can be defined so that it can be instantiated
109 | as a list of ints, a list of Objects, etc. This is achieved using generic
110 | declarations.
111 |
112 | * **Pointer types**
113 |
114 | The name of a type can be used to implicitly create a pointer type related to
115 | that type. The value of a variable declared as being of type `T*` represents the
116 | memory address of an instance of type `T`. The instance is never made aware that
117 | its address has been recorded, and so cannot record the fact that it is referred
118 | to in this way.
119 |
120 | Instances of any type can be assigned to a variable that is declared to be a
121 | pointer to an instance of that type. For referenced types, direct assignment is
122 | allowed in either direction. For value types the pointer-to operator (`&`)
123 | is required to assign to a pointer, and the pointer-indirection operator (`*`)
124 | is used to access the instance pointed to.
125 |
126 | The `void*` type represents a pointer to an unknown type. As the referred type
127 | is unknown, the indirection operator cannot be applied to a pointer of type
128 | `void*`, nor can any arithmetic be performed on such a pointer. However, a
129 | pointer of type `void*` can be cast to any other pointer type (and vice-versa)
130 | and compared to values of other pointer types.
131 |
132 | * **Nullable Types**
133 |
134 | There is another characterization of types, *nullable types*. The name of a type
135 | can be used to implicitly create a nullable type related to that type. An
136 | instance of a nullable type `T?` can either be a value of type `T` or `null`.
137 | A nullable type will have either value or reference type semantics, depending on
138 | the type it is based on.
139 |
140 |
141 |
142 | ## An Upgraded Hello World
143 |
144 | Let us now take a look at a slightly improved `helloworld` with better examples
145 | of callbacks. This will also introduce us to our next topic, packing widgets.
146 |
147 | class HelloWorld : Gtk.Window {
148 |
149 | private Gtk.Button button1;
150 | private Gtk.Button button2;
151 | private Gtk.Box box;
152 |
153 | /* Our new improved callback. The data passed to this function
154 | * is printed to stdout. */
155 | void callback(string data) {
156 | stdout.printf("Hello! - %s was pressed\n", data);
157 | }
158 |
159 | /* another callback */
160 | bool on_delete_event() {
161 | Gtk.main_quit();
162 | return false;
163 | }
164 |
165 | public HelloWorld () {
166 |
167 |
168 | /* This is a new call, which just sets the title of our
169 | * new window to "Hello Buttons!" */
170 | this.set_title("Hello Buttons!");
171 |
172 | /* Here we just set a handler for delete_event that immediately
173 | * exits GTK. */
174 | this.delete_event.connect(this.on_delete_event);
175 |
176 | /* Sets the border width of the window. */
177 | this.set_border_width(10);
178 |
179 | /* We create a box to pack widgets into. This is described
180 | * in detail in the "packing" section. The box is not really
181 | * visible, it is just used as a tool to arrange widgets. */
182 | box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
183 |
184 | /* Put the box into the main window. */
185 | this.add(box);
186 |
187 | /* Creates a new button with the label "Button 1". */
188 | this.button1 = new Gtk.Button.with_label("Button 1");
189 |
190 | /* Now when the button is clicked, we call the "callback" function
191 | * with a pointer to "button 1" as its argument */
192 | this.button1.clicked.connect (() => { this.callback("Button 1"); });
193 |
194 | /* Instead of gtk_container_add, we pack this button into the
195 | * invisible box, which has been packed into the window. */
196 | box.pack_start(button1, true, true, 0);
197 |
198 | /* Always remember this step, this tells GTK that our preparation
199 | * for this button is complete, and it can now be displayed. */
200 | button1.show();
201 |
202 | /* Do these same steps again to create a second button */
203 | this.button2 = new Gtk.Button.with_label("Button 2");
204 |
205 | /* Call the same callback function with a different argument,
206 | passing a pointer to "button 2" instead. */
207 | this.button2.clicked.connect (() => { this.callback("Button 2"); });
208 |
209 | box.pack_start(button2, true, true, 0);
210 |
211 | /* The order in which we show the buttons is not really important,
212 | * but we recommend showing the window last, so it all pops up at
213 | * once. */
214 | button2.show();
215 |
216 | box.show();
217 |
218 | }
219 |
220 | public static int main (string[] args) {
221 | /* This is called in all GTK applications. Arguments are parsed
222 | * from the command line and are returned to the application. */
223 | Gtk.init (ref args);
224 |
225 | var hello = new HelloWorld();
226 |
227 | hello.show();
228 |
229 | /* Rest in gtk_main and wait for the fun to begin! */
230 | Gtk.main();
231 |
232 | return 0;
233 | }
234 | }
235 |
236 |
237 | Compiling and running the code produces the window below, *"Upgraded Hello World
238 | Example"*.
239 |
240 |
242 | void callback(string data) {
259 | stdout.printf("Hello! - %s was pressed\n", data);
260 | }
261 |
262 |
263 | define a callback method which is similar to the `hello()` callback
264 | in the first helloworld. The difference is that the callback prints a message
265 | including data passed in.
266 |
267 | The line
268 |
269 | this.set_title("Hello Buttons!");
270 |
271 | sets a title string to be used on the titlebar of the window, as seen in
272 | the screenshot above.
273 |
274 | The line
275 |
276 | box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
277 |
278 | creates a horizontal box (`Gtk.Box`) to hold the two buttons that are
279 | created in the lines
280 |
281 | this.button1 = new Gtk.Button.with_label("Button 1");
282 | this.button2 = new Gtk.Button.with_label("Button 2");
283 |
284 |
285 | The line
286 |
287 | this.window.add(box);
288 |
289 | adds the horizontal box to the window container.
290 |
291 | The lines
292 |
293 | this.button1.clicked.connect (() => { this.callback("Button 1"); });
294 | this.button2.clicked.connect (() => { this.callback("Button 2"); });
295 |
296 |
297 | connect the `callback()` method to the "clicked" signal of the buttons. Each
298 | button sets up a different string to be passed to the `callback()` method when
299 | invoked.
300 |
301 | The lines
302 |
303 | box.pack_start(button1, true, true, 0);
304 | box.pack_start(button2, true, true, 0);
305 |
306 |
307 | pack the buttons into the horizontal box. The lines
308 |
309 | button1.show();
310 | button2.show();
311 |
312 |
313 | ask GTK to display the buttons.
314 |
315 | The lines
316 |
317 | box.show();
318 |
319 | ask GTK to display the box and the window respectively.
320 |
321 | The window is shown by the line
322 |
323 | hello.show();
324 |
325 | in `main()`.
326 |
327 |
328 | ## References and Further Reading
329 |
330 | * The GTK+ Tutorial: Getting Started. [Online] Available from:
331 | [https://developer.gnome.org/gtk-tutorial/2.90/c39.html](https://developer.gnome.org/gtk-tutorial/2.90/c39.html)
332 | [Accessed 16 September 2014]
333 |
334 | * The GTK 3 Reference Manual. [Online] Available from:
335 | [https://developer.gnome.org/gtk3/stable/](https://developer.gnome.org/gtk3/stable/)
336 | [Accessed 9 November 2014]
337 |
338 | * The Vala Manual (draft) [Online] Available from:
339 | [http://www.vala-project.org/doc/vala-draft/types.html](http://www.vala-project.org/doc/vala-draft/types.html)
340 | [Accessed 9 November 2014]
341 |
342 | * Vala Documentation: Signals and Callbacks. [Online] Available from:
343 | [https://wiki.gnome.org/Projects/Vala/SignalsAndCallbacks](https://wiki.gnome.org/Projects/Vala/SignalsAndCallbacks)
344 | [Accessed 16 September 2014]
345 |
346 | * Valadoc (Vala online package binding reference documentation) [Online] Available from:
347 | [http://valadoc.org/#!api=gobject-2.0/GLib.SignalHandler](http://valadoc.org/#!api=gobject-2.0/GLib.SignalHandler)
348 | [Accessed 16 September 2014]
349 |
350 |
--------------------------------------------------------------------------------
/content/chapter-04-moving-on/code/01_helloworld_2.vala:
--------------------------------------------------------------------------------
1 |
2 | class HelloWorld : Gtk.Window {
3 |
4 | private Gtk.Button button1;
5 | private Gtk.Button button2;
6 | private Gtk.Box box;
7 |
8 | /* Our new improved callback. The data passed to this function
9 | * is printed to stdout. */
10 | void callback(string data) {
11 | stdout.printf("Hello! - %s was pressed\n", data);
12 | }
13 |
14 | /* another callback */
15 | bool on_delete_event() {
16 | Gtk.main_quit();
17 | return false;
18 | }
19 |
20 | public HelloWorld () {
21 |
22 |
23 | /* This is a new call, which just sets the title of our
24 | * new window to "Hello Buttons!" */
25 | this.set_title("Hello Buttons!");
26 |
27 | /* Here we just set a handler for delete_event that immediately
28 | * exits GTK. */
29 | this.delete_event.connect(this.on_delete_event);
30 |
31 | /* Sets the border width of the window. */
32 | this.set_border_width(10);
33 |
34 | /* We create a box to pack widgets into. This is described
35 | * in detail in the "packing" section. The box is not really
36 | * visible, it is just used as a tool to arrange widgets. */
37 | box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
38 |
39 | /* Put the box into the main window. */
40 | this.add(box);
41 |
42 | /* Creates a new button with the label "Button 1". */
43 | this.button1 = new Gtk.Button.with_label("Button 1");
44 |
45 | /* Now when the button is clicked, we call the "callback" function
46 | * with a pointer to "button 1" as its argument */
47 | this.button1.clicked.connect (() => { this.callback("Button 1"); });
48 |
49 | /* Instead of gtk_container_add, we pack this button into the
50 | * invisible box, which has been packed into the window. */
51 | box.pack_start(button1, true, true, 0);
52 |
53 | /* Always remember this step, this tells GTK that our preparation
54 | * for this button is complete, and it can now be displayed. */
55 | button1.show();
56 |
57 | /* Do these same steps again to create a second button */
58 | this.button2 = new Gtk.Button.with_label("Button 2");
59 |
60 | /* Call the same callback function with a different argument,
61 | passing a pointer to "button 2" instead. */
62 | this.button2.clicked.connect (() => { this.callback("Button 2"); });
63 |
64 | box.pack_start(button2, true, true, 0);
65 |
66 | /* The order in which we show the buttons is not really important,
67 | * but we recommend showing the window last, so it all pops up at
68 | * once. */
69 | button2.show();
70 |
71 | box.show();
72 |
73 | }
74 |
75 | public static int main (string[] args) {
76 | /* This is called in all GTK applications. Arguments are parsed
77 | * from the command line and are returned to the application. */
78 | Gtk.init (ref args);
79 |
80 | var hello = new HelloWorld();
81 |
82 | hello.show();
83 |
84 | /* Rest in gtk_main and wait for the fun to begin! */
85 | Gtk.main();
86 |
87 | return 0;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/content/chapter-04-moving-on/screenshots/01HelloWorld2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-04-moving-on/screenshots/01HelloWorld2.png
--------------------------------------------------------------------------------
/content/chapter-05-layout-widgets/05-layout-widgets.md:
--------------------------------------------------------------------------------
1 |
2 | # Layout Widgets
3 |
4 | When creating an application, you'll probably want to put more than one widget
5 | inside a window. Our first helloworld example only used one widget so we could
6 | simply use the `Gtk.Container.add()` method to "pack" the widget into the window.
7 | But when you want to put more than one widget into a window, you will need to
8 | control the widgets' sizes and where they are positioned in the window. This is
9 | where *packing* comes in.
10 |
11 | GTK+ comes with a variety of *layout containers* whose purpose it is to control
12 | the layout of child widgets added to them, as well as how they behave when the
13 | window is resized. An overview may be seen in the
14 | [Layout Containers Overview](https://developer.gnome.org/gtk3/stable/LayoutContainers.html)
15 | in the online GTK+ documentation.
16 |
17 | In this chapter, we shall look at packing using Boxes and Grids in detail.
18 |
19 | ## Packing Using Boxes
20 |
21 | You can do widget packing using boxes. These are invisible widget containers
22 | that we can pack our widgets into (we will refer the widgets we pack into the
23 | box as *children*). These boxes are instances of the `Gtk.Box` widget. The
24 | `Gtk.Box` widget organizes its child widgets into a rectangular area.
25 |
26 | The rectangular area of a `Gtk.Box` is organized into either a single row or a
27 | single column of child widgets depending upon the orientation selected. In a
28 | horizontal `Gtk.Box` all children are allocated the same height, and in a
29 | vertical `Gtk.Box` all the children of the box have the same width.
30 |
31 | `Gtk.Box` uses a notion of *packing*. Packing refers to adding widgets with
32 | reference to a particular position in a `Gtk.Container` (`Gtk.Box` is a subclass
33 | of `Gtk.Container`). For a `Gtk.Box`, there are two reference positions: the
34 | start and the end of the box. For a vertical `Gtk.Box`, the start is defined as
35 | the top of the box and the end is defined as the bottom. For a horizontal
36 | `Gtk.Box` the start is defined as the left side and the end is defined as the
37 | right side.
38 |
39 | When packing widgets horizontally, the objects are inserted from left to right
40 | or right to left depending on the packing method used. If they are inserted
41 | vertically, child widgets are packed from top to bottom or vice versa. You may
42 | use any combination of boxes inside or beside other boxes to create a desired
43 | effect.
44 |
45 | The constructor for `Gtk.Box` is
46 |
47 | Gtk.Box(Gtk.Orientation orientation, int spacing);
48 |
49 | `orientation` is an instance of the enumerated type `Gtk.Orientation`, which
50 | takes one of two values: `Gtk.Orientation.HORIZONTAL` and
51 | `Gtk.Orientation.VERTICAL`. If `HORIZONTAL` is passed to the constructor, the
52 | children of the box are arranged in a single row, and if `VERTICAL` is passed,
53 | the children will be arranged in a single column. `spacing` is the number of
54 | pixels to place by default between children.
55 |
56 | The instance methods `pack_start()` and `pack_end()` are used to place child
57 | objects inside the `Gtk.Box` containers.
58 |
59 | /* When we call box.pack_end(...) we add child to
60 | box, packed with reference to the end of box. */
61 | public void pack_end (Gtk.Widget child, bool expand = true, bool fill = true, uint padding = 0);
62 |
63 | /* When we call box.pack_start(...) it adds child to box,
64 | packed with reference to the beginning of box. */
65 | public void pack_start (Gtk.Widget child, bool expand = true, bool fill = true, uint padding = 0);
66 |
67 |
68 | The `pack_start()` method will start at the top and work its way down in a
69 | vertical box, and pack left to right in a horizontal box. The `pack_end()`
70 | method will do the opposite, packing from bottom to top in a vertical box, and
71 | right to left in a horizontal box. Using these methods allows us to right
72 | justify or left justify our widgets and may be mixed in any way to achieve the
73 | desired effect. We will use `pack_start()` in most of our examples. The child
74 | object may be another container or a widget. In fact, many widgets are actually
75 | containers themselves, including the button (though we usually only use a label
76 | inside a button).
77 |
78 | You may use the `set_homogeneous(bool homogeneous)` instance method to specify
79 | whether or not all children of the `Gtk.Box` are forced to get the same amount
80 | of space (if `homogeneous` is set to `true`, child widgets will be allocated
81 | equal width in a horizontal box or equal height in a vertical box).
82 |
83 | You can also use `box.set_spacing()` to determine how much space will be
84 | minimally placed between all children in the `Gtk.Box`. Note that spacing is
85 | added *between* the children, while the padding added by `pack_start` or
86 | `pack_end` is added *on either side* of the widget it belongs to.
87 |
88 | By using these calls, GTK+ knows where you want to place your widgets so it can
89 | do automatic resizing and other nifty things. There are also a number of options
90 | as to how your widgets should be packed. This method gives us a quite a bit of
91 | flexibility when placing and creating widgets.
92 |
93 |
94 | ## Details of Boxes
95 |
96 | Because of this flexibility, packing boxes in GTK can be confusing at first.
97 | There are a lot of options, and it's not immediately obvious how they all fit
98 | together. In the end, however, there are basically five different styles.
99 |
100 | Below, we see how we may use some of the box packing methods to achieve certain
101 | effects. It's obtained by compiling
102 | `chapter-05-packing-widgets/code/01Packbox.vala` and running the executable with
103 | the command-line argument `1`.
104 |
105 |
107 | box.pack_start(child);
136 |
137 | and `child` will be added to `box` with `expand = true`, `fill = true`, and
138 | `padding = 0`.
139 |
140 | What's the difference between spacing (set when the box is created) and padding
141 | (set when elements are packed)? Spacing is added between objects, and padding is
142 | added on either side of an object.
143 |
144 | The figure below, “Packing with Spacing and Padding” illustrates the difference.
145 | You view see it by compiling `chapter-05-packing-widgets/code/01Packbox.vala`
146 | and running the executable with the command-line argument `2`.
147 |
148 |
150 |
160 | pack_end()/*
168 | * Helper function that makes a new hbox filled with button-labels.
169 | * Arguments for the variables we're interested are passed in to
170 | * this function. We do not show the box, but do show everything
171 | * inside.
172 | */
173 | static Gtk.Box make_box (bool homogeneous, int spacing, bool expand, bool fill, int padding) {
174 |
175 | Gtk.Box box;
176 | Gtk.Button button;
177 | string padstr;
178 |
179 | /* Create a new Gtk.Box with the appropriate orientation
180 | * and spacing settings */
181 | box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, spacing);
182 | /* Set whether all child widgets be the same size. */
183 | box.set_homogeneous(homogeneous);
184 |
185 | /* Create a series of buttons with the appropriate settings */
186 | button = new Gtk.Button.with_label("box.pack");
187 | box.pack_start(button, expand, fill, padding);
188 | button.show();
189 |
190 | /*button = new Gtk.Button.with_label ("(box,");
191 | box.pack_start(button, expand, fill, padding);
192 | button.show();*/
193 |
194 | button = new Gtk.Button.with_label("(button,");
195 | box.pack_start(button, expand, fill, padding);
196 | button.show();
197 |
198 | /* Create a button with the label depending on the value of
199 | * expand. */
200 | button = new Gtk.Button.with_label(@"$expand,");
201 | /*if (expand == true)
202 | button = new Gtk.Button.with_label("true,");
203 | else
204 | button = new Gtk.Button.with_label("false,");*/
205 |
206 | box.pack_start(button, expand, fill, padding);
207 | button.show();
208 |
209 | /* This is the same as the button creation for "expand"
210 | * above, but uses the shorthand form. */
211 | button = new Gtk.Button.with_label(@"$fill,");
212 | box.pack_start (button, expand, fill, padding);
213 | button.show();
214 |
215 | padstr = @"$padding);";
216 |
217 | button = new Gtk.Button.with_label(padstr);
218 | box.pack_start(button, expand, fill, padding);
219 | button.show();
220 |
221 | return box;
222 | }
223 |
224 | class PackBox : Gtk.Window {
225 |
226 | public bool on_delete_event () {
227 | Gtk.main_quit();
228 | return false;
229 | }
230 |
231 | public PackBox (int which) {
232 |
233 | /* You should always remember to connect the delete_event signal
234 | * to the main window. This is very important for proper intuitive
235 | * behavior */
236 |
237 | this.delete_event.connect(this.on_delete_event);
238 | this.set_border_width(10);
239 |
240 | /* We create a vertical box (vbox) to pack the horizontal boxes
241 | * into. This allows us to stack the horizontal boxes filled with
242 | * buttons one on top of the other in this vbox. */
243 | var box1 = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
244 |
245 | /* Which example to show. These correspond to the pictures above. */
246 | switch (which) {
247 | case 1:
248 | /* create two new labels. */
249 | var label = new Gtk.Label("Gtk.Box(HORIZONTAL, 0)");
250 | var label2 = new Gtk.Label("box.set_homogeneous(false)");
251 |
252 | /* Align the labels to the left side. We'll discuss this method
253 | * and others in the section on Widget Attributes. */
254 | label.set_alignment(0, 0);
255 | label2.set_alignment(0, 0);
256 |
257 | /* Pack the labels into the vertical box (box box1). Remember
258 | * that widgets added to a vertically oriented box will be
259 | * packed one on top of the other in order. */
260 | box1.pack_start(label, false, false, 0);
261 | box1.pack_start(label2, false, false, 0);
262 |
263 | /* Show the labels. */
264 | label.show();
265 | label2.show();
266 |
267 | /* Call our make box function - homogeneous = false, spacing = 0,
268 | * expand = false, fill = false, padding = 0 */
269 | var box2 = make_box(false, 0, false, false, 0);
270 | box1.pack_start(box2, false, false, 0);
271 | box2.show();
272 |
273 | /* Call our make box function - homogeneous = false, spacing = 0,
274 | * expand = true, fill = false, padding = 0 */
275 | box2 = make_box(false, 0, true, false, 0);
276 | box1.pack_start(box2, false, false, 0);
277 | box2.show();
278 |
279 | /* Args are: homogeneous, spacing, expand, fill, padding */
280 | box2 = make_box(false, 0, true, true, 0);
281 | box1.pack_start(box2, false, false, 0);
282 | box2.show();
283 |
284 | /* Creates a separator, we'll learn more about these later,
285 | * but they are quite simple. */
286 | var separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL);
287 |
288 | /* Pack the separator into the vbox. Remember each of these
289 | * widgets is being packed into a vertically oriented box, so
290 | * they'll be stacked vertically. */
291 | box1.pack_start(separator, false, true, 5);
292 | separator.show();
293 |
294 | /* Create another new label, and show it. */
295 | label = new Gtk.Label("Gtk.Box(Gtk.Orientation.HORIZONTAL, 0)");
296 |
297 | label.set_alignment(0, 0);
298 | box1.pack_start(label, false, false, 0);
299 | label.show();
300 |
301 | label2 = new Gtk.Label("box.set_homogeneous(true)");
302 | label2.set_alignment(0, 0);
303 | box1.pack_start(label2, false, false, 0);
304 | label2.show();
305 |
306 |
307 | /* Args are: homogeneous, spacing, expand, fill, padding */
308 | box2 = make_box(true, 0, true, false, 0);
309 | box1.pack_start(box2, false, false, 0);
310 | box2.show();
311 |
312 | /* Args are: homogeneous, spacing, expand, fill, padding */
313 | box2 = make_box(true, 0, true, true, 0);
314 | box1.pack_start(box2, false, false, 0);
315 | box2.show();
316 |
317 | /* Another new separator. */
318 | separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL);
319 | /* The last 3 arguments to pack_start are:
320 | * expand, fill, padding. */
321 | box1.pack_start(separator, false, true, 5);
322 | separator.show();
323 | break;
324 | case 2:
325 | /* Create a new label, remember box1 is a vbox as created
326 | * near the beginning of the constructor. */
327 | var label = new Gtk.Label("Gtk.Box(HORIZONTAL, 10)");
328 | label.set_alignment( 0, 0);
329 | box1.pack_start(label, false, false, 0);
330 | label.show();
331 |
332 | var label2 = new Gtk.Label("box.set_homogeneous(false)");
333 | label2.set_alignment( 0, 0);
334 | box1.pack_start(label2, false, false, 0);
335 | label2.show();
336 |
337 | /* Args are: homogeneous, spacing, expand, fill, padding. */
338 | var box2 = make_box(false, 10, true, false, 0);
339 | box1.pack_start(box2, false, false, 0);
340 | box2.show();
341 |
342 | /* Args are: homogeneous, spacing, expand, fill, padding */
343 | box2 = make_box(false, 10, true, true, 0);
344 | box1.pack_start(box2, false, false, 0);
345 | box2.show();
346 |
347 | var separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL);
348 | /* The last 3 arguments to pack_start are:
349 | * expand, fill, padding. */
350 | box1.pack_start(separator, false, true, 5);
351 | separator.show();
352 |
353 | label = new Gtk.Label("Gtk.Box(HORIZONTAL, 0)");
354 | label.set_alignment(0, 0);
355 | box1.pack_start(label, false, false, 0);
356 | label.show();
357 |
358 | label2 = new Gtk.Label("box.set_homogeneous(false)");
359 | label2.set_alignment( 0, 0);
360 | box1.pack_start(label2, false, false, 0);
361 | label2.show();
362 |
363 | /* Args are: homogeneous, spacing, expand, fill, padding. */
364 | box2 = make_box(false, 0, true, false, 10);
365 | box1.pack_start(box2, false, false, 0);
366 | box2.show();
367 |
368 | /* Args are: homogeneous, spacing, expand, fill, padding. */
369 | box2 = make_box(false, 0, true, true, 10);
370 | box1.pack_start(box2, false, false, 0);
371 | box2.show();
372 |
373 | separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL);
374 | /* The last 3 arguments to pack_start are:
375 | * expand, fill, padding. */
376 | box1.pack_start(separator, false, true, 5);
377 | separator.show();
378 | break;
379 | case 3:
380 | /* This demonstrates the ability to use pack_end() to
381 | * right justify widgets. First, we create a new box as before. */
382 | var box2 = make_box(false, 0, false, false, 0);
383 |
384 | /* Create the label that will be put at the end. */
385 | var label = new Gtk.Label("end");
386 | /* Pack it using pack_end(), so it is put on the right
387 | * side of the hbox created in the make_box() call. */
388 | box2.pack_end(label, false, false, 0);
389 | /* Show the label. */
390 | label.show();
391 |
392 | /* Pack box2 into box1 */
393 | box1.pack_start(box2, false, false, 0);
394 | box2.show();
395 |
396 | /* A separator for the bottom. */
397 | var separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL);
398 |
399 | /* This explicitly sets the separator to 400 pixels wide by 5
400 | * pixels high. This is so the hbox we created will also be 400
401 | * pixels wide, and the "end" label will be separated from the
402 | * other labels in the hbox. Otherwise, all the widgets in the
403 | * hbox would be packed as close together as possible.
404 | * separator.set_size_request(400, 5)
405 | * pack the separator into the vbox (box1) created near the
406 | * start of the constructor. */
407 | box1.pack_start(separator, false, true, 5);
408 | separator.show();
409 | break;
410 | }
411 |
412 | /* Create another new hbox. Remember we can use as many as we need! */
413 | var quitbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
414 | quitbox.set_homogeneous(false);
415 |
416 | /* Our quit button. */
417 | var button = new Gtk.Button.with_label("Quit");
418 |
419 | /* Setup the signal to terminate the program when the button is
420 | * clicked */
421 | button.clicked.connect( () => { Gtk.main_quit(); } );
422 | /* Pack the button into the quitbox.
423 | * The last 3 arguments to pack_start are:
424 | * expand, fill, padding. */
425 | quitbox.pack_start(button, true, false, 0);
426 | /* pack the quitbox into the vbox (box1) */
427 | box1.pack_start(quitbox, false, false, 0);
428 |
429 | /* Pack the vbox (box1) which now contains all our widgets, into the
430 | * main window. */
431 | this.add(box1);
432 |
433 | /* And show everything left */
434 | button.show();
435 | quitbox.show();
436 | box1.show();
437 |
438 | /* Showing the window last so everything pops up at once. */
439 | this.show();
440 | }
441 |
442 | public static int main (string[] args) {
443 |
444 | if (args.length == 2) {
445 |
446 | Gtk.init(ref args);
447 |
448 | var window = new PackBox(int.parse(args[1]));
449 | window.show();
450 |
451 | /* And of course, our mainloop. */
452 | Gtk.main();
453 |
454 | /* Control returns here when Gtk.main_quit() is called. */
455 | return 0;
456 |
457 | } else {
458 | stderr.printf("usage: packbox num, where num is 1, 2, or 3.\n");
459 | /* This just does cleanup in GTK and exits with an exit status
460 | * of 1. */
461 | Process.exit (1);
462 | }
463 |
464 | }
465 | }
466 |
467 |
468 | A brief tour of the `01_packingboxes.vala` code starts with lines 10-60 which
469 | define a helper function `make_box()` that creates a horizontal box and
470 | populates it with buttons according to the specified parameters. A reference to
471 | the horizontal box is returned.
472 |
473 | Lines 71-283 define the `PackBox1` class initialization method `PackBox1()` that
474 | creates a window and a child vertical box that is populated with a different
475 | widget arrangement depending on the argument passed to it. If a `1` is passed,
476 | lines 90-166 create a window displaying the five unique packing arrangements
477 | that are available when varying the homogeneous, expand and fill parameters.
478 | If a `2` is passed, lines 167-221 create a window displaying the various
479 | combinations of fill with spacing and padding. Finally, if a `3` is passed,
480 | lines 222-252 create a window displaying the use of the `pack_start()` method to
481 | left justify the buttons and `pack_end()` method to right justify a label. Lines
482 | 255-270 create a horizontal box containing a button that is packed into the
483 | vertical box. The button "clicked" signal is connected to the `Gtk.main_quit()`
484 | function to terminate the program.
485 |
486 | Lines 287-304 check the command line arguments and exit the program by returning
487 | `1` from the `main()` function if there isn't exactly one argument. Line 291
488 | creates a `PackBox1` instance. Line 294 invokes the `Gtk.main()` function to
489 | start the GTK event processing loop.
490 |
491 | In this example program, the references to the various widgets (except the
492 | window) are not saved in the object instance attributes because they are not
493 | needed later.
494 |
495 | To learn more about using the `Gtk.Box` widget, read the C API documentation at
496 | [https://developer.gnome.org/gtk3/stable/GtkBox.html](https://developer.gnome.org/gtk3/stable/GtkBox.html),
497 | and the Vala API documentation at [http://valadoc.org/#!api=gtk+-3.0/Gtk.Box](http://valadoc.org/#!api=gtk+-3.0/Gtk.Box).
498 |
499 | ## Packing Using Grids
500 |
501 | Let's look at another way of packing widgets, the `Gtk.Grid`. `Gtk.Grid` is a
502 | container which arranges its child widgets in rows and columns.
503 |
504 | The first thing to look at, of course, is the `Gtk.Grid()` constructor:
505 |
506 | var grid = Gtk.Grid();
507 |
508 | which creates a new instance of `Gtk.Grid` and returns a reference to it.
509 |
510 | A `Gtk.Grid` has a boolean "`column_homogeneous`" property, if its value is
511 | `true` the grid's columns will all the same width (the width of the widest
512 | child widget in the grid). Its default value is `false`, where the width of a
513 | grid's column is dictated by the widest widget in the column. An analogous
514 | `row_homogeneous` property does the same for the height of rows.
515 |
516 | Note that the coordinate system starts from 0 in the upper left hand corner.
517 |
518 | Children are added using `Gtk.Grid.attach()`. They can span as many rows or
519 | columns as we specify.
520 |
521 | To place a widget into a grid, we use the following method:
522 |
523 | public void attach(Gtk.Widget child, int left, int top, int width, int height);
524 |
525 | The first parameter (`child`) is the widget you wish to place in the table. The
526 | `left` and `top` arguments specify where to place the widget (counting from 0
527 | at the left and top respectively), and the `width` and `height` specify how many
528 | boxes to use (integers specifying how many columns and rows the widget will
529 | occupy respectively).
530 |
531 | If you want to add a button in the upper left corder of a grid, and want it to
532 | fill one row and one column, you would have `left = 0`, `top = 0`, `width = 1`,
533 | and `height = 1`.
534 |
535 | If you wanted a child widget in the upper left corner that will take up two
536 | rows, you'd use `left = 0`, `top = 0`, `width = 2`, and `height=1`.
537 |
538 | We can also use the `Gtk.Grid.attach_next_to` method to add new widgets next to
539 | sibling elements already in the grid.
540 |
541 | public void attach_next_to (Widget child, Widget? sibling, PositionType side, int width, int height)
542 |
543 | adds the widget `child` next to the widget `sibling`, on the side defined by
544 | `side`, which is an instance of `Gtk.PositionType`, which an enumeration that \
545 | takes one of the values: `Gtk.PositionType.LEFT` (adds `child` on the left edge
546 | of `sibling`), `Gtk.PositionType.RIGHT`, `Gtk.PositionType.TOP`, and
547 | `Gtk.PositionType.BOTTOM`.
548 |
549 | We can set spacing between the rows and columns of the grid (in pixels) using
550 | the following methods:
551 |
552 | grid.set_row_spacing (int spacing)
553 |
554 | grid.set_col_spacing (uint spacing)
555 |
556 |
557 | Note that for columns, the space goes to the right of the column, and for rows,
558 | the space goes below the row.
559 |
560 | ## Example
561 |
562 | In the example below, we will make a window with seven buttons in a grid, showing
563 | the various ways you can position child elements in a grid.
564 |
565 | class GridExample : Gtk.Window {
566 |
567 | /* Our callback.
568 | * The data passed to this method is printed to stdout */
569 | void callback(string data) {
570 | stdout.printf("Hello again - %s was pressed\n", data);
571 | }
572 |
573 | /* This callback quits the program. */
574 | public bool on_delete_event() {
575 | Gtk.main_quit();
576 | return false;
577 | }
578 |
579 | public GridExample () {
580 |
581 | /* Set the window title. */
582 | this.set_title("Grid Packing Example");
583 |
584 | /* Set a handler for delete_event that immediately
585 | *exits Gtk. */
586 | this.delete_event.connect(this.on_delete_event);
587 |
588 | /* Sets the border width of the window. */
589 | this.set_border_width(20);
590 |
591 | /* Create a 2x2 table. */
592 | var grid = new Gtk.Grid();
593 |
594 | /* Put the table in the main window. */
595 | this.add(grid);
596 |
597 | /* Create first button. */
598 | var button = new Gtk.Button.with_label("button 1");
599 | /* When the button is clicked, we call the "callback" method. */
600 | button.clicked.connect( ()=>{ this.callback("button 1"); });
601 | grid.attach (button, 0, 0, 1, 1);
602 | button.show();
603 |
604 | /* Create second button. */
605 | button = new Gtk.Button.with_label("button 2");
606 | /* When the button is clicked, we call the "callback" method, this
607 | * time with a different button name. */
608 | button.clicked.connect( () => { this.callback("button 2"); } );
609 | /* Insert button 2 into the second column of the first row. */
610 | grid.attach(button, 1, 0, 1, 1);
611 | button.show();
612 |
613 | /* Create Third button. */
614 | button = new Gtk.Button.with_label("button 3");
615 | button.clicked.connect( () => { this.callback("button 3"); } );
616 | /* Insert button 3 to the right of button 2. */
617 | grid.attach_next_to(button, grid.get_child_at(0, 1), Gtk.PositionType.RIGHT, 1, 1);
618 | button.show();
619 |
620 | /* Create Fourth button. */
621 | button = new Gtk.Button.with_label("button 4");
622 | button.clicked.connect( () => { this.callback("button 4"); } );
623 | /* Insert button 4 into the 2nd row of the grid (below button 1). */
624 | grid.attach_next_to(button, grid.get_child_at(0, 0), Gtk.PositionType.BOTTOM, 1, 2);
625 | button.show();
626 |
627 | button = new Gtk.Button.with_label("button 5");
628 | button.clicked.connect( () => { this.callback("button 5"); } );
629 | /* Insert button 5 into the second row of the grid, to occupy 2
630 | * columns. */
631 | grid.attach(button, 1, 1, 2, 1);
632 | button.show();
633 |
634 | button = new Gtk.Button.with_label("button 6");
635 | button.clicked.connect( () => { this.callback("button 6"); } );
636 | /* Insert button 6 into the third row of the grid. */
637 | grid.attach(button, 1, 2, 1, 1);
638 | button.show();
639 |
640 | button = new Gtk.Button.with_label("button 7");
641 | button.clicked.connect( () => { this.callback("button 7"); } );
642 | /* Insert button 7 into the third row of the grid. */
643 | grid.attach(button, 2, 2, 1, 1);
644 | button.show();
645 |
646 | /* Create "Quit" button */
647 | button = new Gtk.Button.with_label("Quit");
648 | /* When the button is clicked, we call the main_quit function
649 | * and the program exits. */
650 | button.clicked.connect( ()=> { Gtk.main_quit(); });
651 | /* Insert the quit button into the fourth row of the grid. */
652 | grid.attach(button, 0, 3, 3, 1);
653 | button.show();
654 |
655 | grid.show();
656 |
657 | }
658 |
659 | public static int main(string[] args) {
660 |
661 | Gtk.init(ref args);
662 |
663 | var gridexample = new GridExample();
664 |
665 | gridexample.show();
666 |
667 | Gtk.main();
668 |
669 | return 0;
670 | }
671 | }
672 |
673 |
674 | When compiled and run, it looks something like this:
675 |
676 |
678 |
697 | class StackExample : Gtk.Window {
703 |
704 | public StackExample () {
705 |
706 | this.set_title("Stack and StackSwitcher Demo");
707 | this.window_position = Gtk.WindowPosition.CENTER;
708 | this.set_default_size(350, 70);
709 | this.set_border_width(10);
710 | this.destroy.connect(Gtk.main_quit);
711 |
712 | var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 5);
713 | this.add(box);
714 |
715 | var stack = new Gtk.Stack();
716 | stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT);
717 | stack.set_transition_duration(1000);
718 |
719 | var label1 = new Gtk.Label("Page 1 Content.");
720 | stack.add_titled(label1, "page-1", "Page 1");
721 |
722 | var label2 = new Gtk.Label("Page 2 Content.");
723 | stack.add_titled(label2, "page-2", "Page 2");
724 |
725 | var label3 = new Gtk.Label("Page 3 Content.");
726 | stack.add_titled(label3, "page-3", "Page 3");
727 |
728 |
729 | var switcher = new Gtk.StackSwitcher();
730 | switcher.set_stack(stack);
731 | box.pack_start(switcher, true, true, 0);
732 | box.pack_start(stack, true, true, 0);
733 |
734 | }
735 |
736 | public static int main (string[] args) {
737 |
738 | Gtk.init(ref args);
739 |
740 | var win = new StackExample();
741 | win.show_all();
742 |
743 | Gtk.main();
744 |
745 | return 0;
746 | }
747 |
748 | }
749 |
750 |
751 |
752 | ## References and Further Reading
753 |
754 | * The GTK+ Tutorial: Packing Widgets. [Online] Available from:
755 | [https://developer.gnome.org/gtk-tutorial/2.90/c354.html](https://developer.gnome.org/gtk-tutorial/2.90/c354.html)
756 | [Accessed 10 November 2014]
757 |
758 | * The GtkGrid Section of the GTK 3 Reference Manual. [Online] Available from:
759 | [https://developer.gnome.org/gtk3/stable/GtkGrid.html](https://developer.gnome.org/gtk3/stable/GtkGrid.html)
760 | [Accessed 10 November 2014]
761 |
762 | * Documentation on Gtk.Box in Valadoc [Online] Available from:
763 | [http://valadoc.org/#!api=gtk+-3.0/Gtk.Box](http://valadoc.org/#!api=gtk+-3.0/Gtk.Box)
764 | [Accessed 16 September 2014]
765 |
766 | * Documentation on Gtk.Grid in Valadoc [Online] Available from:
767 | [http://valadoc.org/#!api=gtk+-3.0/Gtk.Grid](http://valadoc.org/#!api=gtk+-3.0/Gtk.Grid)
768 | [Accessed 16 September 2014]
769 |
770 | * The Python GTK+3 Tutorial [Online] Available from:
771 | [http://python-gtk-3-tutorial.readthedocs.org/en/latest/layout.html](http://python-gtk-3-tutorial.readthedocs.org/en/latest/layout.html)
772 | [Accessed 22 November 2014]
773 |
--------------------------------------------------------------------------------
/content/chapter-05-layout-widgets/code/01Packbox.vala:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * Helper function that makes a new hbox filled with button-labels.
4 | * Arguments for the variables we're interested are passed in to
5 | * this function. We do not show the box, but do show everything
6 | * inside.
7 | */
8 | static Gtk.Box make_box (bool homogeneous, int spacing,
9 | bool expand, bool fill, int padding) {
10 |
11 | Gtk.Box box;
12 | Gtk.Button button;
13 | string padstr;
14 |
15 | /* Create a new Gtk.Box with the appropriate orientation
16 | * and spacing settings */
17 | box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, spacing);
18 | /* Set whether all child widgets be the same size. */
19 | box.set_homogeneous(homogeneous);
20 |
21 | /* Create a series of buttons with the appropriate settings */
22 | button = new Gtk.Button.with_label("box.pack");
23 | box.pack_start(button, expand, fill, padding);
24 | button.show();
25 |
26 | /*button = new Gtk.Button.with_label ("(box,");
27 | box.pack_start(button, expand, fill, padding);
28 | button.show();*/
29 |
30 | button = new Gtk.Button.with_label("(button,");
31 | box.pack_start(button, expand, fill, padding);
32 | button.show();
33 |
34 | /* Create a button with the label depending on the value of
35 | * expand. */
36 | button = new Gtk.Button.with_label(@"$expand,");
37 | /*if (expand == true)
38 | button = new Gtk.Button.with_label("true,");
39 | else
40 | button = new Gtk.Button.with_label("false,");*/
41 |
42 | box.pack_start(button, expand, fill, padding);
43 | button.show();
44 |
45 | /* This is the same as the button creation for "expand"
46 | * above, but uses the shorthand form. */
47 | button = new Gtk.Button.with_label(@"$fill,");
48 | box.pack_start (button, expand, fill, padding);
49 | button.show();
50 |
51 | padstr = @"$padding);";
52 |
53 | button = new Gtk.Button.with_label(padstr);
54 | box.pack_start(button, expand, fill, padding);
55 | button.show();
56 |
57 | return box;
58 | }
59 |
60 | class PackBox : Gtk.Window {
61 |
62 | public bool on_delete_event () {
63 | Gtk.main_quit();
64 | return false;
65 | }
66 |
67 | public PackBox (int which) {
68 |
69 | /* You should always remember to connect the delete_event signal
70 | * to the main window. This is very important for proper intuitive
71 | * behavior */
72 |
73 | this.delete_event.connect(this.on_delete_event);
74 | this.set_border_width(10);
75 |
76 | /* We create a vertical box (vbox) to pack the horizontal boxes
77 | * into. This allows us to stack the horizontal boxes filled with
78 | * buttons one on top of the other in this vbox. */
79 | var box1 = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
80 |
81 | /* Which example to show. These correspond to the pictures above. */
82 | switch (which) {
83 | case 1:
84 | /* create two new labels. */
85 | var label = new Gtk.Label("Gtk.Box(HORIZONTAL, 0)");
86 | var label2 = new Gtk.Label("box.set_homogeneous(false)");
87 |
88 | /* Align the labels to the left side. We'll discuss this method
89 | * and others in the section on Widget Attributes. */
90 | label.set_alignment(0, 0);
91 | label2.set_alignment(0, 0);
92 |
93 | /* Pack the labels into the vertical box (box box1). Remember
94 | * that widgets added to a vertically oriented box will be
95 | * packed one on top of the other in order. */
96 | box1.pack_start(label, false, false, 0);
97 | box1.pack_start(label2, false, false, 0);
98 |
99 | /* Show the labels. */
100 | label.show();
101 | label2.show();
102 |
103 | /* Call our make box function - homogeneous = false, spacing = 0,
104 | * expand = false, fill = false, padding = 0 */
105 | var box2 = make_box(false, 0, false, false, 0);
106 | box1.pack_start(box2, false, false, 0);
107 | box2.show();
108 |
109 | /* Call our make box function - homogeneous = false, spacing = 0,
110 | * expand = true, fill = false, padding = 0 */
111 | box2 = make_box(false, 0, true, false, 0);
112 | box1.pack_start(box2, false, false, 0);
113 | box2.show();
114 |
115 | /* Args are: homogeneous, spacing, expand, fill, padding */
116 | box2 = make_box(false, 0, true, true, 0);
117 | box1.pack_start(box2, false, false, 0);
118 | box2.show();
119 |
120 | /* Creates a separator, we'll learn more about these later,
121 | * but they are quite simple. */
122 | var separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL);
123 |
124 | /* Pack the separator into the vbox. Remember each of these
125 | * widgets is being packed into a vertically oriented box, so
126 | * they'll be stacked vertically. */
127 | box1.pack_start(separator, false, true, 5);
128 | separator.show();
129 |
130 | /* Create another new label, and show it. */
131 | label = new Gtk.Label("Gtk.Box(Gtk.Orientation.HORIZONTAL, 0)");
132 |
133 | label.set_alignment(0, 0);
134 | box1.pack_start(label, false, false, 0);
135 | label.show();
136 |
137 | label2 = new Gtk.Label("box.set_homogeneous(true)");
138 | label2.set_alignment(0, 0);
139 | box1.pack_start(label2, false, false, 0);
140 | label2.show();
141 |
142 |
143 | /* Args are: homogeneous, spacing, expand, fill, padding */
144 | box2 = make_box(true, 0, true, false, 0);
145 | box1.pack_start(box2, false, false, 0);
146 | box2.show();
147 |
148 | /* Args are: homogeneous, spacing, expand, fill, padding */
149 | box2 = make_box(true, 0, true, true, 0);
150 | box1.pack_start(box2, false, false, 0);
151 | box2.show();
152 |
153 | /* Another new separator. */
154 | separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL);
155 | /* The last 3 arguments to pack_start are:
156 | * expand, fill, padding. */
157 | box1.pack_start(separator, false, true, 5);
158 | separator.show();
159 | break;
160 | case 2:
161 | /* Create a new label, remember box1 is a vbox as created
162 | * near the beginning of the constructor. */
163 | var label = new Gtk.Label("Gtk.Box(HORIZONTAL, 10)");
164 | label.set_alignment( 0, 0);
165 | box1.pack_start(label, false, false, 0);
166 | label.show();
167 |
168 | var label2 = new Gtk.Label("box.set_homogeneous(false)");
169 | label2.set_alignment( 0, 0);
170 | box1.pack_start(label2, false, false, 0);
171 | label2.show();
172 |
173 | /* Args are: homogeneous, spacing, expand, fill, padding. */
174 | var box2 = make_box(false, 10, true, false, 0);
175 | box1.pack_start(box2, false, false, 0);
176 | box2.show();
177 |
178 | /* Args are: homogeneous, spacing, expand, fill, padding */
179 | box2 = make_box(false, 10, true, true, 0);
180 | box1.pack_start(box2, false, false, 0);
181 | box2.show();
182 |
183 | var separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL);
184 | /* The last 3 arguments to pack_start are:
185 | * expand, fill, padding. */
186 | box1.pack_start(separator, false, true, 5);
187 | separator.show();
188 |
189 | label = new Gtk.Label("Gtk.Box(HORIZONTAL, 0)");
190 | label.set_alignment(0, 0);
191 | box1.pack_start(label, false, false, 0);
192 | label.show();
193 |
194 | label2 = new Gtk.Label("box.set_homogeneous(false)");
195 | label2.set_alignment( 0, 0);
196 | box1.pack_start(label2, false, false, 0);
197 | label2.show();
198 |
199 | /* Args are: homogeneous, spacing, expand, fill, padding. */
200 | box2 = make_box(false, 0, true, false, 10);
201 | box1.pack_start(box2, false, false, 0);
202 | box2.show();
203 |
204 | /* Args are: homogeneous, spacing, expand, fill, padding. */
205 | box2 = make_box(false, 0, true, true, 10);
206 | box1.pack_start(box2, false, false, 0);
207 | box2.show();
208 |
209 | separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL);
210 | /* The last 3 arguments to pack_start are:
211 | * expand, fill, padding. */
212 | box1.pack_start(separator, false, true, 5);
213 | separator.show();
214 | break;
215 | case 3:
216 | /* This demonstrates the ability to use pack_end() to
217 | * right justify widgets. First, we create a new box as before. */
218 | var box2 = make_box(false, 0, false, false, 0);
219 |
220 | /* Create the label that will be put at the end. */
221 | var label = new Gtk.Label("end");
222 | /* Pack it using pack_end(), so it is put on the right
223 | * side of the hbox created in the make_box() call. */
224 | box2.pack_end(label, false, false, 0);
225 | /* Show the label. */
226 | label.show();
227 |
228 | /* Pack box2 into box1 */
229 | box1.pack_start(box2, false, false, 0);
230 | box2.show();
231 |
232 | /* A separator for the bottom. */
233 | var separator = new Gtk.Separator(Gtk.Orientation.HORIZONTAL);
234 |
235 | /* This explicitly sets the separator to 400 pixels wide by 5
236 | * pixels high. This is so the hbox we created will also be 400
237 | * pixels wide, and the "end" label will be separated from the
238 | * other labels in the hbox. Otherwise, all the widgets in the
239 | * hbox would be packed as close together as possible.
240 | * separator.set_size_request(400, 5)
241 | * pack the separator into the vbox (box1) created near the
242 | * start of the constructor. */
243 | box1.pack_start(separator, false, true, 5);
244 | separator.show();
245 | break;
246 | }
247 |
248 | /* Create another new hbox. Remember we can use as many as we need! */
249 | var quitbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
250 | quitbox.set_homogeneous(false);
251 |
252 | /* Our quit button. */
253 | var button = new Gtk.Button.with_label("Quit");
254 |
255 | /* Setup the signal to terminate the program when the button is
256 | * clicked */
257 | button.clicked.connect( () => { Gtk.main_quit(); } );
258 | /* Pack the button into the quitbox.
259 | * The last 3 arguments to pack_start are:
260 | * expand, fill, padding. */
261 | quitbox.pack_start(button, true, false, 0);
262 | /* pack the quitbox into the vbox (box1) */
263 | box1.pack_start(quitbox, false, false, 0);
264 |
265 | /* Pack the vbox (box1) which now contains all our widgets, into the
266 | * main window. */
267 | this.add(box1);
268 |
269 | /* And show everything left */
270 | button.show();
271 | quitbox.show();
272 | box1.show();
273 |
274 | /* Showing the window last so everything pops up at once. */
275 | this.show();
276 | }
277 |
278 | public static int main (string[] args) {
279 |
280 | if (args.length == 2) {
281 |
282 | Gtk.init(ref args);
283 |
284 | var window = new PackBox(int.parse(args[1]));
285 | window.show();
286 |
287 | /* And of course, our mainloop. */
288 | Gtk.main();
289 |
290 | /* Control returns here when Gtk.main_quit() is called. */
291 | return 0;
292 |
293 | } else {
294 | stderr.printf("usage: packbox num, where num is 1, 2, or 3.\n");
295 | /* This just does cleanup in GTK and exits with an exit status
296 | * of 1. */
297 | Process.exit (1);
298 | }
299 |
300 | }
301 |
302 | }
--------------------------------------------------------------------------------
/content/chapter-05-layout-widgets/code/02Grid.vala:
--------------------------------------------------------------------------------
1 |
2 | class GridExample : Gtk.Window {
3 |
4 | /* Our callback.
5 | * The data passed to this method is printed to stdout */
6 | void callback(string data) {
7 | stdout.printf("Hello again - %s was pressed\n", data);
8 | }
9 |
10 | /* This callback quits the program. */
11 | public bool on_delete_event() {
12 | Gtk.main_quit();
13 | return false;
14 | }
15 |
16 | public GridExample () {
17 |
18 | /* Set the window title. */
19 | this.set_title("Grid Packing Example");
20 |
21 | /* Set a handler for delete_event that immediately
22 | *exits Gtk. */
23 | this.delete_event.connect(this.on_delete_event);
24 |
25 | /* Sets the border width of the window. */
26 | this.set_border_width(20);
27 |
28 | /* Create a 2x2 table. */
29 | var grid = new Gtk.Grid();
30 |
31 | /* Put the table in the main window. */
32 | this.add(grid);
33 |
34 | /* Create first button. */
35 | var button = new Gtk.Button.with_label("button 1");
36 | /* When the button is clicked, we call the "callback" method. */
37 | button.clicked.connect( ()=>{ this.callback("button 1"); });
38 | grid.attach (button, 0, 0, 1, 1);
39 | button.show();
40 |
41 | /* Create second button. */
42 | button = new Gtk.Button.with_label("button 2");
43 | /* When the button is clicked, we call the "callback" method, this
44 | * time with a different button name. */
45 | button.clicked.connect( () => { this.callback("button 2"); } );
46 | /* Insert button 2 into the second column of the first row. */
47 | grid.attach(button, 1, 0, 1, 1);
48 | button.show();
49 |
50 | /* Create Third button. */
51 | button = new Gtk.Button.with_label("button 3");
52 | button.clicked.connect( () => { this.callback("button 3"); } );
53 | /* Insert button 3 to the right of button 2. */
54 | grid.attach_next_to(button, grid.get_child_at(0, 1), Gtk.PositionType.RIGHT, 1, 1);
55 | button.show();
56 |
57 | /* Create Fourth button. */
58 | button = new Gtk.Button.with_label("button 4");
59 | button.clicked.connect( () => { this.callback("button 4"); } );
60 | /* Insert button 4 into the 2nd row of the grid (below button 1). */
61 | grid.attach_next_to(button, grid.get_child_at(0, 0), Gtk.PositionType.BOTTOM, 1, 2);
62 | button.show();
63 |
64 | button = new Gtk.Button.with_label("button 5");
65 | button.clicked.connect( () => { this.callback("button 5"); } );
66 | /* Insert button 5 into the second row of the grid, to occupy 2
67 | * columns. */
68 | grid.attach(button, 1, 1, 2, 1);
69 | button.show();
70 |
71 | button = new Gtk.Button.with_label("button 6");
72 | button.clicked.connect( () => { this.callback("button 6"); } );
73 | /* Insert button 6 into the third row of the grid. */
74 | grid.attach(button, 1, 2, 1, 1);
75 | button.show();
76 |
77 | button = new Gtk.Button.with_label("button 7");
78 | button.clicked.connect( () => { this.callback("button 7"); } );
79 | /* Insert button 7 into the third row of the grid. */
80 | grid.attach(button, 2, 2, 1, 1);
81 | button.show();
82 |
83 | /* Create "Quit" button */
84 | button = new Gtk.Button.with_label("Quit");
85 | /* When the button is clicked, we call the main_quit function
86 | * and the program exits. */
87 | button.clicked.connect( ()=> { Gtk.main_quit(); });
88 | /* Insert the quit button into the fourth row of the grid. */
89 | grid.attach(button, 0, 3, 3, 1);
90 | button.show();
91 |
92 | grid.show();
93 |
94 | }
95 |
96 | public static int main(string[] args) {
97 |
98 | Gtk.init(ref args);
99 |
100 | var gridexample = new GridExample();
101 |
102 | gridexample.show();
103 |
104 | Gtk.main();
105 |
106 | return 0;
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/content/chapter-05-layout-widgets/code/03Stack.vala:
--------------------------------------------------------------------------------
1 |
2 | class StackExample : Gtk.Window {
3 |
4 | public StackExample () {
5 |
6 | this.set_title("Stack and StackSwitcher Demo");
7 | this.window_position = Gtk.WindowPosition.CENTER;
8 | this.set_default_size(350, 70);
9 | this.set_border_width(10);
10 | this.destroy.connect(Gtk.main_quit);
11 |
12 | var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 5);
13 | this.add(box);
14 |
15 | var stack = new Gtk.Stack();
16 | stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT_RIGHT);
17 | stack.set_transition_duration(1000);
18 |
19 | var label1 = new Gtk.Label("Page 1 Content.");
20 | stack.add_titled(label1, "page-1", "Page 1");
21 |
22 | var label2 = new Gtk.Label("Page 2 Content.");
23 | stack.add_titled(label2, "page-2", "Page 2");
24 |
25 | var label3 = new Gtk.Label("Page 3 Content.");
26 | stack.add_titled(label3, "page-3", "Page 3");
27 |
28 |
29 | var switcher = new Gtk.StackSwitcher();
30 | switcher.set_stack(stack);
31 | box.pack_start(switcher, true, true, 0);
32 | box.pack_start(stack, true, true, 0);
33 |
34 | }
35 |
36 | public static int main (string[] args) {
37 |
38 | Gtk.init(ref args);
39 |
40 | var win = new StackExample();
41 | win.show_all();
42 |
43 | Gtk.main();
44 |
45 | return 0;
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/content/chapter-05-layout-widgets/screenshots/01Packbox_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-05-layout-widgets/screenshots/01Packbox_1.png
--------------------------------------------------------------------------------
/content/chapter-05-layout-widgets/screenshots/01Packbox_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-05-layout-widgets/screenshots/01Packbox_2.png
--------------------------------------------------------------------------------
/content/chapter-05-layout-widgets/screenshots/01Packbox_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-05-layout-widgets/screenshots/01Packbox_3.png
--------------------------------------------------------------------------------
/content/chapter-05-layout-widgets/screenshots/02Grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-05-layout-widgets/screenshots/02Grid.png
--------------------------------------------------------------------------------
/content/chapter-05-layout-widgets/screenshots/03Stack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-05-layout-widgets/screenshots/03Stack.png
--------------------------------------------------------------------------------
/content/chapter-06-widget-overview/06-widget-overview.md:
--------------------------------------------------------------------------------
1 | # Widget Overview
2 |
3 | The general steps to using a widget in GTK are:
4 |
5 | * We use the constructor `Gtk.Widget()` to create a new `Gtk.Widget()`.
6 |
7 | * Connect all signals and events we wish to use to the appropriate handlers.
8 |
9 | * Set the attributes of the widget.
10 |
11 | * Pack the widget into a container using the appropriate call such as
12 | `Gtk.Container.add()` or `Gtk.Box.pack_start()`.
13 |
14 | * `Gtk.Widget.show()` the widget.
15 |
16 | `show()` lets GTK know that we are done setting the attributes of the widget,
17 | and it is ready to be displayed. You may also use `Gtk.Widget.hide()` to make it
18 | disappear again. The order in which you show the widgets is not important, but I
19 | suggest showing the window last so the whole window pops up at once rather than
20 | seeing the individual widgets come up on the screen as they're formed. The
21 | children of a widget (a window is a widget too) will not be displayed until the
22 | window itself is shown using the `show()` method.
23 |
24 |
25 |
26 | ## Widget Hierarchy
27 |
28 | For reference, a comprehensive hierarchy tree used to implement widgets may be
29 | found in the GTK online documetation at
30 | [https://developer.gnome.org/gtk3/stable/ch02.html](https://developer.gnome.org/gtk3/stable/ch02.html).
31 |
32 | This documentation lists the C API objects, but it is straightforward to infer
33 | the Vala API class names, especially with the help of the Valadoc documentation
34 | at [http://valadoc.org/#!api=gtk+-3.0/Gtk](http://valadoc.org/#!api=gtk+-3.0/Gtk).
35 |
36 |
37 |
38 | ## References and Further Reading
39 |
40 | * The GTK 3 Reference Manual : Object Hierarchy. [Online] Available from:
41 | [https://developer.gnome.org/gtk3/stable/ch02.html](https://developer.gnome.org/gtk3/stable/ch02.html)
42 | [Accessed 10 November 2014]
43 |
44 | * Vala Documentation: Gtk. [Online] Available from:
45 | [http://valadoc.org/#!api=gtk+-3.0/Gtk](http://valadoc.org/#!api=gtk+-3.0/Gtk)
46 | [Accessed 16 September 2014]
47 |
--------------------------------------------------------------------------------
/content/chapter-07-button-widgets/07-button-widgets.md:
--------------------------------------------------------------------------------
1 | # Button Widgets
2 |
3 | ## Normal Buttons
4 |
5 | A button (the `Gtk.Button` widget) is a widget that emits a signal when clicked
6 | on. We've almost seen all there is to see of the button widget . It's
7 | pretty simple, as seen from previous examples.
8 |
9 | There is more than one way to create a button:
10 |
11 | 1. You can use the `Gtk.Button()` creates a blank button. It is then up to you
12 | to pack labels and graphics into this button, usually using the `add()`
13 | callback.
14 |
15 | 2. `Gtk.Button.new_with_label()` and `Gtk.Button.new_with_mnemonic()` creates a
16 | button containing a textual label.
17 |
18 | 3. `Gtk.Button.new_with_mnemonic()` creates a button containing a textual label
19 | containing a *mnemonic*. Mnemonics are underlined characters in the label,
20 | used for keyboard navigation. Mnemonics are created by providing a string
21 | with an underscore before the mnemonic character, such as "`_File`".
22 |
23 |
24 | Here's an example of using `Gtk.Button.new()` to create a button with an image
25 | and a label in it. I've broken up the code to create a box from the rest so you
26 | can use it in your programs. There are further examples of using images later
27 | in the tutorial.
28 |
29 |
30 | /* Create a new box with an image and a label packed into it
31 | * and return the box. */
32 | static Gtk.Box xpm_label_box(string xpm_filename, string label_text ) {
33 | Gtk.Box box;
34 | Gtk.Label label;
35 | Gtk.Image image;
36 |
37 | /* Create box for image and label */
38 | box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
39 | box.set_border_width(2);
40 |
41 | /* Now on to the image stuff */
42 | image = new Gtk.Image.from_file(xpm_filename);
43 |
44 | /* Create a label for the button */
45 | label = new Gtk.Label(label_text);
46 |
47 | /* Pack the image and label into the box */
48 | box.pack_start(image, false, false, 3);
49 | box.pack_start(label, false, false, 3);
50 |
51 | image.show();
52 | label.show();
53 |
54 | return box;
55 | }
56 |
57 | class ButtonWindow : Gtk.Window {
58 |
59 | Gtk.Button button;
60 | Gtk.Box box;
61 |
62 | /* Our usual callback function */
63 | void callback (string data) {
64 | stdout.printf("Hello again - %s was pressed\n", data);
65 | }
66 |
67 | public ButtonWindow () {
68 |
69 | this.set_title("Pixmap'd Buttons!");
70 |
71 | /* It's a good idea to do this for all windows. */
72 | this.destroy.connect( ()=> { Gtk.main_quit(); } );
73 |
74 | this.delete_event.connect( ()=> { return false; } );
75 |
76 | /* Sets the border width of the window. */
77 | this.set_border_width(10);
78 |
79 | /* Create a new button. */
80 | this.button = new Gtk.Button();
81 |
82 | /* Connect the "clicked" signal of the button to our callback. */
83 | this.button.clicked.connect( ()=> { this.callback("cool button"); });
84 |
85 | /* This calls our box creating function. */
86 | this.box = xpm_label_box("img.png", "cool button");
87 |
88 | /* Pack and show all our widgets. */
89 | this.box.show();
90 |
91 | this.button.add(box);
92 |
93 | this.button.show();
94 |
95 | this.add(button);
96 |
97 | }
98 |
99 | public static int main (string[] args) {
100 |
101 | Gtk.init(ref args);
102 |
103 | var buttonwindow = new ButtonWindow();
104 | buttonwindow.show();
105 |
106 | Gtk.main();
107 |
108 | return 0;
109 | }
110 |
111 | }
112 |
113 |
114 | The `xpm_label_box()` function could be used to pack images and labels into any
115 | widget that can be a container.
116 |
117 | The signals that we are usually interested in when programming buttons are:
118 |
119 | * `button_press_event` - emitted when a button (typically from a mouse) is
120 | pressed. This is not `Gtk.Button`-specific signal, it may be emitted by any
121 | `Gtk.Widget`.
122 |
123 | * `button_release_event` - emitted when a button (typically from a mouse) is
124 | released. This is not `Gtk.Button`-specific signal, it may be emitted by any
125 | `Gtk.Widget`.
126 |
127 | * `clicked` - emitted when the `Button` has been activated (pressed and released).
128 |
129 | * `enter_notify_event` - emitted when pointer enters the `Button`. This is not
130 | `Button`-specific, it is a signal that may be emitted when the pointer enters
131 | any `Gtk.Widget`.
132 |
133 | * `leave_notify_event` - emitted when pointer leaves the `Button`. This is not
134 | `Button`-specific, it is a signal that may be emitted when the pointer enters
135 | any `Gtk.Widget`.
136 |
137 |
138 | ## Toggle Buttons
139 |
140 | Toggle buttons are derived from normal buttons and are very similar, except they
141 | will always be in one of two states, alternated by a click. They may be depressed,
142 | and when you click again, they will pop back up. Click again, and they will pop
143 | back down.
144 |
145 | Toggle buttons are the basis for check buttons and radio buttons, as such, many
146 | of the calls used for toggle buttons are inherited by radio and check buttons.
147 | I will point these out when we come to them.
148 |
149 | We use the following constructors to instantiate a `Gtk.ToggleButton`
150 |
151 |
152 | new Gtk.ToggleButton();
153 |
154 | new ToggleButton.with_label(string label)
155 |
156 | new ToggleButton.with_mnemonic(string label)
157 |
158 |
159 | As you can imagine, these work identically to the normal button widget calls.
160 | The first creates a blank toggle button, and the last two, a button with a label
161 | widget already packed into it. The `_mnemonic()` variant additionally parses the
162 | label for '_'-prefixed mnemonic characters.
163 |
164 | To retrieve the state of the toggle widget, including radio and check buttons,
165 | we use a construct as shown in our example below. This tests the state of the
166 | toggle button, by accessing the `active` field of the toggle widget's structure.
167 | The signal of interest to us emitted by toggle buttons (the *toggle button*,
168 | *check button*, and *radio button* widgets) is the "`toggled`" signal. To check
169 | the state of these buttons, set up a signal handler to catch the toggled signal,
170 | and access the structure to determine its state.
171 |
172 | The following example shows how to create and use toggle buttons:
173 |
174 |
175 | public class Application : Gtk.Window {
176 |
177 | private void toggled (Gtk.ToggleButton button) {
178 | stdout.printf("%s: %s\n", button.label, button.active ? "true" : "false");
179 | }
180 |
181 | public Application () {
182 |
183 | // Set Window Attributes
184 | this.title = "Toggle Buttons";
185 | this.window_position = Gtk.WindowPosition.CENTER;
186 | this.destroy.connect(Gtk.main_quit);
187 | this.set_default_size(350, 70);
188 | this.set_border_width(10);
189 |
190 | // Create a VBox to pack the radio buttons in.
191 | Gtk.Box box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
192 | this.add (box);
193 |
194 | // The buttons:
195 | Gtk.ToggleButton button1 = new Gtk.ToggleButton.with_label("Button 1");
196 | box.pack_start (button1, false, false, 0);
197 | button1.toggled.connect(toggled);
198 |
199 | Gtk.ToggleButton button2 = new Gtk.ToggleButton.with_label("Button 2");
200 | box.pack_start (button2, false, false, 0);
201 | button2.toggled.connect(toggled);
202 |
203 | }
204 |
205 | public static int main (string[] args) {
206 | Gtk.init(ref args);
207 |
208 | Application app = new Application();
209 | app.show_all();
210 | Gtk.main();
211 | return 0;
212 | }
213 | }
214 |
215 |
216 | Whe compiled, we get a window similar to the following:
217 |
218 |
220 |
240 | Gtk.CheckButton()
241 |
242 | Gtk.CheckButton.with_label(string label);
243 |
244 | Gtk.CheckButton.with_mnemonic(string label);
245 |
246 |
247 | The `Gtk.CheckButton.with_label(string label)` constructor creates a check
248 | button with a label beside it.
249 |
250 | Checking the state of the check button is identical to checking that of the
251 | toggle button, i.e. via the `active` property of the widget.
252 |
253 |
254 | ## Radio Buttons
255 |
256 | Radio buttons (implemented by the `Gtk.RadioButton` widget) are similar to check
257 | buttons except they are grouped so that only one may be selected/depressed at a
258 | time. This is good for places in your application where you need to give the
259 | user a choice from a short list of options.
260 |
261 | A single radio button performs the same basic function as a `Gtk.CheckButton`. It
262 | is only when multiple radio buttons are grouped together that they become a
263 | different user interface component in their own right. Every radio button is a
264 | member of some group of radio buttons. When one is selected, all other radio
265 | buttons in the same group are deselected.
266 |
267 | Creating a new radio button is done with one of these constructors:
268 |
269 |
270 | // Create a new radio button, and add it to group ().
271 | Gtk.RadioButton(GLib.SList? group);
272 |
273 | // Create a new radio button, adding it to the same group
274 | // as radio_group_member
275 | Gtk.RadioButton.from_widget(RadioButton? radio_group_member);
276 |
277 | // Create a new RadioButton with a text label and add it to group.
278 | Gtk.RadioButton.with_label(GLib.SList? group, string label);
279 |
280 | // Create a new RadioButton with a text label, adding it to the same group
281 | // as radio_group_member.
282 | Gtk.RadioButton.with_label_from_widget(RadioButton? radio_group_member, string label);
283 |
284 | // Create a new RadioButton containing a label, adding it to the same group
285 | // as group.
286 | Gtk.RadioButton.with_mnemonic (GLib.SList? group, string label)
287 |
288 | // Create a new RadioButton containing a label.
289 | public RadioButton.with_mnemonic_from_widget (RadioButton? radio_group_member, string label)
290 |
291 |
292 | You'll notice the extra argument to these calls. `Gtk.RadioButton`s require a
293 | group to perform their duty properly. The call to create the first `Gtk.RadioButton`
294 | should be passed `null` as the first argument. In subsequent calls, the group
295 | you wish to add this button to should be passed as an argument.
296 |
297 | The `_from_widget()` variants of the creation functions allow you to shorten
298 | further creation calls. This form is used in the example below.
299 |
300 | It is also a good idea to explicitly set which button should be the default
301 | depressed button.
302 |
303 | This is described in the section on toggle buttons, and works in exactly the
304 | same way. Once the radio buttons are grouped together, only one of the group
305 | may be active at a time. If the user clicks on one radio button, and then on
306 | another, the first radio button will first emit a "toggled" signal (to report
307 | becoming inactive), and then the second will emit its "toggled" signal (to
308 | report becoming active).
309 |
310 | The following example creates a radio button group with three buttons.
311 |
312 |
313 | public class Application : Gtk.Window {
314 |
315 | private void toggled (Gtk.ToggleButton button) {
316 | stdout.printf("%s\n", button.label);
317 | }
318 |
319 | public Application () {
320 |
321 | // Set Window Attributes
322 | this.title = "Radio Buttons";
323 | this.window_position = Gtk.WindowPosition.CENTER;
324 | this.destroy.connect(Gtk.main_quit);
325 | this.set_default_size(350, 70);
326 |
327 | // Create a VBox to pack the radio buttons in.
328 | Gtk.Box box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0);
329 | this.add (box);
330 |
331 | // The buttons:
332 | Gtk.RadioButton button1 = new Gtk.RadioButton.with_label_from_widget (null, "Button 1");
333 | box.pack_start (button1, false, false, 0);
334 | button1.toggled.connect(toggled);
335 |
336 | Gtk.RadioButton button = new Gtk.RadioButton.with_label_from_widget (button1, "Button 2");
337 | box.pack_start (button, false, false, 0);
338 | button.toggled.connect (toggled);
339 |
340 | button = new Gtk.RadioButton.with_label_from_widget (button1, "Button 3");
341 | box.pack_start(button, false, false, 0);
342 | button.toggled.connect(toggled);
343 | button.set_active (true);
344 |
345 | }
346 |
347 | public static int main (string[] args) {
348 | Gtk.init(ref args);
349 |
350 | Application app = new Application();
351 | app.show_all();
352 | Gtk.main();
353 | return 0;
354 | }
355 | }
356 |
357 | ## Link Button
358 |
359 | A `Gtk.LinkButton` is a `Gtk.Button` with a hyperlink, similar to the one used
360 | by web browsers, which triggers an action when clicked. It is useful to show
361 | quick links to resources.
362 |
363 | A link button is created by calling either
364 |
365 |
366 | Gtk.LinkButton(string uri)
367 |
368 |
369 | or
370 |
371 |
372 | Gtk.LinkButton.with_label(string uri, string label)
373 |
374 |
375 | If using the former, the URI you pass to the constructor is used as a label for
376 | the widget.
377 |
378 | The URI bound to a `Gtk.LinkButton` can be set and retrieved specifically using
379 | the property `Gtk.LinkButton.uri`.
380 |
381 | By default, `Gtk.LinkButton` calls `Gtk.show_uri()` when the button is clicked.
382 | To override this behaviour, you can connect to the `activate_link` signal and
383 | stop the propagation of the signal by returning `true` from the handler.
384 |
385 | The following example creates a single link button:
386 |
387 |
388 | public class Application : Gtk.Window {
389 |
390 | public Application () {
391 | // Prepare Gtk.Window:
392 | this.title = "My Gtk.LinkButton";
393 | this.window_position = Gtk.WindowPosition.CENTER;
394 | this.destroy.connect (Gtk.main_quit);
395 | this.set_default_size (350, 70);
396 |
397 | // The button:
398 | Gtk.LinkButton button = new Gtk.LinkButton.with_label ("https://developer.gnome.org/gtk3/stable/index.html", "GTK+ 3 Reference Manual");
399 | this.add (button);
400 | }
401 |
402 | public static int main (string[] args) {
403 | Gtk.init (ref args);
404 |
405 | Application app = new Application ();
406 | app.show_all ();
407 | Gtk.main ();
408 | return 0;
409 | }
410 | }
411 |
412 |
413 | When compiled an run, it should create a window similar to the following:
414 |
415 |
417 | public class Application : Gtk.Window {
54 |
55 | public Application () {
56 | // Prepare Gtk.Window:
57 | this.title = "Text Entry";
58 | this.window_position = Gtk.WindowPosition.CENTER;
59 | this.destroy.connect (Gtk.main_quit);
60 | this.set_default_size (350, 70);
61 | this.set_border_width(10);
62 |
63 | // The Entry:
64 | Gtk.Entry entry = new Gtk.Entry ();
65 | this.add (entry);
66 |
67 | // Add a default-text:
68 | entry.set_text ("Hello, world!");
69 |
70 | // Add a delete-button:
71 | entry.set_icon_from_icon_name (Gtk.EntryIconPosition.SECONDARY, "edit-clear");
72 | entry.icon_press.connect ((pos, event) => {
73 | if (pos == Gtk.EntryIconPosition.SECONDARY) {
74 | entry.set_text ("");
75 | }
76 | });
77 |
78 | // Print text to stdout on enter:
79 | entry.activate.connect (() => {
80 | unowned string str = entry.get_text ();
81 | stdout.printf ("%s\n", str);
82 | });
83 | }
84 |
85 | public static int main (string[] args) {
86 | Gtk.init (ref args);
87 |
88 | Application app = new Application ();
89 | app.show_all ();
90 | Gtk.main ();
91 | return 0;
92 | }
93 | }
94 |
95 |
96 | The code creates a window with a single text entry, as in the image below:
97 |
98 | Gtk.SpinButton (Gtk.Adjustment adjustment, double climb_rate, uint digits);
116 |
117 | The `adjustment` argument is an instance of `Gtk.Adjustment` object, which
118 | represents a value which has an associated lower and upper bound, together with
119 | step and page increments, and a page size. The `Gtk.Adjustment` object does not
120 | update the value itself. Instead it is left up to the owner of the `Adjustment`
121 | to control the value.
122 |
123 | The constructor for a `Gtk.Adjustment` is
124 |
125 | Gtk.Adjustment (double value, // The initial value of the adjustment
126 | double lower, // The minimum value of the adjustment
127 | double upper, // The maximum value of the adjustment
128 | double step_increment, // The step increment
129 | double page_increment, // The page increment
130 | double page_size // The page size. Irrelevant, and should be set to 0
131 | // if the adjustment is used for a simple scalar
132 | // value, e.g. in a SpinButton.
133 | );
134 |
135 |
136 | The owner of the `Gtk.Adjustment` typically calls the `value_changed` function
137 | after changing the value of the adjustment, causing the `value_changed` signal
138 | to be emitted. The owner may also call the `changed` function after changing one
139 | or more of the `Adjustment`'s properties other than the value. This results in
140 | the emission of the `changed` signal.
141 |
142 | The following example creates a `Gtk.SpinButton`, along with the associated
143 | `Gtk.Adjustment`, and outputs the values as they are changed to stdout.
144 |
145 | public class Application : Gtk.Window {
146 |
147 | public Application () {
148 | // Prepare Gtk.Window:
149 | this.title = "Text Entry";
150 | this.window_position = Gtk.WindowPosition.CENTER;
151 | this.destroy.connect (Gtk.main_quit);
152 | this.set_default_size (350, 70);
153 | this.set_border_width(10);
154 |
155 | // Create the Adjustment and SpinButton.
156 | Gtk.Adjustment adj = new Gtk.Adjustment(0, 0, 16, 1, 1, 1);
157 | Gtk.SpinButton button = new Gtk.SpinButton(adj, 1, 0);
158 | button.set_range(0, 16);
159 | this.add(button);
160 |
161 | button.value_changed.connect (() => {
162 | int val = button.get_value_as_int ();
163 | stdout.printf ("%d\n", val);
164 | });
165 | }
166 |
167 | public static int main (string[] args) {
168 | Gtk.init(ref args);
169 |
170 | Application app = new Application();
171 | app.show_all();
172 | Gtk.main();
173 | return 0;
174 | }
175 |
176 | }
177 |
178 |
179 | It should yield a window similar to the following:
180 |
181 |
183 | SpinButton.with_range (double min, double max, double step)
190 |
191 | allows creation of a numeric `Gtk.SpinButton` without manually creating an
192 | adjustment. The value is initially set to the `minimum` value and a page
193 | increment of `10 * step` is the default.
194 |
195 | In the example above, we'd replace the lines creating the adjustment and button
196 | with the lines
197 |
198 | Gtk.SpinButton button = new Gtk.SpinButton.with_range(0, 16, 1);
199 | this.add(button);
200 |
201 |
202 | to the same effect.
203 |
204 |
205 | ## Sources and Further Reading
206 |
207 | * The `GtkEntry` Section of the GTK 3 Reference Manual. [Online] Available from:
208 | [https://developer.gnome.org/gtk3/stable/GtkEntry.html](https://developer.gnome.org/gtk3/stable/GtkEntry.html)
209 | [Accessed 19 November 2014]
210 |
211 | * The `GtkSpinButton` Section of the GTK 3 Reference Manual. [Online] Available from:
212 | [https://developer.gnome.org/gtk3/stable/GtkSpinButton.html](https://developer.gnome.org/gtk3/stable/GtkSpinButton.html)
213 | [Accessed 19 November 2014]
214 |
215 | * Documentation on Gtk.Entry in Valadoc [Online] Available from:
216 | [http://valadoc.org/#!api=gtk+-3.0/Gtk.Entry](http://valadoc.org/#!api=gtk+-3.0/Gtk.Entry)
217 | [Accessed 19 September 2014]
--------------------------------------------------------------------------------
/content/chapter-08-numeric-and-text-entry/code/01GtkEntry.vala:
--------------------------------------------------------------------------------
1 |
2 | public class Application : Gtk.Window {
3 |
4 | public Application () {
5 | // Prepare Gtk.Window:
6 | this.title = "Text Entry";
7 | this.window_position = Gtk.WindowPosition.CENTER;
8 | this.destroy.connect (Gtk.main_quit);
9 | this.set_default_size (350, 70);
10 | this.set_border_width(10);
11 |
12 | // The Entry:
13 | Gtk.Entry entry = new Gtk.Entry ();
14 | this.add (entry);
15 |
16 | // Add a default-text:
17 | entry.set_text ("Hello, world!");
18 |
19 | // Add a delete-button:
20 | entry.set_icon_from_icon_name (Gtk.EntryIconPosition.SECONDARY, "edit-clear");
21 | entry.icon_press.connect ((pos, event) => {
22 | if (pos == Gtk.EntryIconPosition.SECONDARY) {
23 | entry.set_text ("");
24 | }
25 | });
26 |
27 | // Print text to stdout on enter:
28 | entry.activate.connect (() => {
29 | unowned string str = entry.get_text ();
30 | stdout.printf ("%s\n", str);
31 | });
32 | }
33 |
34 | public static int main (string[] args) {
35 | Gtk.init (ref args);
36 |
37 | Application app = new Application ();
38 | app.show_all ();
39 | Gtk.main ();
40 | return 0;
41 | }
42 | }
--------------------------------------------------------------------------------
/content/chapter-08-numeric-and-text-entry/code/02GtkSpinButton1.vala:
--------------------------------------------------------------------------------
1 |
2 | public class Application : Gtk.Window {
3 |
4 | public Application () {
5 | // Prepare Gtk.Window:
6 | this.title = "Spin Button";
7 | this.window_position = Gtk.WindowPosition.CENTER;
8 | this.destroy.connect (Gtk.main_quit);
9 | this.set_default_size (350, 70);
10 | this.set_border_width(10);
11 |
12 | // Create the Adjustment and SpinButton.
13 | Gtk.Adjustment adj = new Gtk.Adjustment(0, 0, 16, 1, 1, 1);
14 | Gtk.SpinButton button = new Gtk.SpinButton(adj, 1, 0);
15 | button.set_range(0, 16);
16 | this.add(button);
17 |
18 | button.value_changed.connect (() => {
19 | int val = button.get_value_as_int ();
20 | stdout.printf ("%d\n", val);
21 | });
22 |
23 | }
24 |
25 | public static int main (string[] args) {
26 | Gtk.init(ref args);
27 |
28 | Application app = new Application();
29 | app.show_all();
30 | Gtk.main();
31 | return 0;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/content/chapter-08-numeric-and-text-entry/code/03GtkSpinButton2.vala:
--------------------------------------------------------------------------------
1 |
2 | public class Application : Gtk.Window {
3 |
4 | public Application () {
5 | // Prepare Gtk.Window:
6 | this.title = "Spin Button";
7 | this.window_position = Gtk.WindowPosition.CENTER;
8 | this.destroy.connect (Gtk.main_quit);
9 | this.set_default_size (350, 70);
10 | this.set_border_width(10);
11 |
12 | // Create the SpinButton.
13 | Gtk.SpinButton button = new Gtk.SpinButton.with_range(0, 16, 1);
14 | this.add(button);
15 |
16 | button.value_changed.connect (() => {
17 | int val = button.get_value_as_int ();
18 | stdout.printf ("%d\n", val);
19 | });
20 |
21 | }
22 |
23 | public static int main (string[] args) {
24 | Gtk.init(ref args);
25 |
26 | Application app = new Application();
27 | app.show_all();
28 | Gtk.main();
29 | return 0;
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/content/chapter-08-numeric-and-text-entry/screenshots/01GtkEntry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-08-numeric-and-text-entry/screenshots/01GtkEntry.png
--------------------------------------------------------------------------------
/content/chapter-08-numeric-and-text-entry/screenshots/02SpinButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-08-numeric-and-text-entry/screenshots/02SpinButton.png
--------------------------------------------------------------------------------
/content/chapter-xx-adjustments/xx-adjustments.md:
--------------------------------------------------------------------------------
1 | Adjustments
2 |
3 | /* Code snippets here need quite some work. */
4 |
5 | GTK has various widgets that can be visually adjusted by the user using the
6 | mouse or the keyboard, such as the range widgets, described in the
7 | [LINK]Range Widgets[/LINK] section. There are also a few widgets that display
8 | some adjustable portion of a larger area of data, such as the text widget and
9 | the viewport widget.
10 |
11 | Obviously, an application needs to be able to react to changes the user makes
12 | in range widgets. One way to do this would be to have each widget emit its own
13 | type of signal when its adjustment changes, and either pass the new value to
14 | the signal handler, or require it to look inside the widget's data structure in
15 | order to ascertain the value. But you may also want to connect the adjustments
16 | of several widgets together, so that adjusting one adjusts the others. The most
17 | obvious example of this is connecting a scrollbar to a panning viewport or a
18 | scrolling text area. If each widget has its own way of setting or getting the
19 | adjustment value, then the programmer may have to write their own signal
20 | handlers to translate between the output of one widget's signal and the "input"
21 | of another's adjustment setting method.
22 |
23 | GTK solves this problem using the Adjustment object, which is not a widget but
24 | a way for widgets to store and pass adjustment information in an abstract and
25 | flexible form. The most obvious use of Adjustment is to store the configuration
26 | parameters and values of range widgets, such as scrollbars and scale controls.
27 | However, since Adjustments are derived from Object, they have some special
28 | powers beyond those of normal data structures. Most importantly, they can emit
29 | signals, just like widgets, and these signals can be used not only to allow your
30 | program to react to user input on adjustable widgets, but also to propagate
31 | adjustment values transparently between adjustable widgets.
32 |
33 | You will see how adjustments fit in when you see the other widgets that
34 | incorporate them: Progress Bars, Viewports, Scrolled Windows, and others.
35 |
36 |
37 |
38 |
39 | Creating an Adjustment
40 |
41 | Many of the widgets which use adjustment objects do so automatically, but some
42 | cases will be shown in later examples where you may need to create one yourself.
43 | You create an adjustment using:
44 |
45 | [CODE]
46 | adjustment = new Gtk.Adjustment (double value, double lower, double upper,
47 | double step_increment, double page_increment,
48 | double page_size);
49 | [/CODE]
50 |
51 | The [CODE]value[/CODE] argument is the initial value you want to give to the
52 | adjustment, usually corresponding to the topmost or leftmost position of an
53 | adjustable widget. The [CODE]lower[/CODE] argument specifies the lowest value
54 | which the adjustment can hold. The [CODE]step_increment[/CODE] argument
55 | specifies the "smaller" of the two increments by which the user can change the
56 | value, while the [CODE]page_increment[/CODE] is the "larger" one. The
57 | [CODE]page_size[/CODE] argument usually corresponds somehow to the visible area
58 | of a panning widget. The upper argument is used to represent the bottom most or
59 | right most coordinate in a panning widget's child. Therefore it is not always
60 | the largest number that value can take, since the [CODE]page_size[/CODE] of such
61 | widgets is usually non-zero.
62 |
63 |
64 |
65 |
66 | Using Adjustments the Easy Way
67 |
68 | The adjustable widgets can be roughly divided into those which use and require
69 | specific units for these values, and those which treat them as arbitrary
70 | numbers. The group which treats the values as arbitrary numbers includes the
71 | range widgets (scrollbars and scales, the progress bar widget, and the spin
72 | button widget). These widgets are all the widgets which are typically "adjusted"
73 | directly by the user with the mouse or keyboard. They will treat the lower and
74 | upper values of an adjustment as a range within which the user can manipulate
75 | the adjustment's value. By default, they will only modify the value of an
76 | adjustment.
77 |
78 | The other group includes the text widget, the viewport widget, the compound
79 | list widget, and the scrolled window widget. All of these widgets use pixel
80 | values for their adjustments. These are also all widgets which are typically
81 | "adjusted" indirectly using scrollbars. While all widgets which use adjustments
82 | can either create their own adjustments or use ones you supply, you'll generally
83 | want to let this particular category of widgets create its own adjustments.
84 | Usually, they will eventually override all the values except the value itself
85 | in whatever adjustments you give them, but the results are, in general,
86 | undefined (meaning, you'll have to read the source code to find out, and it may
87 | be different from widget to widget).
88 |
89 | Now, you're probably thinking, since text widgets and viewports insist on
90 | setting everything except the value of their adjustments, while scrollbars will
91 | only touch the adjustment's value, if you share an adjustment object between a
92 | scrollbar and a text widget, manipulating the scrollbar will automagically
93 | adjust the text widget? Of course it will! Just like this:
94 |
95 | [CODE]
96 | /* creates its own adjustments. */
97 | viewport = Gtk.Viewport();
98 | /* uses the newly-created adjustment for the scrollbar as well. */
99 | vscrollbar = Gtk.VScrollbar(viewport.get_vadjustment());
100 | [/CODE]
101 |
102 |
103 |
104 |
105 |
106 | Adjustment Internals
107 |
108 | Ok, you say, that's nice, but what if I want to create my own handlers to
109 | respond when the user adjusts a range widget or a spin button, and how do I get
110 | at the value of the adjustment in these handlers? To answer these questions and
111 | more, let's start by taking a look at the attributes of a
112 | [CODE]Gtk.Adjustment[/CODE] itself:
113 |
114 | [PRE]
115 | lower
116 | upper
117 | value
118 | step_increment
119 | page_increment
120 | page_size
121 | [/PRE]
122 |
123 | Given a [CODE]Gtk.Adjustment[/CODE] instance [CODE]adj[/CODE], each of the
124 | attributes are retrieved or set by adj.lower, adj.value, etc.
125 |
126 | Since, when you set the value of an adjustment, you generally want the change
127 | to be reflected by every widget that uses this adjustment, GTK+ provides a
128 | method to do this:
129 |
130 | [CODE]
131 | adjustment.set_value(double value)
132 | [/CODE]
133 |
134 | As mentioned earlier, [CODE]Adjustment[/CODE] is a subclass of [CODE]Object[/CODE]
135 | just like all the various widgets, and thus it is able to emit signals. This
136 | is, of course, why updates happen automagically when you share an adjustment
137 | object between a scrollbar and another adjustable widget; all adjustable widgets
138 | connect signal handlers to their adjustment's value_changed signal, as can your
139 | program. Here's the definition of this signal callback:
140 |
141 | [CODE]
142 | virtual void adjustment.value_changed():
143 | [/CODE]
144 |
145 | The various widgets that use the [CODE]Adjustment[/CODE] object will emit this
146 | signal on an adjustment whenever they change its value. This happens both when
147 | user input causes the slider to move on a range widget, as well as when the
148 | program explicitly changes the value with the [CODE]set_value()[/CODE] method.
149 | So, for example, if you have a scale widget, and you want to change the rotation
150 | of a picture whenever its value changes, you would create a callback like this:
151 |
152 | [CODE]
153 | void cb_rotate_picture(picture) {
154 | set_picture_rotation (picture, adj.value)
155 | }
156 | [/CODE]
157 |
158 | and connect it to the scale widget's adjustment like this:
159 |
160 | [CODE]
161 | adj.value_changed.connect(()=>{ cb_rotate_picture(picture); });
162 | [/CODE]
163 |
164 | What about when a widget reconfigures the upper or lower fields of its
165 | adjustment, such as when a user adds more text to a text widget? In this case,
166 | it emits the [CODE]changed[/CODE] signal, which looks like this:
167 |
168 | [CODE]
169 | void changed()
170 | [/CODE]
171 |
172 | Range widgets typically connect a handler to this signal, which changes their
173 | appearance to reflect the change - for example, the size of the slider in a
174 | scrollbar will grow or shrink in inverse proportion to the difference between
175 | the lower and upper values of its adjustment.
176 |
177 | You probably won't ever need to attach a handler to this signal, unless you're
178 | writing a new type of range widget. However, if you change any of the values in
179 | an [CODE]Adjustment[/CODE] directly, you should emit this signal on it to
180 | reconfigure whatever widgets are using it, like this:
181 |
182 | [CODE]
183 | adjustment.emit("changed")
184 | [/CODE]
185 |
186 |
--------------------------------------------------------------------------------