├── .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 | 8 | Everyone is permitted to copy and distribute verbatim copies 9 | of this license document, but changing it is not allowed. 10 | 11 | 0. PREAMBLE 12 | 13 | The purpose of this License is to make a manual, textbook, or other 14 | functional and useful document "free" in the sense of freedom: to 15 | assure everyone the effective freedom to copy and redistribute it, 16 | with or without modifying it, either commercially or noncommercially. 17 | Secondarily, this License preserves for the author and publisher a way 18 | to get credit for their work, while not being considered responsible 19 | for modifications made by others. 20 | 21 | This License is a kind of "copyleft", which means that derivative 22 | works of the document must themselves be free in the same sense. It 23 | complements the GNU General Public License, which is a copyleft 24 | license designed for free software. 25 | 26 | We have designed this License in order to use it for manuals for free 27 | software, because free software needs free documentation: a free 28 | program should come with manuals providing the same freedoms that the 29 | software does. But this License is not limited to software manuals; 30 | it can be used for any textual work, regardless of subject matter or 31 | whether it is published as a printed book. We recommend this License 32 | principally for works whose purpose is instruction or reference. 33 | 34 | 35 | 1. APPLICABILITY AND DEFINITIONS 36 | 37 | This License applies to any manual or other work, in any medium, that 38 | contains a notice placed by the copyright holder saying it can be 39 | distributed under the terms of this License. Such a notice grants a 40 | world-wide, royalty-free license, unlimited in duration, to use that 41 | work under the conditions stated herein. The "Document", below, 42 | refers to any such manual or work. Any member of the public is a 43 | licensee, and is addressed as "you". You accept the license if you 44 | copy, modify or distribute the work in a way requiring permission 45 | under copyright law. 46 | 47 | A "Modified Version" of the Document means any work containing the 48 | Document or a portion of it, either copied verbatim, or with 49 | modifications and/or translated into another language. 50 | 51 | A "Secondary Section" is a named appendix or a front-matter section of 52 | the Document that deals exclusively with the relationship of the 53 | publishers or authors of the Document to the Document's overall 54 | subject (or to related matters) and contains nothing that could fall 55 | directly within that overall subject. (Thus, if the Document is in 56 | part a textbook of mathematics, a Secondary Section may not explain 57 | any mathematics.) The relationship could be a matter of historical 58 | connection with the subject or with related matters, or of legal, 59 | commercial, philosophical, ethical or political position regarding 60 | them. 61 | 62 | The "Invariant Sections" are certain Secondary Sections whose titles 63 | are designated, as being those of Invariant Sections, in the notice 64 | that says that the Document is released under this License. If a 65 | section does not fit the above definition of Secondary then it is not 66 | allowed to be designated as Invariant. The Document may contain zero 67 | Invariant Sections. If the Document does not identify any Invariant 68 | Sections then there are none. 69 | 70 | The "Cover Texts" are certain short passages of text that are listed, 71 | as Front-Cover Texts or Back-Cover Texts, in the notice that says that 72 | the Document is released under this License. A Front-Cover Text may 73 | be at most 5 words, and a Back-Cover Text may be at most 25 words. 74 | 75 | A "Transparent" copy of the Document means a machine-readable copy, 76 | represented in a format whose specification is available to the 77 | general public, that is suitable for revising the document 78 | straightforwardly with generic text editors or (for images composed of 79 | pixels) generic paint programs or (for drawings) some widely available 80 | drawing editor, and that is suitable for input to text formatters or 81 | for automatic translation to a variety of formats suitable for input 82 | to text formatters. A copy made in an otherwise Transparent file 83 | format whose markup, or absence of markup, has been arranged to thwart 84 | or discourage subsequent modification by readers is not Transparent. 85 | An image format is not Transparent if used for any substantial amount 86 | of text. A copy that is not "Transparent" is called "Opaque". 87 | 88 | Examples of suitable formats for Transparent copies include plain 89 | ASCII without markup, Texinfo input format, LaTeX input format, SGML 90 | or XML using a publicly available DTD, and standard-conforming simple 91 | HTML, PostScript or PDF designed for human modification. Examples of 92 | transparent image formats include PNG, XCF and JPG. Opaque formats 93 | include proprietary formats that can be read and edited only by 94 | proprietary word processors, SGML or XML for which the DTD and/or 95 | processing tools are not generally available, and the 96 | machine-generated HTML, PostScript or PDF produced by some word 97 | processors for output purposes only. 98 | 99 | The "Title Page" means, for a printed book, the title page itself, 100 | plus such following pages as are needed to hold, legibly, the material 101 | this License requires to appear in the title page. For works in 102 | formats which do not have any title page as such, "Title Page" means 103 | the text near the most prominent appearance of the work's title, 104 | preceding the beginning of the body of the text. 105 | 106 | The "publisher" means any person or entity that distributes copies of 107 | the Document to the public. 108 | 109 | A section "Entitled XYZ" means a named subunit of the Document whose 110 | title either is precisely XYZ or contains XYZ in parentheses following 111 | text that translates XYZ in another language. (Here XYZ stands for a 112 | specific section name mentioned below, such as "Acknowledgements", 113 | "Dedications", "Endorsements", or "History".) To "Preserve the Title" 114 | of such a section when you modify the Document means that it remains a 115 | section "Entitled XYZ" according to this definition. 116 | 117 | The Document may include Warranty Disclaimers next to the notice which 118 | states that this License applies to the Document. These Warranty 119 | Disclaimers are considered to be included by reference in this 120 | License, but only as regards disclaiming warranties: any other 121 | implication that these Warranty Disclaimers may have is void and has 122 | no effect on the meaning of this License. 123 | 124 | 2. VERBATIM COPYING 125 | 126 | You may copy and distribute the Document in any medium, either 127 | commercially or noncommercially, provided that this License, the 128 | copyright notices, and the license notice saying this License applies 129 | to the Document are reproduced in all copies, and that you add no 130 | other conditions whatsoever to those of this License. You may not use 131 | technical measures to obstruct or control the reading or further 132 | copying of the copies you make or distribute. However, you may accept 133 | compensation in exchange for copies. If you distribute a large enough 134 | number of copies you must also follow the conditions in section 3. 135 | 136 | You may also lend copies, under the same conditions stated above, and 137 | you may publicly display copies. 138 | 139 | 140 | 3. COPYING IN QUANTITY 141 | 142 | If you publish printed copies (or copies in media that commonly have 143 | printed covers) of the Document, numbering more than 100, and the 144 | Document's license notice requires Cover Texts, you must enclose the 145 | copies in covers that carry, clearly and legibly, all these Cover 146 | Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on 147 | the back cover. Both covers must also clearly and legibly identify 148 | you as the publisher of these copies. The front cover must present 149 | the full title with all words of the title equally prominent and 150 | visible. You may add other material on the covers in addition. 151 | Copying with changes limited to the covers, as long as they preserve 152 | the title of the Document and satisfy these conditions, can be treated 153 | as verbatim copying in other respects. 154 | 155 | If the required texts for either cover are too voluminous to fit 156 | legibly, you should put the first ones listed (as many as fit 157 | reasonably) on the actual cover, and continue the rest onto adjacent 158 | pages. 159 | 160 | If you publish or distribute Opaque copies of the Document numbering 161 | more than 100, you must either include a machine-readable Transparent 162 | copy along with each Opaque copy, or state in or with each Opaque copy 163 | a computer-network location from which the general network-using 164 | public has access to download using public-standard network protocols 165 | a complete Transparent copy of the Document, free of added material. 166 | If you use the latter option, you must take reasonably prudent steps, 167 | when you begin distribution of Opaque copies in quantity, to ensure 168 | that this Transparent copy will remain thus accessible at the stated 169 | location until at least one year after the last time you distribute an 170 | Opaque copy (directly or through your agents or retailers) of that 171 | edition to the public. 172 | 173 | It is requested, but not required, that you contact the authors of the 174 | Document well before redistributing any large number of copies, to 175 | give them a chance to provide you with an updated version of the 176 | Document. 177 | 178 | 179 | 4. MODIFICATIONS 180 | 181 | You may copy and distribute a Modified Version of the Document under 182 | the conditions of sections 2 and 3 above, provided that you release 183 | the Modified Version under precisely this License, with the Modified 184 | Version filling the role of the Document, thus licensing distribution 185 | and modification of the Modified Version to whoever possesses a copy 186 | of it. In addition, you must do these things in the Modified Version: 187 | 188 | A. Use in the Title Page (and on the covers, if any) a title distinct 189 | from that of the Document, and from those of previous versions 190 | (which should, if there were any, be listed in the History section 191 | of the Document). You may use the same title as a previous version 192 | if the original publisher of that version gives permission. 193 | B. List on the Title Page, as authors, one or more persons or entities 194 | responsible for authorship of the modifications in the Modified 195 | Version, together with at least five of the principal authors of the 196 | Document (all of its principal authors, if it has fewer than five), 197 | unless they release you from this requirement. 198 | C. State on the Title page the name of the publisher of the 199 | Modified Version, as the publisher. 200 | D. Preserve all the copyright notices of the Document. 201 | E. Add an appropriate copyright notice for your modifications 202 | adjacent to the other copyright notices. 203 | F. Include, immediately after the copyright notices, a license notice 204 | giving the public permission to use the Modified Version under the 205 | terms of this License, in the form shown in the Addendum below. 206 | G. Preserve in that license notice the full lists of Invariant Sections 207 | and required Cover Texts given in the Document's license notice. 208 | H. Include an unaltered copy of this License. 209 | I. Preserve the section Entitled "History", Preserve its Title, and add 210 | to it an item stating at least the title, year, new authors, and 211 | publisher of the Modified Version as given on the Title Page. If 212 | there is no section Entitled "History" in the Document, create one 213 | stating the title, year, authors, and publisher of the Document as 214 | given on its Title Page, then add an item describing the Modified 215 | Version as stated in the previous sentence. 216 | J. Preserve the network location, if any, given in the Document for 217 | public access to a Transparent copy of the Document, and likewise 218 | the network locations given in the Document for previous versions 219 | it was based on. These may be placed in the "History" section. 220 | You may omit a network location for a work that was published at 221 | least four years before the Document itself, or if the original 222 | publisher of the version it refers to gives permission. 223 | K. For any section Entitled "Acknowledgements" or "Dedications", 224 | Preserve the Title of the section, and preserve in the section all 225 | the substance and tone of each of the contributor acknowledgements 226 | and/or dedications given therein. 227 | L. Preserve all the Invariant Sections of the Document, 228 | unaltered in their text and in their titles. Section numbers 229 | or the equivalent are not considered part of the section titles. 230 | M. Delete any section Entitled "Endorsements". Such a section 231 | may not be included in the Modified Version. 232 | N. Do not retitle any existing section to be Entitled "Endorsements" 233 | or to conflict in title with any Invariant Section. 234 | O. Preserve any Warranty Disclaimers. 235 | 236 | If the Modified Version includes new front-matter sections or 237 | appendices that qualify as Secondary Sections and contain no material 238 | copied from the Document, you may at your option designate some or all 239 | of these sections as invariant. To do this, add their titles to the 240 | list of Invariant Sections in the Modified Version's license notice. 241 | These titles must be distinct from any other section titles. 242 | 243 | You may add a section Entitled "Endorsements", provided it contains 244 | nothing but endorsements of your Modified Version by various 245 | parties--for example, statements of peer review or that the text has 246 | been approved by an organization as the authoritative definition of a 247 | standard. 248 | 249 | You may add a passage of up to five words as a Front-Cover Text, and a 250 | passage of up to 25 words as a Back-Cover Text, to the end of the list 251 | of Cover Texts in the Modified Version. Only one passage of 252 | Front-Cover Text and one of Back-Cover Text may be added by (or 253 | through arrangements made by) any one entity. If the Document already 254 | includes a cover text for the same cover, previously added by you or 255 | by arrangement made by the same entity you are acting on behalf of, 256 | you may not add another; but you may replace the old one, on explicit 257 | permission from the previous publisher that added the old one. 258 | 259 | The author(s) and publisher(s) of the Document do not by this License 260 | give permission to use their names for publicity for or to assert or 261 | imply endorsement of any Modified Version. 262 | 263 | 264 | 5. COMBINING DOCUMENTS 265 | 266 | You may combine the Document with other documents released under this 267 | License, under the terms defined in section 4 above for modified 268 | versions, provided that you include in the combination all of the 269 | Invariant Sections of all of the original documents, unmodified, and 270 | list them all as Invariant Sections of your combined work in its 271 | license notice, and that you preserve all their Warranty Disclaimers. 272 | 273 | The combined work need only contain one copy of this License, and 274 | multiple identical Invariant Sections may be replaced with a single 275 | copy. If there are multiple Invariant Sections with the same name but 276 | different contents, make the title of each such section unique by 277 | adding at the end of it, in parentheses, the name of the original 278 | author or publisher of that section if known, or else a unique number. 279 | Make the same adjustment to the section titles in the list of 280 | Invariant Sections in the license notice of the combined work. 281 | 282 | In the combination, you must combine any sections Entitled "History" 283 | in the various original documents, forming one section Entitled 284 | "History"; likewise combine any sections Entitled "Acknowledgements", 285 | and any sections Entitled "Dedications". You must delete all sections 286 | Entitled "Endorsements". 287 | 288 | 289 | 6. COLLECTIONS OF DOCUMENTS 290 | 291 | You may make a collection consisting of the Document and other 292 | documents released under this License, and replace the individual 293 | copies of this License in the various documents with a single copy 294 | that is included in the collection, provided that you follow the rules 295 | of this License for verbatim copying of each of the documents in all 296 | other respects. 297 | 298 | You may extract a single document from such a collection, and 299 | distribute it individually under this License, provided you insert a 300 | copy of this License into the extracted document, and follow this 301 | License in all other respects regarding verbatim copying of that 302 | document. 303 | 304 | 305 | 7. AGGREGATION WITH INDEPENDENT WORKS 306 | 307 | A compilation of the Document or its derivatives with other separate 308 | and independent documents or works, in or on a volume of a storage or 309 | distribution medium, is called an "aggregate" if the copyright 310 | resulting from the compilation is not used to limit the legal rights 311 | of the compilation's users beyond what the individual works permit. 312 | When the Document is included in an aggregate, this License does not 313 | apply to the other works in the aggregate which are not themselves 314 | derivative works of the Document. 315 | 316 | If the Cover Text requirement of section 3 is applicable to these 317 | copies of the Document, then if the Document is less than one half of 318 | the entire aggregate, the Document's Cover Texts may be placed on 319 | covers that bracket the Document within the aggregate, or the 320 | electronic equivalent of covers if the Document is in electronic form. 321 | Otherwise they must appear on printed covers that bracket the whole 322 | aggregate. 323 | 324 | 325 | 8. TRANSLATION 326 | 327 | Translation is considered a kind of modification, so you may 328 | distribute translations of the Document under the terms of section 4. 329 | Replacing Invariant Sections with translations requires special 330 | permission from their copyright holders, but you may include 331 | translations of some or all Invariant Sections in addition to the 332 | original versions of these Invariant Sections. You may include a 333 | translation of this License, and all the license notices in the 334 | Document, and any Warranty Disclaimers, provided that you also include 335 | the original English version of this License and the original versions 336 | of those notices and disclaimers. In case of a disagreement between 337 | the translation and the original version of this License or a notice 338 | or disclaimer, the original version will prevail. 339 | 340 | If a section in the Document is Entitled "Acknowledgements", 341 | "Dedications", or "History", the requirement (section 4) to Preserve 342 | its Title (section 1) will typically require changing the actual 343 | title. 344 | 345 | 346 | 9. TERMINATION 347 | 348 | You may not copy, modify, sublicense, or distribute the Document 349 | except as expressly provided under this License. Any attempt 350 | otherwise to copy, modify, sublicense, or distribute it is void, and 351 | will automatically terminate your rights under this License. 352 | 353 | However, if you cease all violation of this License, then your license 354 | from a particular copyright holder is reinstated (a) provisionally, 355 | unless and until the copyright holder explicitly and finally 356 | terminates your license, and (b) permanently, if the copyright holder 357 | fails to notify you of the violation by some reasonable means prior to 358 | 60 days after the cessation. 359 | 360 | Moreover, your license from a particular copyright holder is 361 | reinstated permanently if the copyright holder notifies you of the 362 | violation by some reasonable means, this is the first time you have 363 | received notice of violation of this License (for any work) from that 364 | copyright holder, and you cure the violation prior to 30 days after 365 | your receipt of the notice. 366 | 367 | Termination of your rights under this section does not terminate the 368 | licenses of parties who have received copies or rights from you under 369 | this License. If your rights have been terminated and not permanently 370 | reinstated, receipt of a copy of some or all of the same material does 371 | not give you any rights to use it. 372 | 373 | 374 | 10. FUTURE REVISIONS OF THIS LICENSE 375 | 376 | The Free Software Foundation may publish new, revised versions of the 377 | GNU Free Documentation License from time to time. Such new versions 378 | will be similar in spirit to the present version, but may differ in 379 | detail to address new problems or concerns. See 380 | http://www.gnu.org/copyleft/. 381 | 382 | Each version of the License is given a distinguishing version number. 383 | If the Document specifies that a particular numbered version of this 384 | License "or any later version" applies to it, you have the option of 385 | following the terms and conditions either of that specified version or 386 | of any later version that has been published (not as a draft) by the 387 | Free Software Foundation. If the Document does not specify a version 388 | number of this License, you may choose any version ever published (not 389 | as a draft) by the Free Software Foundation. If the Document 390 | specifies that a proxy can decide which future versions of this 391 | License can be used, that proxy's public statement of acceptance of a 392 | version permanently authorizes you to choose that version for the 393 | Document. 394 | 395 | 11. RELICENSING 396 | 397 | "Massive Multiauthor Collaboration Site" (or "MMC Site") means any 398 | World Wide Web server that publishes copyrightable works and also 399 | provides prominent facilities for anybody to edit those works. A 400 | public wiki that anybody can edit is an example of such a server. A 401 | "Massive Multiauthor Collaboration" (or "MMC") contained in the site 402 | means any set of copyrightable works thus published on the MMC site. 403 | 404 | "CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 405 | license published by Creative Commons Corporation, a not-for-profit 406 | corporation with a principal place of business in San Francisco, 407 | California, as well as future copyleft versions of that license 408 | published by that same organization. 409 | 410 | "Incorporate" means to publish or republish a Document, in whole or in 411 | part, as part of another Document. 412 | 413 | An MMC is "eligible for relicensing" if it is licensed under this 414 | License, and if all works that were first published under this License 415 | somewhere other than this MMC, and subsequently incorporated in whole or 416 | in part into the MMC, (1) had no cover texts or invariant sections, and 417 | (2) were thus incorporated prior to November 1, 2008. 418 | 419 | The operator of an MMC Site may republish an MMC contained in the site 420 | under CC-BY-SA on the same site at any time before August 1, 2009, 421 | provided the MMC is eligible for relicensing. 422 | 423 | 424 | ADDENDUM: How to use this License for your documents 425 | 426 | To use this License in a document you have written, include a copy of 427 | the License in the document and put the following copyright and 428 | license notices just after the title page: 429 | 430 | Copyright (c) YEAR YOUR NAME. 431 | Permission is granted to copy, distribute and/or modify this document 432 | under the terms of the GNU Free Documentation License, Version 1.3 433 | or any later version published by the Free Software Foundation; 434 | with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. 435 | A copy of the license is included in the section entitled "GNU 436 | Free Documentation License". 437 | 438 | If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, 439 | replace the "with...Texts." line with this: 440 | 441 | with the Invariant Sections being LIST THEIR TITLES, with the 442 | Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. 443 | 444 | If you have Invariant Sections without Cover Texts, or some other 445 | combination of the three, merge those two alternatives to suit the 446 | situation. 447 | 448 | If your document contains nontrivial examples of program code, we 449 | recommend releasing these examples in parallel under your choice of 450 | free software license, such as the GNU General Public License, 451 | to permit their use in free software. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | This is an introductory tutorial on programming for the GNOME platform using 3 | [GTK+3](http://www.gtk.org) in [Vala](https://live.gnome.org/Vala). It is based 4 | on and will closely follow the popular 5 | [GTK+2.0 tutorial](http://developer.gnome.org/gtk-tutorial/2.90/) written by 6 | Tony Gale, Ian Main, and the GTK team. 7 | 8 | Chapters from this tutorial can be read as articles on my personal website at 9 | [abenga.com](http://www.abenga.com/postseries/introduction-to-gtk+-programming-using-vala/). 10 | -------------------------------------------------------------------------------- /content/chapter-01-introduction/01-introduction.md: -------------------------------------------------------------------------------- 1 | # Background 2 | 3 | ## About GTK 4 | 5 | [GTK+](http://www.gtk.org) (acronym for the *GIMP Toolkit*) is a 6 | library for creating graphical user interfaces. It works on many 7 | UNIX-like platforms, Windows, and OS X. 8 | 9 | It's called the GIMP toolkit because it was originally written for 10 | developing the [GNU Image Manipulation Program](http://www.gimp.org) (a popular 11 | free cross platform photo editor), but GTK+ has now been used in a large number 12 | of software projects, including [GNOME](http://www.gnome.org) (a popular desktop 13 | environment for Linux). 14 | 15 | GTK+ is released under the 16 | [GNU Library General Public License](http://www.gnu.org/licenses/lgpl.html) 17 | (GNU LGPL), which allows for flexible licensing of client applications. You can 18 | develop open software, free software, or even commercial non-free software using 19 | GTK+ without having to spend anything for licenses or royalties. GTK+ has a 20 | C-based object-oriented architecture that allows for maximum flexibility. 21 | 22 | Bindings for many other languages have been written, including 23 | [C++](http://www.gtkmm.org/), [Objective-C](https://code.google.com/p/obgtk/), 24 | [Guile/Scheme](http://www.gnu.org/software/guile-gtk/), 25 | [Perl](https://metacpan.org/module/Gtk3), [Python](http://www.pygtk.org/), 26 | TOM, Ada95, Free Pascal, Eiffel and [Vala](https://live.gnome.org/Vala/). This 27 | tutorial describes the Vala interface to GTK. 28 | 29 | GTK+ depends on the following libraries: 30 | 31 | * **GLib** 32 | 33 | [GLib](https://developer.gnome.org/glib/) is a general-purpose 34 | utility library, not specific to graphical user interfaces. GLib 35 | provides many useful data types, macros, type conversions, string 36 | utilities, file utilities, a main loop abstraction, and so on. 37 | 38 | * **GObject** 39 | 40 | [GObject](https://developer.gnome.org/gobject/stable/‎) is library that 41 | provides a type system, a collection of fundamental types 42 | including an object type, a signal system.GObject, and its lower-level 43 | type system, GType, are used by GTK+ and most GNOME libraries to 44 | provide Object-oriented C-based APIs and automatic transparent API 45 | bindings to other compiled or interpreted languages. 46 | 47 | * **GIO** 48 | 49 | [GIO](https://developer.gnome.org/gio/) is a modern, easy-to-use 50 | Virtual File System API including abstractions for files, drives, 51 | volumes, stream IO, as well as network programming and DBus 52 | communication. 53 | 54 | A virtual file system (VFS) or virtual filesystem switch is an 55 | abstraction layer on top of a more concrete file system. The purpose of 56 | a VFS is to allow client applications to access different types of 57 | concrete file systems (Ext3, FAT, NTFS, etc) in a uniform way. 58 | 59 | * **Cairo** 60 | 61 | [Cairo](http://www.cairographics.org/‎) is a 2D graphics library with 62 | support for multiple output devices. 63 | 64 | * **Pango** 65 | 66 | [Pango](http://pango.org) is a library for internationalized text 67 | handling. It centers around the `PangoLayout` object, representing a 68 | paragraph of text. Pango provides the engine for `Gtk.TextView`, 69 | `Gtk.Label`, `Gtk.Entry`, and other widgets that display text. 70 | 71 | * **ATK** 72 | 73 | [ATK](https://developer.gnome.org/atk/) is the Accessibility Toolkit. 74 | It provides a set of generic interfaces allowing accessibility 75 | technologies (technologies that allow people with physical disabilities, 76 | e.g. blindness to use computers) to interact with a graphical user 77 | interface. For example, a screen reader uses ATK to discover the text 78 | in an interface and read it to blind users. GTK+ widgets have built-in 79 | support for accessibility using the ATK framework. 80 | 81 | * **GdkPixbuf** 82 | 83 | [GdkPixbuf](https://developer.gnome.org/gdk-pixbuf/) is a small library 84 | which allows you to create GdkPixbuf ("pixel buffer") objects from 85 | image data or image files. Use a GdkPixbuf in combination with 86 | `Gtk.Image` to display images. 87 | 88 | * **GDK** 89 | 90 | [GDK](https://developer.gnome.org/gdk/) is the abstraction layer that 91 | allows GTK+ to support multiple windowing systems. GDK provides window 92 | system facilities on X11, Windows, and OS X. 93 | 94 | GTK+ is essentially an object oriented application programming interface 95 | (API). Although written completely in C, it is implemented using the 96 | idea of classes and callback functions. 97 | 98 | ## About Vala 99 | 100 | [Vala](https://live.gnome.org/Vala/) is a new programming language that aims 101 | to bring modern programming language features to GNOME developers without 102 | imposing any additional runtime requirements and without using a different 103 | ABI compared to applications and libraries written in C. 104 | 105 | According to the [Vala tutorial](https://wiki.gnome.org/Vala/Tutorial): 106 | 107 | > Vala is a new programming language that allows modern programming techniques 108 | > to be used to write applications that run on the GNOME runtime libraries, 109 | > particularly GLib and GObject. This platform has long provided a very complete 110 | > programming environment, with such features as a dynamic type system and 111 | > assisted memory management. Before Vala, the only ways to program for the 112 | > platform were with the machine native C API, which exposes a lot of often 113 | > unwanted detail, with a high level language that has an attendant virtual 114 | > machine, such as Python or the Mono C# language, or alternatively, with C++ 115 | > through a wrapper library. 116 | > 117 | > Vala is different from all these other techniques, as it outputs C code which 118 | > can be compiled to run with no extra library support beyond the GNOME platform. 119 | > This has several consequences, but most importantly: 120 | > 121 | > * Programs written in Vala should have broadly similar performance to those 122 | > written directly in C, whilst being easier and faster to write and maintain. 123 | > 124 | > * A Vala application can do nothing that a C equivalent cannot. Whilst Vala 125 | > introduces a lot of language features that are not available in C, these are 126 | > all mapped to C constructs, although they are often ones that are difficult 127 | > or too time consuming to write directly. 128 | > 129 | > As such, whilst Vala is a modern language with all of the features you would 130 | > expect, it gains its power from an existing platform, and must in some ways 131 | > comply with the rules set down by it. 132 | 133 | 134 | ## References 135 | 136 | * The GTK+ 2.0 Tutorial: Introduction. [Online] Available from: 137 | [https://developer.gnome.org/gtk-tutorial/2.90/c24.html](https://developer.gnome.org/gtk-tutorial/2.90/c24.html) 138 | [Accessed 16 September 2014] 139 | 140 | * The Vala Tutorial. [Online] Available from: 141 | [https://wiki.gnome.org/Projects/Vala/Tutorial](https://wiki.gnome.org/Projects/Vala/Tutorial) 142 | [Accessed: 16 September 2014] 143 | -------------------------------------------------------------------------------- /content/chapter-02-getting-started/02-getting-started.md: -------------------------------------------------------------------------------- 1 | 2 | # Getting Started 3 | 4 | ## Installation 5 | 6 | Before we can begin programming, we need to install GTK+ and Vala, 7 | along with their dependencies. 8 | 9 | ### Installing GTK+ 10 | 11 | You may install GTK+ and its various dependencies from source by 12 | downloading several archives, untarring them and running 13 | `./configure`, `make` and `make install` to install them. 14 | 15 | However, if you're using a Linux distribution with a package manager, 16 | it is easier to install the precompiled packages from the distribution's 17 | repositories. The packages would also receive security updates and any 18 | other upgrades as they are rolled out by the distribution. 19 | 20 | You need to install the `-dev` packages that contain 21 | the header files and static libraries that are needed to compile GTK+ 22 | applications. Installing the GTK+ development package will also install 23 | all its dependecies (the header files for **GLib**, 24 | **gdk-pixbuf**, etc). 25 | 26 | On Debian and its derivatives, the command to do this would be: 27 | 28 |
# 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 |
50 | Simple Window 51 |
Simple Window
52 |
53 | 54 | Just closing this window does not kill the program, however (the window will be 55 | closed, but the program will keep on running). To stop execution of the program, 56 | you have to press `CTR+C` in the console from which you launched the program. 57 | 58 | The line: 59 | 60 |
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 |
127 | GTK Hello World 128 |
Hello World a la GTK
129 |
130 | 131 |
/** 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 |
241 | Upgraded Hello World Example 242 |
Upgraded Hello World Example
243 |
244 | 245 | You'll notice this time there is no way to exit the program except to use your 246 | window manager or command line to kill it. A good exercise for the reader would 247 | be to insert a third "Quit" button that will exit the program. You may also wish 248 | to play with the options to `pack_start()` while reading the next section. Try 249 | resizing the window, and observe the behavior. 250 | 251 | A short commentary on the code differences from the first *Hello World* program 252 | is in order. 253 | 254 | As noted above there is no "destroy" event handler in the upgraded *Hello World*. 255 | 256 | The lines 257 | 258 |
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 |
106 | Packing: Five Variations 107 |
Packing: Five Variations
108 |
109 | 110 | Each line contains one box with several buttons. The call to pack is shorthand 111 | for the call to pack each of the buttons into the box. Each of the buttons is 112 | packed into the box the same way (i.e., with the same arguments to the 113 | `pack_start()` method). 114 | 115 | The expand argument to `pack_start()` and `pack_end()` controls whether the 116 | widgets are laid out in the box to fill in all the extra space in the box so the 117 | box is expanded to fill the area allotted to it (`true`); or the box is shrunk 118 | to just fit the widgets (`false`). Setting expand to `false` will allow you to 119 | do right and left justification of your widgets. Otherwise, they will all expand 120 | to fit into the box, and the same effect could be achieved by using only one of 121 | `pack_start()` or `pack_end()`. 122 | 123 | The `fill` argument to the pack methods control whether the extra space is 124 | allocated to the objects themselves (`true`), or as extra padding in the box 125 | around these objects (`false`). It's value affects the appearance of the box and 126 | its widgets only if the `expand` argument is also set to `true`. 127 | 128 | Vala allows a method or function to be defined with default argument values for 129 | the last parameters of a method, so that you do not have to explicitly pass 130 | values for them with every call. For example the `pack_start()` and `pack_end()` 131 | method are defined as above, with default arguments for `expand`, `fill` and 132 | `padding` as shown above. The `child` argument, however, must be specified. This 133 | means that one may add a widget `child` to a box `box` using the call 134 | 135 |
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 |
149 | Packing with Spacing and Padding 150 |
Packing with Spacing and Padding
151 |
152 | 153 | The figure below, “Packing with `pack_end()`” illustrates the use of the 154 | `pack_end()` method (pass an argument of 3 to the compiled executable). The 155 | label "end" is packed with the `pack_end()` method. It will stick to the right 156 | edge of the window when the window is resized. 157 | 158 |
159 | Packing with pack_end() 160 |
Packing with pack_end()
161 |
162 | 163 | 164 | The following code contains the code used to create the above images. Try to 165 | play with it to see the effect of any changes. 166 | 167 |
/*
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 |
677 | Grid Packing Example Image 678 |
Grid Packing Example
679 |
680 | 681 | 682 | ## Stacks and StackSwitchers 683 | 684 | The `Gtk.Stack` widget is a container which only shows one of its children at a 685 | time. A `Gtk.StackSwitcher` widget can be used with a `Gtk.Stack` to provide 686 | this functionality. The `StackSwitcher` widget shows a row of buttons to switch 687 | between the various pages of the associated `Gtk.Stack`. 688 | 689 | Transitions between pages can be animated as slides or fades. This can be 690 | controlled with `Gtk.Stack.set_transition_type()`. These animations respect the 691 | “`gtk-enable-animations`” setting. 692 | 693 | The window below 694 | 695 |
696 | Stack & StackSwitcher Example 697 |
Stack & StackSwitcher Example
698 |
699 | 700 | is obtained when you compile and run the code: 701 | 702 |
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 |
219 | Toggle Buttons 220 |
Toggle Buttons
221 |
222 | 223 | 224 | To get and set the state of a `Gtk.ToggleButton`, `Gtk.RadioButton`, and 225 | `Gtk.CheckButton`, we can use the `active` property of these widgets. It is a 226 | boolean property, which is `true` when the button is active (depressed) and 227 | false otherwise. 228 | 229 | 230 | ## Check Buttons 231 | 232 | Check buttons inherit many properties and functions from the the toggle buttons 233 | above, but look a little different. Rather than being buttons with text inside 234 | them, they place a discrete `Gtk.ToggleButton` next to a widget, (usually a 235 | `Gtk.Label`). 236 | 237 | The `Gtk.CheckButton` constructors are similar to those for the `Gtk.ToggleButton`: 238 | 239 |

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 |
416 | Link Button 417 |
Link Button
418 |
419 | 420 | 421 | 422 | ## References and Further Reading 423 | 424 | * The GTK 3 Reference Manual : Buttons and Toggles. [Online] Available from: 425 | [https://developer.gnome.org/gtk3/stable/ButtonWidgets.html](https://developer.gnome.org/gtk3/stable/ButtonWidgets.html) 426 | [Accessed 13th November 2014] 427 | 428 | * The Python GTK 3 Tutorial : Button Widgets. [Online] Available from: 429 | [http://python-gtk-3-tutorial.readthedocs.org/en/latest/button_widgets.html](http://python-gtk-3-tutorial.readthedocs.org/en/latest/button_widgets.html) 430 | [Accessed 13th November 2014] 431 | 432 | * Documentation on Gtk.LinkButton in Valadoc [Online] Available from: 433 | [http://valadoc.org/#!api=gtk+-3.0/Gtk.LinkButton](http://valadoc.org/#!api=gtk+-3.0/Gtk.LinkButton) 434 | [Accessed 13th November 2014] -------------------------------------------------------------------------------- /content/chapter-07-button-widgets/code/01Button.vala: -------------------------------------------------------------------------------- 1 | 2 | /* Create a new box with an image and a label packed into it 3 | * and return the box. */ 4 | static Gtk.Box xpm_label_box(string xpm_filename, 5 | string label_text ) { 6 | Gtk.Box box; 7 | Gtk.Label label; 8 | Gtk.Image image; 9 | 10 | /* Create box for image and label */ 11 | box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0); 12 | box.set_border_width(2); 13 | 14 | /* Now on to the image stuff */ 15 | image = new Gtk.Image.from_file(xpm_filename); 16 | 17 | /* Create a label for the button */ 18 | label = new Gtk.Label(label_text); 19 | 20 | /* Pack the image and label into the box */ 21 | box.pack_start(image, false, false, 3); 22 | box.pack_start(label, false, false, 3); 23 | 24 | image.show(); 25 | label.show(); 26 | 27 | return box; 28 | } 29 | 30 | class ButtonWindow : Gtk.Window { 31 | 32 | Gtk.Button button; 33 | Gtk.Box box; 34 | 35 | /* Our usual callback function */ 36 | void callback (string data) { 37 | stdout.printf("Hello again - %s was pressed\n", data); 38 | } 39 | 40 | public ButtonWindow () { 41 | 42 | this.set_title("Pixmap'd Buttons!"); 43 | 44 | /* It's a good idea to do this for all windows. */ 45 | this.destroy.connect( ()=> { Gtk.main_quit(); } ); 46 | 47 | this.delete_event.connect( ()=> { return false; } ); 48 | 49 | /* Sets the border width of the window. */ 50 | this.set_border_width(10); 51 | 52 | /* Create a new button. */ 53 | this.button = new Gtk.Button(); 54 | 55 | /* Connect the "clicked" signal of the button to our callback. */ 56 | this.button.clicked.connect( ()=> { this.callback("cool button"); }); 57 | 58 | /* This calls our box creating function. */ 59 | this.box = xpm_label_box("img.png", "cool button"); 60 | 61 | /* Pack and show all our widgets. */ 62 | this.box.show(); 63 | 64 | this.button.add(box); 65 | 66 | this.button.show(); 67 | 68 | this.add(button); 69 | 70 | } 71 | 72 | public static int main (string[] args) { 73 | 74 | Gtk.init(ref args); 75 | 76 | var buttonwindow = new ButtonWindow(); 77 | buttonwindow.show(); 78 | 79 | Gtk.main(); 80 | 81 | return 0; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /content/chapter-07-button-widgets/code/02ToggleButton.vala: -------------------------------------------------------------------------------- 1 | 2 | public class Application : Gtk.Window { 3 | 4 | private void toggled (Gtk.ToggleButton button) { 5 | stdout.printf("%s: %s\n", button.label, button.active ? "true" : "false"); 6 | } 7 | 8 | public Application () { 9 | 10 | // Set Window Attributes 11 | this.title = "Toggle Buttons"; 12 | this.window_position = Gtk.WindowPosition.CENTER; 13 | this.destroy.connect(Gtk.main_quit); 14 | this.set_default_size(350, 70); 15 | this.set_border_width(10); 16 | 17 | // Create a VBox to pack the radio buttons in. 18 | Gtk.Box box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); 19 | this.add (box); 20 | 21 | // The buttons: 22 | Gtk.ToggleButton button1 = new Gtk.ToggleButton.with_label("Button 1"); 23 | box.pack_start (button1, false, false, 0); 24 | button1.toggled.connect(toggled); 25 | 26 | Gtk.ToggleButton button2 = new Gtk.ToggleButton.with_label("Button 2"); 27 | box.pack_start (button2, false, false, 0); 28 | button2.toggled.connect(toggled); 29 | 30 | } 31 | 32 | public static int main (string[] args) { 33 | Gtk.init(ref args); 34 | 35 | Application app = new Application(); 36 | app.show_all(); 37 | Gtk.main(); 38 | return 0; 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /content/chapter-07-button-widgets/code/03RadioButton.vala: -------------------------------------------------------------------------------- 1 | 2 | public class Application : Gtk.Window { 3 | 4 | private void toggled (Gtk.ToggleButton button) { 5 | stdout.printf("%s\n", button.label); 6 | } 7 | 8 | public Application () { 9 | 10 | // Set Window Attributes 11 | this.title = "Radio Buttons"; 12 | this.window_position = Gtk.WindowPosition.CENTER; 13 | this.destroy.connect(Gtk.main_quit); 14 | this.set_default_size(350, 70); 15 | 16 | // Create a VBox to pack the radio buttons in. 17 | Gtk.Box box = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); 18 | this.add (box); 19 | 20 | // The buttons: 21 | Gtk.RadioButton button1 = new Gtk.RadioButton.with_label_from_widget (null, "Button 1"); 22 | box.pack_start (button1, false, false, 0); 23 | button1.toggled.connect(toggled); 24 | 25 | Gtk.RadioButton button = new Gtk.RadioButton.with_label_from_widget (button1, "Button 2"); 26 | box.pack_start (button, false, false, 0); 27 | button.toggled.connect (toggled); 28 | 29 | button = new Gtk.RadioButton.with_label_from_widget (button1, "Button 3"); 30 | box.pack_start(button, false, false, 0); 31 | button.toggled.connect(toggled); 32 | button.set_active (true); 33 | 34 | } 35 | 36 | public static int main (string[] args) { 37 | Gtk.init(ref args); 38 | 39 | Application app = new Application(); 40 | app.show_all(); 41 | Gtk.main(); 42 | return 0; 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /content/chapter-07-button-widgets/code/04LinkButton.vala: -------------------------------------------------------------------------------- 1 | 2 | public class Application : Gtk.Window { 3 | 4 | public Application () { 5 | // Prepare Gtk.Window: 6 | this.title = "My Gtk.LinkButton"; 7 | this.window_position = Gtk.WindowPosition.CENTER; 8 | this.destroy.connect (Gtk.main_quit); 9 | this.set_default_size (350, 70); 10 | 11 | // The button: 12 | Gtk.LinkButton button = new Gtk.LinkButton.with_label ("https://developer.gnome.org/gtk3/stable/index.html", "GTK+ 3 Reference Manual"); 13 | this.add (button); 14 | } 15 | 16 | public static int main (string[] args) { 17 | Gtk.init (ref args); 18 | 19 | Application app = new Application (); 20 | app.show_all (); 21 | Gtk.main (); 22 | return 0; 23 | } 24 | 25 | } -------------------------------------------------------------------------------- /content/chapter-07-button-widgets/code/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-07-button-widgets/code/img.png -------------------------------------------------------------------------------- /content/chapter-07-button-widgets/screenshots/01Button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-07-button-widgets/screenshots/01Button.png -------------------------------------------------------------------------------- /content/chapter-07-button-widgets/screenshots/02ToggleButton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-07-button-widgets/screenshots/02ToggleButton.png -------------------------------------------------------------------------------- /content/chapter-07-button-widgets/screenshots/03LinkButton.png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abenga/valagtk3tutorial/b13724cc84919327146ec24c59088696a68d9e1a/content/chapter-07-button-widgets/screenshots/03LinkButton.png.png -------------------------------------------------------------------------------- /content/chapter-08-numeric-and-text-entry/08-numeric-and-text-entry.md: -------------------------------------------------------------------------------- 1 | # Numeric and Text Data Entry 2 | 3 | 4 | ## Gtk.Entry 5 | 6 | The `Gtk.Entry` widget is a widget that allows the user to enter text. If the 7 | entered text is longer than the allocation of the widget, the widget will scroll 8 | so that the cursor position is visible. 9 | 10 | When a `Gtk.Entry` is used for passwords or other sensitive information, the 11 | characters typed may be hidden from third parties by calling 12 | `Gtk.Entry.set_visibility(false)`. 13 | 14 | `Gtk.Entry.set_invisible_char(unichar ch)` sets the character (supplied as the 15 | argument `ch`) to use in place of the actual text typed by the user after 16 | calling `Gtk.Entry.set_visibility(false)` as above. This is the character used 17 | in "password mode" to show the user how many characters have been typed. By 18 | default, GTK+ picks the best invisible char available in the current font. If 19 | you set the invisible char to `0`, then the user will get no feedback at all; 20 | there will be no text on the screen as they type. 21 | 22 | You can set the contents using the `Gtk.Entry.set_text(string text)` method, 23 | replacing the current contents with the string `text` supplied as the argument 24 | to the method. You can also read the current contents with the `Gtk.Entry.get_text()` 25 | method. 26 | 27 | The number of characters the entry can take may be limited by calling 28 | `Gtk.Entry.set_max_length(int max)`. If contents of the entry upon calling this 29 | method are longer than `max`, they will be truncated to fit. 30 | 31 | You can also set the alignment for the contents of the entry, controlling the 32 | horizontal positioning of the contents when the displayed text is shorter than 33 | the width of the entry. This is done by calling `Gtk.Entry.set_alignment(float xalign)`. 34 | The alignment parameter takes one of a range of values from `0` (left) to `1` 35 | (right). 36 | 37 | Placeholder text (text displayed in the entry when it is empty and unfocused) 38 | may be set using `Gtk.Entry.set_placeholder_text()`. This can be used to give a 39 | visual hint of the expected contents of the entry. Note that since this 40 | placeholder text gets removed when the entry receives focus, using this feature 41 | is a bit problematic if the entry is given the initial focus in a window. 42 | Sometimes this can be worked around by delaying the initial focus setting until 43 | the first key event arrives. 44 | 45 | Additionally, a `Gtk.Entry` can show icons at either side of the entry. These 46 | icons can be activatable by clicking, can be set up as drag source and can have 47 | tooltips. To add an icon, use `Gtk.Entry.set_icon_from_stock()` or one of the 48 | various other functions that set an icon from an icon name, or a pixbuf. To set 49 | a tooltip on an icon, use `Gtk.Entry.set_icon_tooltip_text()`. 50 | 51 | The following example demonstrates some of these concepts in action: 52 | 53 |
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 |
99 | Text Entry 100 |
Text Entry
101 |
102 | 103 | 104 | ## SpinButton 105 | 106 | A `Gtk.SpinButton` is an ideal way to allow the user to set the numeric value of 107 | some attribute. Rather than having to directly type a number into a `Gtk.Entry`, 108 | the `Gtk.SpinButton` allows the user to click on one of two arrows to increment 109 | or decrement the displayed value. A value can still be manually typed in, in 110 | which case it can be checked to ensure it is in a given range. The main 111 | properties of a `Gtk.SpinButton` are achieved through an associated *adjustment*. 112 | 113 | The basic constructor for a SpinButton is 114 | 115 |
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 |
182 | Spin Button 183 |
Spin Button
184 |
185 | 186 | It is not necessary to manually create the associated `Adjustment`. for the 187 | spin button. The alternative constructor 188 | 189 |
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 | --------------------------------------------------------------------------------