{"id":15721665,"url":"https://github.com/stefansalewski/gintro","last_synced_at":"2025-04-09T09:07:46.554Z","repository":{"id":43919268,"uuid":"96431457","full_name":"StefanSalewski/gintro","owner":"StefanSalewski","description":"High level GObject-Introspection based GTK3/GTK4 bindings for Nim language","archived":false,"fork":false,"pushed_at":"2023-05-19T08:16:42.000Z","size":1081,"stargazers_count":301,"open_issues_count":105,"forks_count":20,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-04-09T09:07:41.779Z","etag":null,"topics":["bindings","gobject-introspection","gtk","gtk3","gtk4","much-work","nim","nim-lang"],"latest_commit_sha":null,"homepage":"","language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/StefanSalewski.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-07-06T13:09:34.000Z","updated_at":"2025-04-07T19:04:31.000Z","dependencies_parsed_at":"2024-01-06T12:02:54.195Z","dependency_job_id":"a13a7cb1-f105-4250-857b-5003e103e4c0","html_url":"https://github.com/StefanSalewski/gintro","commit_stats":null,"previous_names":[],"tags_count":60,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StefanSalewski%2Fgintro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StefanSalewski%2Fgintro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StefanSalewski%2Fgintro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StefanSalewski%2Fgintro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/StefanSalewski","download_url":"https://codeload.github.com/StefanSalewski/gintro/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248008630,"owners_count":21032556,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bindings","gobject-introspection","gtk","gtk3","gtk4","much-work","nim","nim-lang"],"created_at":"2024-10-03T22:01:33.550Z","updated_at":"2025-04-09T09:07:46.533Z","avatar_url":"https://github.com/StefanSalewski.png","language":"Nim","funding_links":[],"categories":[],"sub_categories":[],"readme":"= High level GTK4 and GTK3 bindings for the Nim programming language\n//(c) Stefan Salewski                                     \n//Version 0.9.9\n:experimental:\n:imagesdir: http://ssalewski.de/tmp\n//:source-highlighter: pygments\n//:pygments-style: monokai\n:source-highlighter: rouge\n:rouge-style: molokai\n:icons: font\n:toc: left\n\n:GIR: GObject-Introspection\n:MAC: MacOSX\n\n//(c) Stefan Salewski +\n//2018\n\nTIP: A more fancy copy of this document with dark source code background is available at http://ssalewski.de/gintroreadme.html[GIntro README]\nThis document describes mainly the use of these bindings for GTK3. For GTK4 you may also consult the pre-print of the \n*Nim GTK4 book* located at http://ssalewski.de/gtkprogramming.html.\n\nWARNING: Please do not use the code from the examples in this page, but use the actual code from\nhttps://github.com/StefanSalewski/gintro/tree/master/examples . The github readme.adoc does not\nallow to insert code from files, so I had to manually insert it, and so that code in the html page\nmay not compile with latest gintro version. I plan to create a new page hosted somewhere else\nwith code inserted from files directly.\n\nNOTE: This work is partly based on earlier works of J. Mansour and has been supported by E. Bassi and other _GTK/Gnome_ developers.\nThe `combinatorics` module was kindly provided by R. Behrends.\n\nNOTE: As we have already reached version 0.9.9, we have stopped doing releases for now. So you should do a #head install\nwith \"nimble uninstall gintro; nimble install gintro@#head\" to get an up to date version.\n\nNOTE: This is finally version 0.9.9 of the gintro Nim GTK bindings. It contains some small fixes, that\nwe applied in the last months. Note that gintro's version numbers are unsigned integers and so wrap around, so\nwhenever there should be one more new release, that may get the tag 0.1 again.\n(More seriously, we hesitate to call next gintro version 1.0 already, because 1.0 would indicate some final state,\nwhich gintro can never archive. All the GTK related libraries are so complex, that it is nearly impossible to\ncreate perfect bindings.)\nBut as the number of serious\ngintro users is tiny, we may fully remove gintro from GitHub by end of 2022. This will save us some work\nof continous bug fixes, and users can choose one of the 20 other Nim GUI toolkits. It is indeed very questionable\nif gintro would work with Nim 2.0 or GTK 5.0 at all. And after having worked on gintro for more than 1600 hours,\nit may be a good decision to just retire now.\n\nNOTE: Version 0.9.8 of the gintro Nim GTK bindings contains some small fixes,\nincluding a fix for the latest uref/unref name change in gobject.\n\nNOTE: In version 0.9.7 of the gintro Nim GTK bindings we tried to fix an\nissue resulting from use of symbols without a module prefix in the code generated by the mconnect() macro.\nSee https://github.com/StefanSalewski/gintro/issues/188. Unfortunately this fix may break some existing projects.\nWe tried hard to make changes as tiny as possible: We tried to let the content of the generated modules unchanged,\nand changed only files gimplgob.nim and gen.nim and the generated gisup4.nim and gisup3.nim files.\nThe example programs still compile and seems to run.\n\nNOTE: In version 0.9.6 of the gintro Nim GTK bindings we tried to fix a bug related to the appearance of libsoup 3.0.\nWhen gobject-introspection was first processing libnice, it loads old libsoup 2.4 and when then processing linsoup 3.0\nname conflicts lead to error messages and the install process hung. Now we process always libsoup in version 2.4 and 3.0\nbefore libnice, this seems to work. For libsoup 2.4 we generate a module called libsoup.nim as before, and the\nlibsoup 3.0 module is now called libsoup3.nim.  \n\nNOTE: For version 0.9.5 of the gintro Nim GTK bindings we added a patch to support very old GTK3 versions\nlike Debian Buster, updated the list of \"light symbols\" like Rectangle, TextIter and such that do not\nneed Nim Proxy objects, and finally fixed a recent glib proc name conflict.\n\nNOTE: Version 0.9.4 of the gintro Nim GTK bindings contains a lot of fixes.\nBefore 0.9.4 some GtkDrawingArea  examples gave crashes with the most recent Nim compiler\ndue to a wrong cast from ref object to RootRef. That is fixed, and the drawingarea example works now\nalso without manually freeing cairo resources like Context, Surface and Pattern. We have not updated the example,\nso it still contains the manually memory management, but you can comment it out if you want. When you compile your code with\n--gc:arc you really should not have to care for releasing cairo resources. If you still use the default refc GC you may\nrelease cairo resources manually, as the GC has a delay, which may first allocate some GB before the GC becomes active and frees\nthe cairo resources. Note that only a small part of all the cairo functions has been tested yet, so there may still be bugs.\nStrings are now passed to GTK functions as cstrings, so you can pass nil as well as empty strings. The nil value has for GTK in most cases\na special meaning and is different from an empty \"\" string. For some function parameters nil is the default value for cstrings.\nFinally we support now named empty flag sets, so instead of\npassing the empty set as {} for default flag set values you can use names like BindingFlagsDefault or BindingFlags.default.\n\nNOTE: Version 0.9.3 of the gintro Nim GTK bindings contains some serious changes\nfollowing the discussion in https://discourse.gnome.org/t/get-ref-function-for-none-gobject-classes-like-gtkexpression/6696.\nWith that changes issue https://github.com/StefanSalewski/gintro/issues/135 should be fixed, and the listview_clocks example works\nwith a C and a Nim part. For most apps that changes should be invisible, but it is possible that we have introduced\nnew bugs or maybe memory leaks. At least the existing examples seems still to compile, but we have not yet tested them all.\n//You can test this version with nimble install gintro@#head.\nAnd the notify signals for gobject properties like \"notify::cursor-moved\" for entry widgets should work now.\n\nNOTE: The version 0.9.2 of the gintro Nim GTK bindings is only a fix for the issue with latest gstreamer\nfrom gitlab, see https://github.com/StefanSalewski/gintro/issues/138.\n\nNOTE: Version 0.9.1 is mostly a plain fix for issue https://github.com/StefanSalewski/gintro/issues/133.\nFor GObject proc parameters with direction in and transfer full we have to avoid that the Nim memory management destroys the GObject when the Nim proxy object is destroyed. Current fix was adding a plain \".ignoreFinalizer = true\" for that case, which works in most cases. But maybe a better fix would be to ref() the gtk object instead. Maybe that makes no difference for gobjects, but it can make a difference for entities like GtkExpression which are used further, see issue\nhttps://github.com/StefanSalewski/gintro/issues/137. But we will leave that for next version 0.9.3.\n\nNOTE: Due to a user request we added support for the adwaita library for version 0.8.9.\nThis lib is the libhandy variant for GTK4 and is intended to\nsupport GTK on mobile devices. Unfortunately we can not yet provide\nan example program for this library. The C example from git sources\nis not really tiny, so porting to Nim would take some hours at least, as we have\nabsolutely no knowledge about that lib yet. Maybe we can provide an example next\nyear or maybe that user will finally provide something? \n\nNOTE: Starting with version 0.8.8 of the gintro Nim GTK bindings\nfor procs like getStartIter() without a result but with a var out\nparameter an overloaded version is created where the var out\nparameter is returned as result. So we can write \"let startIter = buffer.getStartIter()\" now.\nAnd we tried to fix the issues with out gobject parameters as in g_file_new_tmp().\n\nNOTE: For version 0.8.7 of the gintro Nim GTK bindings\nwe did a larger internal cleanup for the gen.nim generator script but tried to\ngenerate output modules identical to v.0.8.6 still. For next release v0.8.8\nthere will be some changes in the generated modules then.\nAlso for this version 0.8.7 we do support webkit2 for GTK4.\n\nNOTE: Version 0.8.6 of the gintro Nim GTK bindings\ncontains now all the standard gst modules, and due to a recent\nrequest also the gtklayershell.nim module. Due to issues with some gst modules\nwe do now call init() for the gst module before we use it with gobject-introspection.\nWe do the same for GTK3 and GTK4 as these provide also an init() proc. According to\na recent discussion with the GTK core devs that init() call is necessary. The call is\ndone by use of dlopen(), for which we need to provide the names of the\ndynamic libraries, which is some guesswork for Windows and Mac.\n\nNOTE: For version 0.8.5 of the gintro Nim GTK bindings\nwe have added webkitgtk support.\nUnfortunately still only for GTK3, as the latest webkitgtk package 2.30.4\ndoes not compile with stable GTK4. But we should get\nwebkitgtk for GTK4 in a few weeks. One question is still how to name the GTK4\nversion then. The official name of the version for GTK3 is webkit2, so shall\nwe call the version for GTK4 webkitgtk4 or webkit4? Or different?\n\nNOTE: Since version 0.8.4 we do support the\nlibnice module for Linux and Windows. And we added gtksourceview5 to\nsupport gtksourceview usage for GTK4. The module is called gtksourceview5\nbecause the underlying C library has mayor version 5 already. So you can\nwrite your own GTK4 Nim editor now. Additional optional var out parameters\nare supported now and GtkGestures should work also.\n\nNOTE: The version gintro v0.8.3 will have some internal cleanup, which removes the temporary\nArray types. In the past we used that names to indicate array parameters, it was working well, but still\nit was ugly using names to indicates data types. So we fixed that. And we have added some better support for\nGList parameter. GLists are now converted to Nim seqs and vice versa. Not everywhere still, and this conversion\nis still untested, as GList parameters are used most of the time in exotic functions only. Another change is that\nwe have support for libnice by a request of a user from Japan now. As he intents to use libnice on Windows 8.1\nwith only glib and gobject installed, but with no gtk installed, we had to break module gimpl.nim\ninto two units called gimplgobj.nim and gimplgtk.nim, and we added a module called dummygtk.nim\nwhich can be imported to make the connect() macro available while a real gtk module is not available.\nPlease let us know if these changes should break something for ordinary gtk users.\nTo test libnice we added the sdp_example.nim converted from\na C example, it seems to compile and run. But we have not tested real communication between\ndifferent computers yet, and not tested it on windows at all. If you have a use case for libnice you may test it yourself,\npossible issues may Nim's string vs seq[uint8] vs plain ptr char, or the uint data types in C example which we replaced\nby plain int as used in Nim prefereable. Debugging should be not hard, if you do not manage it yourself then you may\nopen a github issue. Version v0.8.3 uses also some more light entities like gtk.TextIter or glib.Mutex, that are entities\nwhich are generally allocated on the stack and are only initialized to plain binary zero. For these types no\nproxy symbols are needed. Unfortunately discovering these entities by use of gobject-introspection works\nnot reliable, so we are using a manually created list, which may contain errors.\nAnd finally with v0.8.3 the examples from the GTK4 book  (http://ssalewski.de/gtkprogramming.html) should work.\nFor the GTK3 examples we had to do some minimal fixes, so you may have to do similar fixes for your own code as well.\n\nNOTE: Version 0.8.2 of gintro was only a fix for recent gstreamer 1.18 which added some uncommon\ngobject-introspection stuff which brook the install of 0.8.1 for a few users. We were not yet able to\nverify that the gst example which is bases on the gst C tutorial1 works with gstreamer 1.18.\n\nNOTE: For version 0.8.1 of gintro, requested by FedericoCeratto, we have added libhandy support.\nLibhandy is not yet available for GTK4 but seems to work with gtk+-3.24.22. As Gentoo Linux ships still\nonly a very old libhandy, we tested with latest release from https://gitlab.gnome.org/GNOME/libhandy/\nOnly available example is currently /examples/gtk3/handy.nim converted from example.py.\n\nNOTE: If you are using Arch Linux you may still have to ensure that\ncurl or wget is available, see https://github.com/StefanSalewski/gintro/issues/83.\nNext release will get support for GList proc parameters and results, but providing that is some non trivial work\nand may break some other stuff unfortunately. Note that the File type of module gio is now called GFile to\nprevent name conflicts with Nims own File type.\n\nNOTE: Version 0.7.7 contains the fixes from https://github.com/StefanSalewski/gintro/issues/75.\nMr lscrd has tested it already by use of nimble install gintro@#head some weeks ago, so we may assume that\nit works properly. Next version will again have serious modifications for GTK4, see http://ssalewski.de/gtkprogramming.html.\nSo it may be a good idea to have a version 0.7.7 available as a fallback.\n\nNOTE: Starting with version 0.7.4 we support latest GTK 3.98.3 which may become GTK 4 at the end of 2020.\n\nNOTE: Starting with version v0.7.3 we allow passing a type descriptor as first parameter to the\nnew() procs like `newButton(MyButtonSubclass)` to support subclassing and extending Widgets in OOP style.\nSee \u003c\u003cExtending or sub-classing Widgets\u003e\u003e for an complete example. The init() procs which were used\nbefore for this task are now deprecated and will be removed later. This new approach generally saves\none line of source code, allows using `let` instead of `var`, and the naming of procs is more consistent.\n\nNOTE: Starting with version v0.7.1 we have added destructor support when compiled with `--gc:arc`,\nso we have no memory leak for subclassed objects any more, see the example in \u003c\u003cExtending or sub-classing Widgets\u003e\u003e section.\nWhen compiled with default (refc) GC finalizers are used as before. And for objects marked with `nullable`\ntag in gobject-introspection we now return nil value for the proxy object when the C lib has returned NULL.\nSo according to the C API docs, you can check for nil result for the few functions which may return NULL.\nNil objects returned by GTK Builder causes now a program termination (by assert()) because in that case\nnil should indicate a programming error. \n\nNOTE: Starting with version v0.7.0 we support the new Nim memory management called ARC, see\nhttps://forum.nim-lang.org/t/5734. Just compile your programs with `--gc:arc`. The main advantage is that\nARC is deterministic, so it is easier to find bugs in the bindings or in your programs. And\nmanually freeing resources, as we did previous for some cairo data structures to free them\nwithout GC delay should be now unnecessary. Generally this version should be more stable:\nNim without option `--gc:arc` compiled new() calls silently with and without a finalizer proc parameter for\nthe same data type, but the finalizer was then always called. This behaviour was stated in the Nim manual,\nbut it was easy to forget this strange behaviour, so unintentionally finalizer calls may have ocurred.\nNim with --gc:arc detects at least some of these errors, and for gintro we now try hard to not mix these calls.\nGenerally we specify a finalizer, and use a field in the ref object to ignore the call when necessary.\nFor a type always the same finalizer has to be used (or always none) and finalizer must be defined in the\nsame module as the object type itself. For this to work reliable we have generally to qualify the\nfinalizer proc with its module prefix. All that made a larger rewrite of gen.nim generator script\nnecessary, with the danger of introducing bugs. We have not tested v0.7 much yet, the examples in\ngtk3 directory compile and seems to start at least. We would still have to check the macros in gimpl.nim more\ncarefully -- we had to replace deepcopy by a plain copy and removed a (wrong?) GC_ref(). Generally we have\nto investigate possible memory leaks. One leak is unavoidable: If we subclass Widgets, then\nfinalizer are not applied to the GTK object, so its memory leaks. See https://forum.nim-lang.org/t/5825#36241.\nBut that should be not a too serious problem, subclassed objects are generally only allocated once\nin a program and generally live as long as the program is running any way. For the next version of\ngintro we do consider using only destructors and no finalizers, see https://forum.nim-lang.org/t/5854\nand https://forum.nim-lang.org/t/5786. That may simplify the code and enable subclassed GTK objects\nto release its memory, but require rewriting gen.nim again. But then we would have to use `--gc:arc`\nalways. Maybe we can join both by specifying some conditional `when` expression -- we will see.\nIf for you installation or compiling with v0.7 should not work, then please report issues on\ngithub issue tracker and continue using v0.6.1 for now. \n\nNOTE: Starting with version v.0.6.0 we support gstreamer (gst). At the same time we have split\ncairo module into an gobject-introspection basic part and an manually created part. Unfortunately the\ngobject-introspection is not available for very old GTK/cairo libraries, so installation may fail for you.\nUse v0.5.5 in this case. Also we support gBoxed types now, this is assumed to work well but is not well tested yet.\nCommand to install older releases should be something like\n`nimble uninstall gintro` followed by `nimble install gintro@v0.5.5`\n\nNOTE: Starting with release 0.5.3 we do not generate field entries for objects and we do\nnot generate class structs and private objects. Also we stopped exporting the low level functions\nlike gtk_button_new(). For a real high-level binding we should not need these. If that is a serious\nlimitation for you, then use release 0.5.2 for now and create an github issue for your use case, we\nwill try to fix it, maybe undo these changes. Also starting with v0.5.3 we try to support array\nparameters like TargetEntryArray, PageRangeArray and KeymapKeyArray. Use of these array parameters is rare,\nif you will use functions with these parameters you may inspect the source code first, as the\ncode is auto-generated and still untested. \n\nNOTE: Starting with release 0.5.0 we also support GTK4. GTK4 is still work in progress and not intended for\nend users yet, but it is good to have it available for migration testing. For GTK4 we have a new module gsk, and\nnew versions of modules gtk, gdk and gdkX11, which are not backward compatible with the old once of GTK3. The other\nmodules can be used by GTK3 and GTK4 in parallel. Due to this fact we use a single nimble package which can be used for\nGTK3 and GTK4 development. To archive this, we have named the new modules gtk4, gdk4 and gdkX114 -- the\nold once are named gtk, gdk and gdkX11 still. So for existing GTK3 software no code changes are necessary.\nFor GTK4 an example is provided -- it imports gtk4 instead of gtk now, and instead of window.showAll()\nwindow.show() is needed. More GTK4 examples may follow eventually, see GTK4 migration page at\nhttps://developer.gnome.org/gtk4/stable/gtk-migrating-3-to-4.html. The gintro package tries to install\nthe GTK4 modules when GTK4 is available on the local computer and skips it if not available.\nFor successful detection of GTK4 the typelibs must be found. For example, if you have installed\nGTK4 from sources on /opt/gtk as described in https://developer.gnome.org/gtk4/3.96/gtk-building.html, then\nyou may have to execute \"export GI_TYPELIB_PATH=/opt/gtk/lib64/girepository-1.0\" in your shell before you\ndo \"nimble install gintro\". Currently gtksourceview and vte is not available for GTK4. GTK4 provides a\nofficial test program called gtk4-demo -- of course that one should work fine on your box before you\nconsider testing Nim with GTK4.\n\n//icon:thumbs-up[]\n//This repository contains bindings from the Nim programming language to the GTK3 _GUI_ (_Graphical User Interface_) library and related libraries. (With some fixes\n//it should also work for upcoming _GTK4_.)\n\nhttps://nim-lang.org/[Nim] is a modern universal programming language.\n\nhttps://www.gtk.org/[GTK], also known as the _Gimp Tool Kit_ and now sometimes called _Gnome Tool Kit_, is a Graphical User Interface library.\n\nNOTE: Later we will insert at this location a nice picture of a fancy Nim GTK3 GUI. Such a picture is fine to attract users and indeed is a good motivation.\nBut such pictures are no real evidence for the quality of a GUI toolkit -- the concrete example may look nice, while the toolkit\nlooks much worse in other environments and offers by far not all that what is needed in real life. \n\nWhile GTK was initially designed and advertised as cross platform GUI toolkit, it is currently mostly used on _Linux_ and other _Unix_ like operation systems.\nMost Linux distributions include it, and some use it for their default desktop environment, often with the Gnome environment or other window managers.\nWhile GTK2 applications like _GIMP_ are still used on _Windows_, there seems to exist currently only very few GTK3 applications for Windows or _{MAC}_.\nWhen you develop primary _free open source software_ (_FOSS_) for Linux or other Unix like operating systems, then GTK3 is a good choice for you. With some \neffort you should be even able to port your application to the proprietary Windows or {MAC} operating systems. But when your primary target platforms\nare Windows and {MAC} and you desire a real native look and feel there, then you may find better suited ones in the Nim software repository.\nAlso, when you only need a minimal restricted GUI which is very easy to install on Windows and {MAC}, then you may find better suited packages\nin the Nim package repository. _Android OS_ is currently not supported by GTK at all.\n\nTIP: At least for Windows 10 it seems to be not that hard to install GTK3 libraries, as was recently reported in\nhttps://github.com/StefanSalewski/gintro/issues/24 by user zetashift:\n----\n  Sketch of GTK3 install for Windows 10:\n  For the GTK libs I did according these instructions(https://www.gtk.org/download/windows.php):\n  Install MSYS2\n  In the msys2 cmd I entered:\n  pacman -S mingw-w64-x86_64-gtk3\n  Then for some other necessary depencies(girepository.dll) you need to do:\n  pacman -S mingw-w64-x86_64-python3-gobject\n\n  Additional, you have to install the separate GtkSourceView lib in a similar manner from\n  https://github.com/Alexpux/MINGW-packages/blob/master/mingw-w64-gtksourceview3/\n----\n\nWhile low level Nim bindings for GTK3 are already available since a few years, this one is an attempt to\nprovide real high level bindings with full type safety, full _Garbage Collector_ (_GC_) support and an idiomatic\n_Application Programming Interface_ (_API_).\n\nCurrently there are at least 3 sources of GTK3 bindings for Nim:\n\n* https://github.com/ngtk3 (obsolete, has been deleted)\n* https://github.com/StefanSalewski/oldgtk3\n* https://github.com/StefanSalewski/gintro\n\nngtk3 was the first attempt to provide GTK3 support for Nim. It contained single repositories for all the GTK related libraries and\nwas not supported by nimble package manager. It was created from GTK 3.20 headers and is now deprecated.\n\noldgtk3 is the port of ngtk3 to GTK 3.22 -- joining all libraries and providing nimble support. Some people may still prefer\nusing oldgtk3. As it is generated with the Nim tool c2nim directly from the C header files without much manual intervention,\nit should be complete and contain not that much bugs. Missing Garbage Collector support is generally not really a problem, as\nwidgets are generally put into containers and were automatically deleted together with its parents due to GTK's reference counting.\n\nStill there can be some demand for really high level bindings -- so this gintro repository tries to provide them.\n\nHigh level GTK3 bindings, as available for many other programming languages like _C++_, _Python_, _Ruby_ or _D_ already,\nhave these advantages:\n\n* full Garbage Collector or Destructor support -- you should never have to free resources manually\n* Widgets are Nim objects, so inheritance and sub-classing can be used\n* full type safety -- no needs for casts or other unsafe and dangerous operations\n\nThese high level bindings are based on _{GIR}_, an _XML_ based database like interface description. Compared to the _C_ header\nfiles this description gives us more and deeper information about data types and function calls, for example ownership transfer of objects and\nin or out direction of procedure variables,  which makes writing the glue code much easier.\nAnd it should work with minimal\nmodifications also for the upcoming GTK4.\n\nUnfortunately there are also some drawbacks:\n\n* The Application Programming Interface (API) will be different from what is known from _C_ API, so using _C_ examples or _C_ tutorials is not really straight forward \n* The high level source code will differ from available _C_ examples, so there would be a big demand for tutorials\n* We need a lot of glue code, which has much room for bugs. So much testing is necessary.\n* There is some overhead due to indirect calls, leading to some code size increase and minimal\nperformance loss.\n\nNOTE: The new package name is *gintro*, short for _{GIR}_. The previous name was _nim-gi_, but the hyphen is deprecated for package names, as is the\nnim prefix.\n\n== Current state of these bindings\n\nWe are still in an early stage, but it is already more than a proof of concept. GTK and related libraries have many thousand of\ncallable functions and nearly as many data types. Testing all that is nearly impossible for a small team with limited resources.\nThe initial approach was to generate low level\nbindings, which looked similar to the ones generated by the `c2nim` tool from the _C_ headers. After that was done, we have associated all\nthe _C_ structs and _GObject_ data types with Nim proxy objects. A well defined relation between these proxy object and the low level _C_ data types\nshould ensure fully automatic garbage collection. This is supported by smart type conversion, for example _C_ strings returned by `glib` library\nare assigned to newly created Nim strings, while the memory of the _C_ strings is automatically freed. For most cases this seems to work. But there\nexists a few more complicated cases, for example functions may return whole arrays of _C_ strings or other non elementary data types,\nor function arguments or results may be so called _glists_,\nlist structures of `glib` library. These cases can not be processed automatically but needs carefully manual investigations. And there may be still functions and data\ntypes missing: {GIR} query gives us many thousand lines of Nim interface code, and it is not really obvious if and what is missing.\nSome functions and data types are missing for sure -- at least some low level ones, which are considered unneeded for high level bindings by {GIR}.\nBut maybe more is missing, we have to investigate that. Until now these bindings have been tested only for 64 bit Linux systems with GTK 3.24.\n\nThese basic libraries are already partly tested:\n\nGtk, Gdk, GLib, GObject, Gio, GdkPixbuf, GtkSource, Pango, PangoCairo, PangoFT2, GModule, Rsvg, fontconfig, freetype2, xlib, Atk, Vte, cairo\n\nIn best case it should be possible to add more GObject based libraries to this list without larger modifications of the generator source code.\nUnfortunately the bindings for the _cairo_ drawing library provided by {GIR} was only a minimal stub -- we have extend it manually.\n\n== How to try it out\n\nOf course you will need a working Nim installation with a recent compiler version and you have to ensure that GTK and related libraries are installed on your system. For some Linux\ndistributions which provide mainly pre-compiled software you may have to also install some GTK related developer files. \n\nWith a recent nimble version (\u003e= v0.8.10) you only have to type in a shell window:\n\n----\nnimble install gintro\n----\n\nNOTE: Latest version of gintro package uses some files from oldgtk3 package for bootstrapping. We assume that\nusers of gintro generally are not interested in low level oldgtk3 package, so we try to download only 3 single files\nfrom oldgtk3 package. That should work if wget or nimgrab executables are available. If it fails you should\nget a longer error message which may help you to solve the issue.\n\nNOTE: Nimble prepare should run for about 20 seconds, it compiles and executes the generator program `gen.nim`.\nUnfortunately we can not guarantee that the generator command  will be able to really build all the\ndesired modules. The built process highly depends on your OS and installed GTK version. For 64 bit Linux systems\nwith GTK 3.24 and all required dependencies installed it should work. For never GTK versions it may fail, when that GTK\nrelease introduces for example new unknown data types like array containers. In that case manual fixes may be necessary.\nThe {GIR} based built process generates bindings customized to the OS where the generator is executed,\nso for older GTK releases or a 32 bit system different files are created. Later we may also provide pre-generated\nfiles for various OS and GTK versions, but building locally is preferred when possible. \n\n== A few basic examples\n\nNOTE: Currently we do not install the example programs. If you want to try them, you have to copy the source code of the\nexamples from https://github.com/StefanSalewski/gintro/tree/master/examples to your local computer, maybe to /tmp/gintro/examples directory.\n\nThen you can compile and run them from shell with commands like\n\n----\ncd /tmp/gintro/examples/\nnim c app0.nim\n./app0\n----\n\nor you may open the source files in your favorite Nim IDE or editor. [black yellow-background]#Taking the source code from this Readme file is not\nreally recommended, as these source code listings may be not the latest versions.#\n\nGTK3 programs can use still the old _GTK2_ design, where you first initialize the GTK library, create your widgets and finally enter the GTK main loop.\nThis style is still used in many tutorials as in http://zetcode.com/gui/gtk2/[Zetcode tutorial] or in the GTK book of A. Krause.\nOr you can use the new _GTK3 App style_, this is generally recommended by newer original GTK documentation.\nUnfortunately the GTK3 original documentation is mostly restricted to the GTK3 API documentation, which is generally very good, but makes\nit not really easy for beginners to start with GTK. API docs and some basic introduction is available here:\n\n* https://www.gnome.org/\n* https://www.gtk.org/\n* https://developer.gnome.org/\n* https://developer.gnome.org/gtk3/stable/\n* https://developer.gnome.org/gtk3/stable/ch01s04.html#id-1.2.3.12.5\n* https://developer.gnome.org/gnome-devel-demos/stable/c.html.en\n\nTIP: If you should decide to continue developing software with GTK, then you may consider installing the so called\n`devhelp` tool. It gives you easy and fast access to the GTK API docs. For example, if you want to use a _Button Widget_ in your\nGUI and wants to learn more about related functions and signals, you just enter _Button_ in that tool and are guided to\nall the relevant information. \n\nWe start with a minimal traditional old style example, which should be familiar to most of us:\n\n[[t0.nim]]\n[source,nim]\n.t0.nim\n----\n# nim c t0.nim\nimport gintro/[gtk, gobject]\n\nproc bye(w: Window) =\n  mainQuit()\n  echo \"Bye...\"\n\nproc main =\n  gtk.init()\n  let window = newWindow()\n  window.title = \"First Test\"\n  window.connect(\"destroy\", bye)\n  window.showAll\n  gtk.main()\n\nmain()\n----\n\nThis is the traditional layout of GTK2 programs. When using this style then it is important to initialize the GTK library by calling `gtk.init()`\nat the very beginning. Then we create the desired widgets, connect signals, show all widgets and finally enter the GTK main loop\nby calling `gtk.main`. About connecting signals we will learn more soon, for now it is only important that we have to connect to\nthe destroy signal here to enable the user to terminate program execution by clicking the window close button. \n\nNow a really minimal but complete App style example, which displays an empty window.\n\nNOTE: The source text of all these examples is contained in the examples directory. Unfortunately _github_\nseems to not allow to include that sources directly into this document, so there may be minimal\ndifferences between the source code displayed here and the sources in examples directory.\n\n[[app0.nim]]\n[source,nim]\n.app0.nim\n----\n# app0.nim -- minimal application style example\n# nim c app0.nim\nimport gintro/[gtk, glib, gobject, gio]\n\nproc appActivate(app: Application) =\n  let window = newApplicationWindow(app)\n  window.title = \"GTK3 \u0026 Nim\"\n  window.defaultSize = (200, 200)\n  showAll(window)\n\nproc main =\n  let app = newApplication(\"org.gtk.example\")\n  connect(app, \"activate\", appActivate)\n  discard run(app)\n\nmain()\n----\n\nIn the `main proc` we create a new application and connect the activate signal to our `activate proc`, which then creates and displays\nthe still empty window.\n\nNOTE: We are importing modules gtk and gio. Initially both modules had a data type called `Application` (`gtk.Application`\nextends indeed the `gio.Application`), so we would have to use module name prefixes, or we could import from gio only\nwhat is really needed (`from gio import ...`) or use the form (`import gio exept ...`). But as gio.Application is generally\nnot needed often, we have no renamed gio.Application to GApplication. No more name clashes.\n\nVarious ways to set widget parameters are supported -- the number 1 to 6 refer to the comments below:\n\n//. Setting widget parameters\n[source,nim]\n----\nsetDefaultSize(window, 200, 200) # \u003c1\u003e\ngtk.setDefaultSize(window, 200, 200) # \u003c2\u003e\nwindow.setDefaultSize(200, 200) # \u003c3\u003e\nwindow.setDefaultSize(width = 200, height = 200) # \u003c4\u003e\nwindow.defaultSize = (200, 200) # \u003c5\u003e\nwindow.defaultSize = (width: 200, height: 200) # \u003c6\u003e\n----\n\n\u003c1\u003e proc call syntax\n\u003c2\u003e optional qualified with module name prefix\n\u003c3\u003e method call syntax\n\u003c4\u003e named parameters\n\u003c5\u003e tupel assignment\n\u003c6\u003e tupel assignment with named members\n\nWell, that empty window is really not very interesting. The GTK and Gnome team provides some GTK examples\nat https://developer.gnome.org/gnome-devel-demos/.\nThe https://developer.gnome.org/gnome-devel-demos/3.22/c.html.en[C demos] seems to be most actual and complete,\nand are easy to port to Nim. So we start with these,\nbut if you are familiar with the other listed languages, then you can try to port them to Nim as well.\nLet us start with https://developer.gnome.org/gnome-devel-demos/3.22/button.c.html.en as it is\nstill short and easy to understand, but shows already some interesting topics.\n\nimage::NimGTK3Button.png[]\n\nThe _C_ code looks like this:\n\n[[button.c]]\n[source,c]\n.button.c\n----\n#include \u003cgtk/gtk.h\u003e\n\n/*This is the callback function. It is a handler function which \nreacts to the signal. In this case, it will cause the button label's \nstring to reverse.*/\nstatic void\nbutton_clicked (GtkButton *button,\n                gpointer   user_data)\n{\n  const char *old_label;\n  char *new_label;\n\n  old_label = gtk_button_get_label (button);\n  new_label = g_utf8_strreverse (old_label, -1);\n\n  gtk_button_set_label (button, new_label);\n  g_free (new_label);\n}\n\nstatic void\nactivate (GtkApplication *app,\n          gpointer        user_data)\n{\n  GtkWidget *window;\n  GtkWidget *button;\n\n  /*Create a window with a title and a default size*/\n  window = gtk_application_window_new (app);\n  gtk_window_set_title (GTK_WINDOW (window), \"GNOME Button\");\n  gtk_window_set_default_size (GTK_WINDOW (window), 250, 50);\n\n  /*Create a button with a label, and add it to the window*/\n  button = gtk_button_new_with_label (\"Click Me\");\n  gtk_container_add (GTK_CONTAINER (window), button);\n\n  /*Connecting the clicked signal to the callback function*/\n  g_signal_connect (GTK_BUTTON (button),\n                    \"clicked\", \n                    G_CALLBACK (button_clicked), \n                    G_OBJECT (window));\n\n  gtk_widget_show_all (window);\n}\n\nint\nmain (int argc, char **argv)\n{\n  GtkApplication *app;\n  int status;\n\n  app = gtk_application_new (\"org.gtk.example\", G_APPLICATION_FLAGS_NONE);\n  g_signal_connect (app, \"activate\", G_CALLBACK (activate), NULL);\n  status = g_application_run (G_APPLICATION (app), argc, argv);\n  g_object_unref (app);\n\n  return status;\n}\n\n----\n\nConverting it to Nim is straight forward with some basic _C_ and Nim knowledge, and Nim does not force us\nto convert its shape into all the classes known from pure _Object Orientated_ (_OO_) languages. We can either use the\nNim tool `c2nim` to help us with the conversion, or do it manually. Indeed `c2nim` can be very helpful by\nconverting _C_ sources to Nim. Most of the time it works well. Personally I generally pre-process _C_ files, for example\nby removing too strange `macros` and `defines, or by replacing strange constructs, like _C_ `for loops`, to simpler\nones like `while loops`. Then I apply `c2nim` to the _C_ file and finally manually compare the result line by line and\nfine tune the Nim code. But for this short source text we may do all that manually and finally get something like\nthis:\n\n[[button.nim]]\n[source,nim]\n.button.nim\n----\n# nim c button.nim\nimport gintro/[gtk, glib, gobject, gio]\n\nproc buttonClicked (button: Button) =\n  button.label = utf8Strreverse(button.label, -1)\n\nproc appActivate (app: Application) =\n  let window = newApplicationWindow(app)\n  window.title = \"GNOME Button\"\n  window.defaultSize = (250, 50)\n  let button = newButton(\"Click Me\")\n  window.add(button)\n  button.connect(\"clicked\",  buttonClicked)\n  window.showAll\n\nproc main =\n  let app = newApplication(\"org.gtk.example\")\n  connect(app, \"activate\", appActivate)\n  discard app.run\n\nmain()\n----\n\nAgain we have the basic shape already known from \u003c\u003capp0.nim\u003e\u003e example: `Main proc` creates the application, connect\nto the activate signal and finally runs the application. When GTK launches the application and emits the `activate` signal, then\nour activate proc is called, which creates a main window containing a button widget. That button is again connected with a\nsignal, in this case named `clicked`. That signal is emitted by GTK whenever that button is clicked with the mouse and results\nin a call of our provided `buttonClicked()` proc. The procs connected to signals are called _callbacks_ and generally got the widget\non which the signal was emitted as first parameter. They can also get a second optional parameter of arbitrary type -- we will\nsee that in a later example. This callback here gets only the button itself as parameter, and it's task is to reverse the\ntext displayed by the button. Not very interesting basically, but we are indeed using the _glib_ function `utf8Strreverse()`\nfor this task. While that function internally works with `cstrings`, and in _C_ we have to free the memory of the returned `cstring`,\nin our Nim example that is done automatically by Nim's Garbage Collector. When you compare our example carefully with the _C_ code,\nthen you may notice a difference. The _C_ code passes the window containing the button as an additional parameter to the\ncallback function, but that parameter is not really used. We simple ignore it here, as it is not used at all.\nIn one of the following examples you will learn how passing (nearly) arbitrary parameters in a type safe way is done.  \nAnother difference is, that  the _C_ code returns an `integer` status value returned by `g_application_run()` to the _OS_. We\ncould do the same by using the `quit() proc` of Nim's _OS_ module, but as that would give us no additional benefit, we simply ignore it.\n\nTIP: The command `nim c sourcetext.nim` generates an executable which contains code for runtime checks and debugging,\nwhich increases executable size and decreases performance.\nAfter you have tested your software carefully, you may give the additional parameter `-d:release` to avoid this. For the `gcc` backend\nyou may additional enable _Link Time Optimization_ (_LTO_), which reduces executable size further. To enable LTO you may put\na `nim.cfg` file in your sources directory with content like\n\n----\npath:\"$projectdir\"\nnimcache:\"/tmp/$projectdir\"\ngcc.options.speed = \"-march=native -O3 -flto -fstrict-aliasing\"\n----   \n\nWith that optimization, your executable sizes should be in the range of about 50 kB only!\n\n== Optional, type safe parameters for callbacks\n\nThe next example shows, how we can pass (nearly) arbitrary parameters to our connect procs.\nWe pass a string, an object from the stack, a reference to an object allocated on the heap\nand finally a widget (in this case the application window itself, you may also try passing\nanother button). As the main window itself is a so called GTK `bin` and can contain only one\nsingle child widget, we create a container widget, a vertical box in this case, fill that box with\nsome buttons, and add that box to the window.\n\nCompile and start this example from the command line and watch what\nhappens when you click on the buttons.\n\n[[connect_args.nim]]\n[source,nim]\n.connect_args.nim\n----\n# nim c connect_args.nim\nimport gintro/[gtk, glib, gobject, gio]\n\ntype\n  O = object\n    i: int\n\nproc b1Callback(button: Button; str: string) =\n  echo str\n\nproc b2Callback(button: Button; o: O) =\n  echo \"Value of field i in object o = \", o.i\n\nproc b3Callback(button: Button; r: ref O) =\n  echo \"Value of field i in ref to object O = \", r.i\n\nproc b4Callback(button: Button; w: ApplicationWindow) =\n  if w.title == \"Nim with GTK3\":\n    w.title = \"GTK3 with Nim\"\n  else:\n    w.title = \"Nim with GTK3\"\n\nproc appActivate (app: Application) =\n  var o: O\n  var r: ref O\n  new r\n  o.i = 1234567\n  r.i = 7654321\n  let window = newApplicationWindow(app)\n  let box = newBox(Orientation.vertical, 0)\n  window.title = \"Parameters for callbacks\"\n  let b1 = newButton(\"Nim with GTK3\")\n  let b2 = newButton(\"Passing an object from stack\")\n  let b3 = newButton(\"Passing an object from heap\")\n  let b4 = newButton(\"Passing a Widget\")\n  b1.connect(\"clicked\",  b1Callback, \"is much fun.\")\n  b2.connect(\"clicked\",  b2Callback, o)\n  b3.connect(\"clicked\",  b3Callback, r)\n  b4.connect(\"clicked\",  b4Callback, window)\n  box.add(b1)\n  box.add(b2)\n  box.add(b3)\n  box.add(b4)\n  window.add(box)\n  window.showAll\n\nproc main =\n  let app = newApplication(\"org.gtk.example\")\n  connect(app, \"activate\", appActivate)\n  discard app.run\n\nmain()\n----\n\nTo prove type safety, we may modify one of the callback procs and watch the compiler output:\n\n[source,nim]\n----\nproc b1Callback(button: Button; str: int) =\n  discard # echo str\n----\n\n----\nconnect_args.nim(37, 5) template/generic instantiation from here\ngtk.nim(-15021, 10) Error: type mismatch: got (ref Button:ObjectType, string)\nbut expected one of: \nproc b1Callback(button: Button; str: int)\n----\n\nIt may be not always really obvious what the compiler wants to tell us, but at least we\nare told that it got a string and expected an int.\n\nCurrently the connect function is realized by a Nim type safe `macro`. Connect accepts two or three\narguments -- the widget, the signal name and the optional argument. When the optional argument\nis a ref (reference to objects on the heap) then it is passed as a reference, otherwise a deep copy\nof the argument is passed. For the above code this means, that `r` and the `window` variables are passed\nas references, while the string and the stack object are deep copied. Currently it is not possible\nto release the memory of passed arguments again. This should be no real problem, as in most\ncases no arguments are passed at all, and when arguments are passed, then they are general\nsmall in size like plain numbers or strings, or maybe references to widgets which could not be freed\nat all, as they are part of the GUI. Later we may add more variants of that connect macro.\n\nNOTE: Navigation can be hard for beginners. You may have basic knowledge of GTK and want\nto build a GUI for your application. But how to find what you need. Well, we offer no separate \nautomatically generated API documentation currently, as that is not really helpful. In most cases\nit is easy to just guess Nim symbol names, proc parameters and all that. Using a smart editor\nwith good `nimsuggest` support further supports navigation -- for example `NEd` shows us\nall the needed proc parameters when we move the cursor on a proc name, or we press  kbd:[Ctrl+W] and jump\nto the definition of that symbol. For unknown stuff the original _C_ function name is often a good starting point.\nAssume you don't know much about GTK's buttons, but you know that you want to have a button in \nyour GUI application. GTK generally offers generator functions containing the string `new` in their name.\nSo it is easy to guess that there exists a _C_ function named `gtk_button_new`. That name is also\ncontained in the bindings files, in this case in `gtk.nim`. So we open that file in a text editor and search for\nthat term. So it is really easy to find first starting points for related procs and data types. Most data types\nare located near by their related functions, so you should be able to find all relevant information fast.\nRemember the GTK `devhelp` tool, and use also `grep` or the `nimgrep` variant.\n\n== Extending or sub-classing Widgets\n\nIt may occur that we want to attach additional information to GTK widgets\nby extending or subclassing them. [line-through]#Doing this is supported\nby providing for each widget class not only a corresponding new() proc which returns \nthe newly created widget, but also\na init() proc, which gets an uninitialized variable of the (extended) widget type as argument and\ninitializes that variable with a newly created\nGTK widget.#\nDoing this is supported\nby providing for each widget class an additional new() proc which takes an type descriptor\nas first argument, like `newButton(CountButton, \"Counting down from 100 by 5\")` in the example below.\nInitializing the added fields is\ndone separately by the user. The following code shows a GTK button, which is\nextended with a counter member field. That counter is decreased for\neach button click. The amount of decrease (5) is passed to the callback as a int parameter.\n\n[black yellow-background]#Recent tests proved that providing custom destructors is\nnot really needed any more, see https://forum.nim-lang.org/t/7360#46632.#\n\nSince gintro version 0.7.1 we support destructors when compile option `--gc:arc` is used.\n[line-through]#To destroy subclassed widgets we have to create a `=destroy()` proc as shown in the code below.#\nThis may look a bit verbose, and it is only necessary to avoid memory leaks for widgets\nwhich are created and destroyed multiple times during program execution. Most widgets are\ncreated at startup and live until program terminates, so there is no noticeable leak even\nwithout a matching destroy. (In `examples/gtk3` there is a extended file called `subclassArcDestructorTest.nim`\nto test the destructor behaviour.)\n\n[[count_button.nim]]\n[source,nim]\n.count_button.nim\n----\n# nim c count_button.nim\nimport gintro/[gtk, gobject, gio]\n\ntype\n  CountButton = ref object of Button\n    counter: int\n\nwhen defined(gcDestructors):\n  proc `=destroy`(x: var typeof(CountButton()[])) =\n    gtk.`=destroy`(typeof(Button()[])(x))\n\nproc buttonClicked (button: CountButton; decrement: int) =\n  dec(button.counter, decrement)\n  button.label = \"Counter: \" \u0026 $button.counter\n  echo \"Counter is now: \", button.counter\n\nproc appActivate (app: Application) =\n  #var button: CountButton\n  let window = newApplicationWindow(app)\n  window.title = \"Count Button\"\n  #initButton(button, \"Counting down from 100 by 5\") # deprecated\n  let button = newButton(CountButton, \"Counting down from 100 by 5\")\n  button.counter = 100\n  window.add(button)\n  button.connect(\"clicked\", buttonClicked, 5)\n  window.showAll\n\nproc main =\n  let app = newApplication(\"org.gtk.example\")\n  connect(app, \"activate\", appActivate)\n  discard app.run\n\nmain()\n----\n\nIn this example we have to define our new widget type first, then we have to\ndeclare a variable of that type and pass that variable to the init() proc.\n\n== CSS styles, GErrors and Exceptions\n\nimage::NimGTK3Label.png[]\n\nOften GTK beginners ask how one can apply custom styles to GTK widgets, for example custom colors.\nWhile in most cases the use of custom colors gives just ugly results, as the custom colors generally do\nnot match well with the default color scheme, it is good to know how we can do it. For GTK3 styles are\napplied to widgets by using _Cascading Style Sheets_ (_CSS_). You may find C example code similar to this:\n\n[[label.c]]\n[source,c]\n.label.c\n----\n// https://stackoverflow.com/questions/30791670/how-to-style-a-gtklabel-with-css\n// gcc `pkg-config gtk+-3.0 --cflags` test.c -o test `pkg-config --libs gtk+-3.0`\n#include \u003cgtk/gtk.h\u003e\nint main(int argc, char *argv[]) {\n    gtk_init(\u0026argc, \u0026argv);\n    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);\n    GtkWidget *label = gtk_label_new(\"Label\");\n    GtkCssProvider *cssProvider = gtk_css_provider_new();\n    char *data = \"label {color: green;}\";\n    gtk_css_provider_load_from_data(cssProvider, data, -1, NULL);\n    gtk_style_context_add_provider(gtk_widget_get_style_context(window),\n                                   GTK_STYLE_PROVIDER(cssProvider),\n                                   GTK_STYLE_PROVIDER_PRIORITY_USER);\n    g_signal_connect(window, \"destroy\", G_CALLBACK(gtk_main_quit), NULL);\n    gtk_container_add(GTK_CONTAINER(window), label);\n    gtk_widget_show_all(window);\n    gtk_main();\n}\n----\n\nConverting that to Nim is again straight forward:\n\n[[label.nim]]\n[source,nim]\n.label.nim\n----\n# nim c label.nim\nimport gintro/[gtk, glib, gobject, gio]\n\nproc appActivate(app: Application) =\n  let window = newApplicationWindow(app)\n  let label = newLabel(\"Yellow text on green background\")\n  let cssProvider = newCssProvider()\n  let data = \"label {color: yellow; background: green;}\"\n  #discard cssProvider.loadFromPath(\"doesnotexist\")\n  discard cssProvider.loadFromData(data)\n  let styleContext = label.getStyleContext\n  assert styleContext != nil\n  addProvider(styleContext, cssProvider, STYLE_PROVIDER_PRIORITY_USER)\n  window.add(label)\n  showAll(window)\n\nproc main =\n  let app = newApplication(\"org.gtk.example\")\n  connect(app, \"activate\", appActivate)\n  discard run(app)\n\nmain()\n----\n\nFor this example we create a plain label widget with some text. To colorize it, we generate a\nCssProvider and load it with a textual description of our desired colors. Then we extract the\nstyle context from the label and add our CssProvider to it.\n\nThe last parameter of the _C_ function gtk_css_provider_load_from_data() is of type GError and can\nbe used in _C_ code to detect runtime errors. The _C_ code above just passes NULL to ignore this error.\nFor Nim we map that GError argument to _exceptions_. To test what happens in Nim when an GError would\nreport an error condition, you may uncomment  function loadFromPath() in the code above. As the specified path\ndoes not exist, we should get an exception with a message telling us the problem. Of course in your real\ncode you may catch such exceptions with Nim's `try:` blocks. (You may also modify the data variable above to\nan illegal CSS statement -- if the statement is seriously wrong, then you should get an exception from\nloadFromData().\n\n\n== SpinButton\n\nThis widget is used for entering numerical values. We can type in the value with the keyboard, click on the\n+/- symbols or use the scroll wheel of the mouse. This example also shows that we can use vertical or horizontal\norientation for this widget, and how we can use bindProperty() to bind a property of one widget to another widget.\nHere we use a button to control wrapping behaviour of the spin buttons.\n\n[[spinbutton.nim]]\n[source,nim]\n.spinbutton.nim\n----\n##  https://github.com/GNOME/gtk/blob/gtk-3-24/tests/testspinbutton.c\n##  gcc `pkg-config gtk+-3.0 --cflags` spinbutton.c -o spinbutton `pkg-config --libs gtk+-3.0`\n\nimport gintro/[gtk, gdk, glib, gobject]\n\nvar numWindows: int\n\nproc onDeleteEvent(w: gtk.Window; event: gdk.Event): bool =\n  dec(numWindows)\n  if numWindows == 0:\n    gtk.mainQuit()\n  return EVENT_PROPAGATE # false\n\nproc prepareWindowForOrientation(orientation: gtk.Orientation) =\n  let window = newWindow()\n  discard connect(window, \"delete_event\", onDeleteEvent)\n  let mainbox = gtk.newBox(if orientation == gtk.Orientation.horizontal: Orientation.vertical else: Orientation.horizontal, 2)\n  window.add(mainbox)\n  let wrapButton = newToggleButtonWithLabel(\"Wrap\")\n  mainbox.add(wrapButton)\n  var max = 0\n  while max \u003c= 999999999:\n    let adj = newAdjustment(max.float, 1, max.float, 1, (max.float + 1) * 0.1, 0)\n    let spin = newSpinButton(adj, 1, 0)\n    spin.setOrientation(orientation)\n    spin.setHalign(gtk.Align.center)\n    discard bindProperty(wrapButton, \"active\", spin, \"wrap\", {BindingFlag.syncCreate})\n    let hbox = newBox(gtk.Orientation.horizontal, 2)\n    hbox.packStart(spin, false, false, 2)\n    mainbox.add(hbox)\n    max = max * 10 + 9\n  window.showAll()\n  inc(numWindows)\n\nproc main =\n  gtk.init()\n  prepareWindowForOrientation(gtk.Orientation.horizontal)\n  prepareWindowForOrientation(gtk.Orientation.vertical)\n  gtk.main()\n\nmain()\n----\n\n== GTK Builder -- user interfaces created with the glade tool \n\nAs C code can be very verbose, some people prefer outsourcing the GUI layout\nin XML files which can be created and modified with the glade GUI creator program.\nFor high level languages like Python or Nim the program source code is generally\nshort and clean, so that use of XML files may not have much benefit. But of course\nwe can use GTK builder from Nim. We follow the example from\nhttps://developer.gnome.org/gtk3/stable/ch01s03.html\nbut we modify it to use the new GTK3 app style: For the XML file we have to change only\nclass=\"GtkWindow\" into class=\"GtkApplicationWindow\". Our Nim program has\nthe well known application shape, with one addition: We have to\nexplicitly set the application for the main window. Of course you can also\nuse the traditional program structure with Nim and Builder, for that case\nyou can straight follow the linked page or other examples. Here is the XML file and the Nim code:\n\n[[builder.ui]]\n[source, xml]\n.builder.ui\n----\n\u003cinterface\u003e\n  \u003cobject id=\"window\" class=\"GtkApplicationWindow\"\u003e\n    \u003cproperty name=\"visible\"\u003eTrue\u003c/property\u003e\n    \u003cproperty name=\"title\"\u003eGrid\u003c/property\u003e\n    \u003cproperty name=\"border-width\"\u003e10\u003c/property\u003e\n    \u003cchild\u003e\n      \u003cobject id=\"grid\" class=\"GtkGrid\"\u003e\n        \u003cproperty name=\"visible\"\u003eTrue\u003c/property\u003e\n        \u003cchild\u003e\n          \u003cobject id=\"button1\" class=\"GtkButton\"\u003e\n            \u003cproperty name=\"visible\"\u003eTrue\u003c/property\u003e\n            \u003cproperty name=\"label\"\u003eButton 1\u003c/property\u003e\n          \u003c/object\u003e\n          \u003cpacking\u003e\n            \u003cproperty name=\"left-attach\"\u003e0\u003c/property\u003e\n            \u003cproperty name=\"top-attach\"\u003e0\u003c/property\u003e\n          \u003c/packing\u003e\n        \u003c/child\u003e\n        \u003cchild\u003e\n          \u003cobject id=\"button2\" class=\"GtkButton\"\u003e\n            \u003cproperty name=\"visible\"\u003eTrue\u003c/property\u003e\n            \u003cproperty name=\"label\"\u003eButton 2\u003c/property\u003e\n          \u003c/object\u003e\n          \u003cpacking\u003e\n            \u003cproperty name=\"left-attach\"\u003e1\u003c/property\u003e\n            \u003cproperty name=\"top-attach\"\u003e0\u003c/property\u003e\n          \u003c/packing\u003e\n        \u003c/child\u003e\n        \u003cchild\u003e\n          \u003cobject id=\"quit\" class=\"GtkButton\"\u003e\n            \u003cproperty name=\"visible\"\u003eTrue\u003c/property\u003e\n            \u003cproperty name=\"label\"\u003eQuit\u003c/property\u003e\n          \u003c/object\u003e\n          \u003cpacking\u003e\n            \u003cproperty name=\"left-attach\"\u003e0\u003c/property\u003e\n            \u003cproperty name=\"top-attach\"\u003e1\u003c/property\u003e\n            \u003cproperty name=\"width\"\u003e2\u003c/property\u003e\n          \u003c/packing\u003e\n        \u003c/child\u003e\n      \u003c/object\u003e\n      \u003cpacking\u003e\n      \u003c/packing\u003e\n    \u003c/child\u003e\n  \u003c/object\u003e\n\u003c/interface\u003e\n----\n\n\n[[builder.nim]]\n[source, nim]\n.builder.nim\n----\n https://developer.gnome.org/gtk3/stable/ch01s03.html\n# builder.nim -- application style example using builder/glade xml file for user interface\n# nim c builder.nim\nimport gintro/[gtk, glib, gobject, gio]\n\nproc hello(b: Button; msg: string) =\n  echo \"Hello\", msg\n\nproc quitApp(b: Button; app: Application) =\n  echo \"Bye\"\n  quit(app)\n\nproc appActivate(app: Application) =\n  let builder = newBuilder()\n  discard builder.addFromFile(\"builder.ui\")\n  let window = builder.getApplicationWindow(\"window\")\n  window.setApplication(app)\n  var button = builder.getButton(\"button1\")\n  button.connect(\"clicked\", hello, \"\")\n  button = builder.getButton(\"button2\")\n  button.connect(\"clicked\", hello, \" again...\")\n  button = builder.getButton(\"quit\")\n  button.connect(\"clicked\", quitApp, app)\n  #showAll(window)\n\nproc main =\n  let app = newApplication(\"org.gtk.example\")\n  connect(app, \"activate\", appActivate)\n  discard run(app)\n\nmain()\n----\n\nFor each builder component gintro provides a typesafe access proc like\ngetApplicationWindow() and getButton() in this example.\n\nGenerally it is possible to use resource files merged with the executable program\ninstead of an external XML files, we have to investigate how we can do that in Nim.\nAnd it may be possible to connect the signal handlers to handler procs from within\nthe XML file -- this is also work in progress...\n\n== GAction\n\nGAction represents a single named action and is for GTK3 the prefered way to do\nuser interactions. GAction works with button, menus and keyboard shortcuts.\n\nThe following example is based on\n\nhttps://wiki.gnome.org/HowDoI/GAction\n\n[[gaction.nim]]\n[source, nim]\n.gaction.nim\n----\n# https://wiki.gnome.org/HowDoI/GAction\n# nim c gaction.nim\nimport gintro/[gtk, glib, gobject, gio]\n\nproc saveCb(action: SimpleAction; v: Variant) =\n  echo \"saveCb\"\n\nproc appActivate(app: Application) =\n  let window = newApplicationWindow(app)\n  let action = newSimpleAction(\"save\")\n  discard action.connect(\"activate\", saveCB)\n  window.actionMap.addAction(action)\n  let button = newButton()\n  button.label = \"Save\"\n  window.add(button)\n  button.setActionName(\"win.save\")\n  setAccelsForAction(app, \"win.save\", \"\u003cControl\u003e\u003cShift\u003eS\")\n  showAll(window)\n\nproc main =\n  let app = newApplication(\"org.gtk.example\")\n  connect(app, \"activate\", appActivate)\n  discard run(app)\n\nmain()\n----\n\nGtkApplicationWindow provides an interface to GActionMap. As\nthe interface itself and the interface provider are defined in different modules,\nautomatic conversion is not possible, so we have to convert the ApplicationWindow\nto ActionMap. (We could use a converter to do the conversion for us, but as\nthese conversions are rare, and because gintro use no converters at all still, we use\nan explicit proc.) The use of cstringArray as third parameter for proc setAccelsForAction()\nis a bit ugly, we have to fix that later.\n\n\n\n== GMenu with GActions\n\nThe following example shows how we can define GActions and bind them to Menus, Buttons\nand Keyboard shortcuts. Examples for stateless actions (quit), for toggle actions (spellcheck)\nand for statefull actions (text justify) are provided.\n\nNote that the following code is not a direct translation of an existing example, but\na collections of informations from various sources, so\nit may contain bugs or not fully optimal code.\n\n[[menubar.nim]]\n[source, nim]\n.menubar.nim\n----\n# https://developer.gnome.org/glib/stable/glib-GVariant.html\n# https://developer.gnome.org/glib/stable/glib-GVariantType.html\n# https://wiki.gnome.org/HowDoI/GMenu\n# https://wiki.gnome.org/HowDoI/GAction\n# nim c menubar.nim\nimport gintro/[gtk, glib, gobject, gio]\nfrom strutils import `%`, format\n\n# https://github.com/GNOME/glib/blob/master/gio/tests/gapplication-example-actions.c\nproc activateToggleAction(action: SimpleAction; parameter: Variant; app: Application) =  \n  app.hold # hold/release taken over from C example, there may be reasons...\n  block:\n    echo format(\"action $1 activated\", action.name)\n    let state: Variant = action.state\n    let b = state.getBoolean\n    action.state = newVariantBoolean(not b)  \n    echo format(\"state change $1 -\u003e $2\", b, not b)\n  app.release\n\nproc activateStatefulAction(action: SimpleAction; parameter: Variant; app: Application) =  \n  app.hold\n  block:\n    echo format(\"action $1 activated\", action.name)\n    let state: Variant = action.state\n    var l: uint64\n    let oldState = state.getString(l) # yes uint64 parameter is a bit ugly\n    let newState = parameter.getString(l)\n    action.state = newVariantString(newState)  \n    echo format(\"state change $1 -\u003e $2\", oldState, newState)\n  app.release\n\nproc quitProgram(action: SimpleAction; parameter: Variant; app: Application) =\n  quit(app)\n\nproc appStartup(app: Application) =\n  let quit = newSimpleAction(\"quit\") # here we create the actions for whole app\n  connect(quit, \"activate\", quitProgram, app)\n  app.addAction(quit)\n\n  let menu = gio.newMenu() # root of all menus\n  block: # plain stateless menu\n    let subMenu = gio.newMenu()\n    menu.appendSubMenu(\"Application\", submenu)\n    # let section = gio.newMenu() # no separating section needed here\n    # submenu.appendSection(nil, section)\n    # section.append(\"Quit\", \"app.quit\")\n    submenu.append(\"Quit\", \"app.quit\")\n\n  block: #stateful menu with radio items\n    let subMenu = gio.newMenu()\n    menu.appendSubMenu(\"Layout\", submenu)\n    let subMenu2 = gio.newMenu()\n    submenu.appendSubMenu(\"justify\", submenu2)\n    let section = gio.newMenu()\n    submenu2.appendSection(nil, section)\n    section.append(\"left\", \"win.justify::left\")\n    section.append(\"center\", \"win.justify::center\")\n    section.append(\"right\", \"win.justify::right\")\n\n  block: # and finally a toggle menu\n    let subMenu = gio.newMenu()\n    menu.appendSubMenu(\"Spelling\", submenu)\n    let section = gio.newMenu()\n    submenu.appendSection(nil, section)\n    section.append(\"Check\", \"win.toggleSpellCheck\")\n   # finally add the menubar\n    setMenuBar(app, menu)\n\nproc appActivate(app: Application) =\n  let window = newApplicationWindow(app)\n  window.title = \"GTK3 App with Menubar\"\n  window.defaultSize = (500, 200)\n  window.position = WindowPosition.center\n  block: # creat the window related actions\n    let v = newVariantBoolean(true)\n    let spellCheck = newSimpleActionStateful(\"toggleSpellCheck\", nil, v)\n    connect(spellCheck, \"activate\", activateToggleAction, app)\n    window.actionMap.addAction(spellCheck)\n  block:\n    let v = newVariantString(\"left\") # default value and\n    let vt = newVariantType(\"s\") # string (value type)\n    let justifyAction = newSimpleActionStateful(\"justify\", vt, v)\n    connect(justifyAction, \"activate\", activateStatefulAction, app)\n    window.actionMap.addAction(justifyAction)\n  let button = newButton()\n  button.label = \"Justify Center\"\n  #window.add(button) # do not add it here already: (menubar:10010): Gtk-WARNING **:\n  # 22:00:33.230: actionhelper: action win.justify can't be activated due to\n  # parameter type mismatch (parameter type s, target type NULL)\n  button.setDetailedActionName(\"win.justify::center\")\n  #button.setActionName(\"app.quit\") # for a stateless action\n  setAccelsForAction(app, \"win.justify::right\", \"\u003cControl\u003e\u003cShift\u003eR\")\n  window.add(button)\n  showAll(window)\n\nproc main =\n  let app = newApplication(\"app.example\")\n  connect(app, \"startup\", appStartup)\n  connect(app, \"activate\", appActivate)\n  echo \"GTK Version $1.$2.$3\" % [$majorVersion(), $minorVersion(), $microVersion()]\n  let status = run(app)\n  quit(status)\n\nmain()\n\n----\n\nWe can easily modify the above example to get the more modern look with\na HeaderBar and the \"Gears\" MenuButtons:\n\n[[gearsmenu.nim]]\n[source, nim]\n.gearsmenu.nim\n----\n# https://developer.gnome.org/glib/stable/glib-GVariant.html\n# https://developer.gnome.org/glib/stable/glib-GVariantType.html\n# https://wiki.gnome.org/HowDoI/GMenu\n# https://wiki.gnome.org/HowDoI/GAction\n# https://developer.gnome.org/gnome-devel-demos/stable/menubutton.c.html.en\n# nim c gearsmenu.nim\nimport gintro/[gtk, glib, gobject, gio]\nimport strformat\n\n# https://github.com/GNOME/glib/blob/master/gio/tests/gapplication-example-actions.c\nproc activateToggleAction(action: SimpleAction; parameter: Variant; app: Application) =\n  app.hold # hold/release taken over from C example, there may be reasons...\n  block:\n    echo fmt\"action {action.name} activated\"\n    let state: Variant = action.state\n    let b = state.getBoolean\n    action.state = newVariantBoolean(not b)\n    echo fmt\"state change {b} -\u003e {not b}\"\n  app.release\n\nproc activateStatefulAction(action: SimpleAction; parameter: Variant; app: Application) =\n  app.hold\n  block:\n    echo fmt\"action {action.name} activated\"\n    let state: Variant = action.state\n    var l: uint64\n    let oldState = state.getString(l) # yes uint64 parameter is a bit ugly\n    let newState = parameter.getString(l)\n    action.state = newVariantString(newState)\n    echo fmt\"state change {oldState} -\u003e {newState}\"\n  app.release\n\nproc quitProgram(action: SimpleAction; parameter: Variant; app: Application) =\n  quit(app)\n\nproc appStartup(app: Application) =\n  echo \"appStartup\"\n  let quit = newSimpleAction(\"quit\") # here we create the actions for whole app\n  connect(quit, \"activate\", quitProgram, app)\n  app.addAction(quit)\n\nproc appActivate(app: Application) =\n  echo \"appActivate\"\n  let window = newApplicationWindow(app)\n  # window.title = \"GTK3 App with Headerbar and Gears Menu\" # unused due to HeaderBar\n  window.defaultSize = (500, 200)\n  window.position = WindowPosition.center\n\n  let menu = gio.newMenu() # root of all menus\n  block: # plain stateless menu\n    let subMenu = gio.newMenu()\n    menu.appendSubMenu(\"Application\", submenu)\n    # let section = gio.newMenu() # no separating section needed here\n    # submenu.appendSection(nil, section)\n    # section.append(\"Quit\", \"app.quit\")\n    submenu.append(\"Quit\", \"app.quit\")\n\n  block: #stateful menu with radio items\n    let subMenu = gio.newMenu()\n    menu.appendSubMenu(\"Layout\", submenu)\n    let subMenu2 = gio.newMenu()\n    submenu.appendSubMenu(\"justify\", submenu2)\n    let section = gio.newMenu()\n    submenu2.appendSection(nil, section)\n    section.append(\"left\", \"win.justify::left\")\n    section.append(\"center\", \"win.justify::center\")\n    section.append(\"right\", \"win.justify::right\")\n\n  block: # and finally a toggle menu\n    let subMenu = gio.newMenu()\n    menu.appendSubMenu(\"Spelling\", submenu)\n    let section = gio.newMenu()\n    submenu.appendSection(nil, section)\n    section.append(\"Check\", \"win.toggleSpellCheck\")\n\n  let headerBar = newHeaderBar()\n  headerBar.setShowCloseButton\n  headerBar.setTitle(\"Title\")\n  headerBar.setSubtitle(\"Subtitle\")\n  window.setTitlebar (headerBar)\n\n  let menubar = newMenuButton()\n  # menubar.setDirection(ArrowType.none) # show the gears Icon\n  # let image = newImageFromIconName(\"open-menu-symbolic\", IconSize.menu.ord)\n  let image = newImageFromIconName(\"document-save\", IconSize.dialog.ord) # dialog is really big!\n  menubar.setImage(image) # this is only an example for a custom image\n  # menubar.setIconName(\"open-menu-symbolic\") # only gtk4\n  headerBar.packEnd(menubar)\n  menubar.setMenuModel(menu)\n\n  block: # creat the window related actions\n    let v = newVariantBoolean(true)\n    let spellCheck = newSimpleActionStateful(\"toggleSpellCheck\", nil, v)\n    connect(spellCheck, \"activate\", activateToggleAction, app)\n    window.actionMap.addAction(spellCheck)\n  block:\n    let v = newVariantString(\"left\") # default value and\n    let vt = newVariantType(\"s\") # string (value type)\n    let justifyAction = newSimpleActionStateful(\"justify\", vt, v)\n    connect(justifyAction, \"activate\", activateStatefulAction, app)\n    window.actionMap.addAction(justifyAction)\n  let button = newButton()\n  button.label = \"Justify Center\"\n  button.setDetailedActionName(\"win.justify::center\")\n  #button.setActionName(\"app.quit\") # for a stateless action\n  setAccelsForAction(app, \"win.justify::right\", \"\u003cControl\u003e\u003cShift\u003eR\")\n  window.add(button)\n  showAll(window)\n\nproc main =\n  let app = newApplication(\"app.example\")\n  connect(app, \"startup\", appStartup)\n  connect(app, \"activate\", appActivate)\n  echo fmt\"GTK Version {majorVersion()}.{minorVersion()}.{microVersion()}\"\n  let status = run(app)\n  quit(status)\n\nmain()\n----\n\nWhile in the previous example we create only a single menu instance in proc appStartup()\nfor all of our application windows, here we create a new menu for all of our instances\nin proc appActivate(). That seems to work fine, so I assume it is correct.\n\n== GMenu and GAction with GTK Builder\n\nAnd here is an example from https://github.com/GNOME/gtk/blob/mainline/tests/\nwhich uses a combination of gaction and gmenu with a GTK builder XML file for\nthe menu description. \n\n[[gaction2.nim]]\n[source, nim]\n.gaction2.nim\n----\n# nim c gaction2.nim\n# https://github.com/GNOME/gtk/blob/mainline/tests/testgaction.c\n# gcc -Wall gaction.c -o gaction `pkg-config --cflags --libs gtk4`\nimport gintro/[gtk, glib, gobject, gio]\n\nconst menuData = \"\"\"\n\u003cinterface\u003e\n  \u003cmenu id=\"menuModel\"\u003e\n    \u003csection\u003e\n      \u003citem\u003e\n        \u003cattribute name=\"label\"\u003eNormal Menu Item\u003c/attribute\u003e\n        \u003cattribute name=\"action\"\u003ewin.normal-menu-item\u003c/attribute\u003e\n      \u003c/item\u003e\n      \u003csubmenu\u003e\n        \u003cattribute name=\"label\"\u003eSubmenu\u003c/attribute\u003e\n        \u003citem\u003e\n          \u003cattribute name=\"label\"\u003eSubmenu Item\u003c/attribute\u003e\n          \u003cattribute name=\"action\"\u003ewin.submenu-item\u003c/attribute\u003e\n        \u003c/item\u003e\n      \u003c/submenu\u003e\n      \u003citem\u003e\n        \u003cattribute name=\"label\"\u003eToggle Menu Item\u003c/attribute\u003e\n        \u003cattribute name=\"action\"\u003ewin.toggle-menu-item\u003c/attribute\u003e\n      \u003c/item\u003e\n    \u003c/section\u003e\n    \u003csection\u003e\n      \u003citem\u003e\n        \u003cattribute name=\"label\"\u003eRadio 1\u003c/attribute\u003e\n        \u003cattribute name=\"action\"\u003ewin.radio\u003c/attribute\u003e\n        \u003cattribute name=\"target\"\u003e1\u003c/attribute\u003e\n      \u003c/item\u003e\n      \u003citem\u003e\n        \u003cattribute name=\"label\"\u003eRadio 2\u003c/attribute\u003e\n        \u003cattribute name=\"action\"\u003ewin.radio\u003c/attribute\u003e\n        \u003cattribute name=\"target\"\u003e2\u003c/attribute\u003e\n      \u003c/item\u003e\n      \u003citem\u003e\n        \u003cattribute name=\"label\"\u003eRadio 3\u003c/attribute\u003e\n        \u003cattribute name=\"action\"\u003ewin.radio\u003c/attribute\u003e\n        \u003cattribute name=\"target\"\u003e3\u003c/attribute\u003e\n      \u003c/item\u003e\n    \u003c/section\u003e\n  \u003c/menu\u003e\n\u003c/interface\u003e\n\"\"\"\n\nproc changeLabelButton(action: SimpleAction; v: Variant; label: Label) =\n  label.setLabel(\"Text set from button\")\n\nproc normalMenuItem(action: SimpleAction; v: Variant; label: Label) =\n  label.setLabel(\"Text set from normal menu item\")\n\nproc toggleMenuItem(action: SimpleAction; v: Variant; label: Label) =\n  label.setLabel(\"Text set from toggle menu item\")\n\nproc submenuItem(action: SimpleAction; v: Variant; label: Label) =\n  label.setLabel(\"Text set from submenu item\")\n\nproc radio(action: SimpleAction; parameter: Variant; label: Label) =\n  var l: uint64\n  let newState: Variant = newVariantString(getString(parameter, l))\n  let str: string = \"From Radio menu item \" \u0026 getString(newState, l)\n  label.setLabel(str)\n\nproc bye(w: Window) =\n  mainQuit()\n  echo \"Bye...\"\n\nproc main =\n  gtk.init()\n  let\n    window = newWindow()\n    box = newBox(Orientation.vertical, 12)\n    menubutton = newMenuButton()\n    button1 = newButton(\"Change Label Text\")\n    label = newLabel(\"Initial Text\")\n    actionGroup = newSimpleActionGroup()\n\n  window.connect(\"destroy\", gtk.mainQuit)\n  #window.connect(\"destroy\", bye)\n\n  var action = newSimpleAction(\"change-label-button\")\n  discard action.connect(\"activate\", changeLabelButton, label)\n  actionGroup.addAction(action)\n\n  action = newSimpleAction(\"normal-menu-item\")\n  discard action.connect(\"activate\", normalMenuItem, label)\n  actionGroup.addAction(action)\n\n  var v = newVariantBoolean(true)\n  action = newSimpleActionStateful(\"toggle-menu-item\", nil, v)\n  discard action.connect(\"activate\", toggleMenuItem, label)\n  actionGroup.addAction(action)\n\n  action = newSimpleAction(\"submenu-item\")\n  discard action.connect(\"activate\", subMenuItem, label)\n  actionGroup.addAction(action)\n\n  v = newVariantString(\"1\")\n  let vt = newVariantType(\"s\")\n  action = newSimpleActionStateful(\"radio\", vt, v)\n  discard action.connect(\"activate\", radio, label)\n  actionGroup.addAction(action)\n\n  insertActionGroup(window, \"win\", actionGroup)\n\n  label.setMarginTop(12)\n  label.setMarginBottom(12)\n  box.add(label)\n  menubutton.setHAlign(Align.center)\n  let builder: Builder = newBuilderFromString(menuData)\n  let menuModel = builder.getMenuModel(\"menuModel\")\n  let menu = newMenuFromModel(menuModel)\n  menuButton.setPopup(menu)\n  box.add(menubutton)\n  button1.setHalign(Align.center)\n  button1.setActionName(\"win.change-label-button\")\n  box.add(button1)\n  window.add(box)\n  window.showAll\n  gtk.main()\n\nmain()\n----\n\n== GSettings\n\nGSettings provides a convenient way to permanently storing configuration data,\nand to bind them to properties of widgets.\n\nYou can read an introduction at https://blog.gtk.org/2017/05/01/first-steps-with-gsettings/.\n\nFor using GSettings in our own programs, we have first to create a XML file\nwhich defines names and type of each configuration entry, and additional\nprovides default value and a description. The file name of such xml files\nmust always end with \".gschema.xml\".\nThe following example has only one\nfield called like-nim of type boolean (b). For a real application program\nwe would install the configuration on our computer -- unfortunately we\nwould need root access for this. We could do it this way:\n\n----\n# For making gsettings available system wide one method is, as root\n# https://developer.gnome.org/gio/stable/glib-compile-schemas.html\n# echo $XDG_DATA_DIRS\n# /usr/share/gnome:/usr/local/share:/usr/share:/usr/share/gdm\n# cd /usr/local/share/glib-2.0/schemas\n# cp test.gschema.xml .\n# glib-compile-schemas .\n#\n----\n\nFor testing there is an easier method available:\n\nCreate a directory and copy the xml file and the test program below into it.\n\nThen do, as ordinary user:\n\n----\nglib-compile-schemas .\nnim c gsettings.nim\nGSETTINGS_SCHEMA_DIR=\".\" ./gsettings\n---- \n\nThis is the xml file and the test program: \n\n[[test.gschema.xml]]\n[source, xml]\n.test.gschema.xml\n----\n\u003cschemalist\u003e\n  \u003cschema path=\"/org/gnome/recipes/\"       \n         id=\"org.gnome.Recipes\"\u003e\n    \u003ckey type=\"b\" name=\"like-nim\"\u003e\n      \u003cdefault\u003efalse\u003c/default\u003e\n      \u003csummary\u003eI like Nim\u003c/summary\u003e\n      \u003cdescription\u003e\n        I like or like not\n        the Nim programming language.\n      \u003c/description\u003e\n    \u003c/key\u003e\n  \u003c/schema\u003e\n\u003c/schemalist\u003e\n----\n\n[[gsettings.nim]]\n[source, nim]\n.gsettings.nim\n----\n# gsettings.nim -- basic use of gsettings\n# nim c gsettings.nim\n# https://blog.gtk.org/2017/05/01/first-steps-with-gsettings/\n# https://mail.gnome.org/archives/gtk-list/2016-December/msg00003.html\nimport gintro/[gtk, glib, gobject, gio]\n\n# unused\nproc toggle(b: CheckButton) = \n  echo b.active\n  let s = newSettings(\"org.gnome.Recipes\")\n  discard s.setBoolean(\"like-nim\", b.active)\n\nproc appActivate(app: Application) =\n  let window = newApplicationWindow(app)\n  window.title = \"GTK3, Nim and GSettings\"\n  window.defaultSize = (200, 200)\n  let b = newCheckButton()\n  b.halign = Align.center\n  b.label = \"I like Nim\"\n  #b.connect(\"toggled\", toggle) # we don't need this for plain binding!\n  let s = newSettings(\"org.gnome.Recipes\")\n  if s.getBoolean(\"like-nim\"):\n    echo \"I like Nim language\"\n  `bind`(s, \"like-nim\", b, \"active\", {SettingsBindFlag.get, SettingsBindFlag.set})\n  window.add(b)\n  showAll(window)\n\nproc main =\n  let app = newApplication(\"org.gtk.example\")\n  connect(app, \"activate\", appActivate)\n  discard run(app)\n\nmain()\n----\n\nThe command \"glib-compile-schemas .\" compiles all schemas in the current directory. And\n\"GSETTINGS_SCHEMA_DIR=\".\" ./gsettings\" launches our test program with the environment\nvariable GSETTINGS_SCHEMA_DIR pointing to the current directory, containing the compiled schema.\n\nNote that a system tool with same name as our test program exists -- that one can be used\nto get or set configuration data -- for example you may query the current state of field\n\"like-nim\" with\n\n----\ngsettings --schemadir \".\" get org.gnome.Recipes like-nim\n----\n\nOr test program first creates a window with a check button. Then our settings file is\nopened and we print the current value of the boolean variable. After that the\nbind procedure binds the active property (checkmark state) of our widget to the\n\"like-nim\" entry of our settings file. The result of this binding is, that\nour checkmark state is automatically made persistent, that is when we terminate\nand restart our test program, the checkmark will have the last state again.\n\nThese bindings works for booleans, integers, floats, strings. The type of the property of the\nwidget must be identical with the corresponding type of the entry in the settings xml file. \n\nOn Linux you may permanently set the gsetting directory by adding the statement\n\n----\nexport GSETTINGS_SCHEMA_DIR=\"pathToMyProg\"\n----\n\nto your .bashrc file -- of course after replacing pathToMyProg with the actual path.\n\nFor more informations about gsettings see\n\nhttps://developer.gnome.org/gio/stable/GSettings.html.\n\nhttps://developer.gnome.org/gio/stable/running-gio-apps.html\n\n== Drawing with Cairo graphics library\n\nThe next example shows how we can use the cairo graphics library for drawing on a DrawingArea widget,\nand at the same time uses glib timeoutAdd() function to create a timer which periodically calls the\ndrawing function to create some animations. The code is based on a recent post to the cairo mailing list\nand shows a sine wave which is continuously moving to the left.\n\nNOTE: The gobject-introspection generated cairo module was only a minimal stub, because cairo\nlibrary does not really support introspection. Now we are using a cairo module which is generated \ndirectly from the cairo C header files with the tool c2nim and then modified to support a high level\nAPI.\n\n[[cairo_anim.nim]]\n[source,nim]\n.cairo_anim.nim\n----\n# https://lists.cairographics.org/archives/cairo/2016-October/027791.html\n# Nim version of that plain cairo animation example\n\nimport gintro/[gtk, glib, gobject, gio, cairo]\nimport math\n\nconst\n  NumPoints = 1000\n  Period = 100.0\n\nproc invalidateCb(w: Widget): bool =\n  queueDraw(w)\n  return SOURCE_CONTINUE\n\nproc sineToPoint(x, width, height: int): float =\n  math.sin(x.float * math.TAU / Period) * height.float * 0.5 + height.float * 0.5\n\nproc drawingAreaDrawCb(widget: DrawingArea; context: Context): bool =\n  var redrawNumber {.global.} : int\n  let width = getAllocatedWidth(widget)\n  let height = getAllocatedHeight(widget)\n  for i in 1 ..\u003c NumPoints:\n    context.lineTo(i.float , sineToPoint(i + redrawNumber, width, height))\n  context.stroke\n  inc(redrawNumber)\n  return true # TRUE to stop other handlers from being invoked for the event. FALSE to propagate the event further.\n\nproc appActivate(app: Application) =\n  let window = newApplicationWindow(app)\n  window.title = \"Drawing example\"\n  window.defaultSize = (400, 400)\n  let drawingArea = newDrawingArea()\n  window.add(drawingArea)\n  showAll(window)\n  discard timeoutAdd(1000 div 60, invalidateCb, drawingArea)\n  connect(drawingArea, \"draw\", drawingAreaDrawCb)\n\nproc main =\n  let app = newApplication(\"org.gtk.example\")\n  connect(app, \"activate\", appActivate)\n  discard run(app)\n\nmain()\n----\n\n== A simple ListView example\n\nimage::NimGTK3ListView.png[]\n\nRecently someone reported about some problems porting a GTK2 application to Nim GTK3, so I will give a small example\nwhich may help using ListViews and TreeViews. These two widget types are the most complicated widget types in GTK --\nI can remember that I had some trouble myself when I used Ruby-GTK some years ago. As I can currently not remember\ndetails about use of ListView widgets, I decided to take an example code from http://zetcode.com/gui/gtk2/gtktreeview/[zetcode.com] as starting point. Of course\nporting is straight forward, but when I tried to compile the result I noticed some bugs and restrictions of current\ngintro package. Of course not really surprising, as the package is not really tested yet. I will try to fix these bugs later.\nFirst problem is, that we store a ListStore as model in our TreeView, and we need to extract that ListStore from the TreeView\nfor some operations. But module gtk.nim offers currently only a function to extract the model itself, which is of type TreeModel.\nIn the C code an upcast is used to get the ListStore from the retrieved TreeModel. To avoid casting in our Nim code, I have just copied\nthe getModel() proc and modified  it to return a ListStore. Second problem was, that module gio export a ListStore datatype also.\nTo avoid prefixing all ListStore types with gtk prefix, I excluded gio.ListStore from import list. And finally a real bug:\nProc newListStore() expects currently a plain pointer as last parameter, while we know that it should be the address of a list of GTypes.\nSo we have to use an ugly cast for now. For populating the ListStore currently GValues are used. That is not very convenient, and\nfor that we need the correct GType of our string list. In C one would use the macro G_TYPE_STRING, which is not provided by\ngobject-introspection. So we use typeFromName() to get the correct GType, which works fine when we know that the string name is \"gchararray\".\nLater we will provide a higher level function for this process.\n\nI will try to give more and better explained ListView and TreeView examples later...\n\n[[listview.nim]]\n[source,nim]\n.listview.nim\n----\n# http://zetcode.com/gui/gtk2/gtktreeview/\n# dynamiclistview.c\n\nimport gintro/[glib, gobject, gtk]\nimport gintro/gio except ListStore\n\nconst    \n  LIST_ITEM = 0\n  N_COLUMNS = 1\n\nvar list: TreeView\n\n# this is copied from gtk.nim\n#proc getModel*(self: TreeView): TreeModel =\n#  new(result)\n#  result.impl = gtk_tree_view_get_model(cast[ptr TreeView00](self.impl))\n\nproc getListStore(self: TreeView): ListStore =\n  new(result)\n  result.impl = gtk_tree_view_get_model(cast[ptr TreeView00](self.impl))\n\nproc appendItem(widget: Button; entry: Entry) =\n  var\n    val: Value\n    iter: TreeIter\n  let store = getListStore(list)\n  let gtype = typeFromName(\"gchararray\")\n  discard gValueInit(val, gtype)\n  gValueSetString(val, entry.text)\n  store.append(iter)\n  store.setValue(iter, LIST_ITEM, val)\n  entry.text = \"\"\n\nproc removeItem(widget: Button; selection: TreeSelection) =\n  var    \n    ls: ListStore\n    iter: TreeIter\n  let store = getListStore(list)\n  if not store.getIterFirst(iter):\n      return\n  if getSelected(selection, ls, iter):\n    discard store.remove(iter)\n\nproc onRemoveAll(widget: Button; selection: TreeSelection) =\n  var\n    iter: TreeIter\n  let store = getListStore(list)\n  if not store.getIterFirst(iter):\n    return\n  clear(store)\n\nproc initList(list: TreeView) =\n  let renderer = newCellRendererText()\n  let column = newTreeViewColumn()\n  column.title = \"List Item\"\n  column.packStart(renderer, true)\n  column.addAttribute(renderer, \"text\", LIST_ITEM)\n  discard list.appendColumn(column)\n  let gtype = typeFromName(\"gchararray\")\n  let store = newListStore(N_COLUMNS, cast[pointer]( unsafeaddr gtype)) # cast due to bug in gtk.nim\n  list.setModel(store)\n\nproc appActivate(app: Application) =\n  let\n    window = newApplicationWindow(app)\n    sw = newScrolledWindow()\n    hbox = newBox(Orientation.horizontal, 5)\n    vbox = newBox(Orientation.vertical, 0)\n    add = newButton(\"Add\")\n    remove = newButton(\"Remove\")\n    removeAll = newButton(\"Remove All\")\n    entry = newEntry()\n  window. title = \"List view\"\n  window.position = WindowPosition.center\n  window.borderWidth = 10\n  window.setSizeRequest(370, 270)\n  list = newTreeView()  \n  sw.add(list)\n  sw.setPolicy(PolicyType.automatic, PolicyType.automatic)\n  sw.setShadowType(ShadowType.etchedIn)\n  list.setHeadersVisible(false)\n  vbox.packStart(sw, true, true, 5)\n  entry.setSizeRequest(120, -1)\n  hbox.packStart(add, false, true, 3)\n  hbox.packStart(entry, false, true, 3)\n  hbox.packStart(remove, false, true, 3)\n  hbox.packStart(removeAll, false, true, 3)\n  vbox.packStart(hbox, false, true, 3)\n  window.add(vbox)\n  initList(list)\n  let selection = getSelection(list)\n  connect(add, \"clicked\", listview.appendItem, entry)\n  connect(remove, \"clicked\", listview.removeItem, selection)\n  connect(removeAll, \"clicked\", listview.onRemoveAll, selection)\n  showAll(window)\n\nproc main =\n  let app = newApplication(\"org.gtk.example\")\n  connect(app, \"activate\", appActivate)\n  discard run(app)\n\nmain()\n----\n\n\n== A ListView example with CSS styling\n\nRecently C. Eric Cashon provided this example at https://discourse.gnome.org/t/gtk-treeview-cell-color-change/1750/3\n\nI will show his original code here too, so we can compare it better with the Nim version.\nWe see that Nim code has currently some disadvantages still, for example we have no\nvarargs procs implemented, so setting of properties and attributes is done using GValues,\nwhich is typesafe, but not really compact. That is not too bad, but we may consider\ncreating macros to support a more dense, but still typesafe way similar to C's varargs functions.\n\n[[cell_color1.c]]\n[source,c]\n.cell_color1.c\n----\n// gcc -Wall cell_color1.c -o cell_color1 `pkg-config --cflags --libs gtk+-3.0`\n// https://discourse.gnome.org/t/gtk-treeview-cell-color-change/1750/4\n// C. Eric Cashon\n\n#include\u003cgtk/gtk.h\u003e\n\nenum\n{\n   ID,\n   PROGRAM,\n   COLOR1,\n   COLOR2,\n   COLUMNS\n};\n\nint main(int argc, char *argv[])\n  {\n    gtk_init(\u0026argc, \u0026argv);\n\n    GtkWidget *window=gtk_window_new(GTK_WINDOW_TOPLEVEL);\n    gtk_window_set_title(GTK_WINDOW(window), \"Select Cell\");\n    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);\n    gtk_window_set_default_size(GTK_WINDOW(window), 500, 500);\n    gtk_container_set_border_width(GTK_CONTAINER(window), 20);\n    g_signal_connect(window, \"destroy\", G_CALLBACK(gtk_main_quit), NULL);\n\n    GtkTreeIter iter;\n    GtkListStore *store=gtk_list_store_new(COLUMNS, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);\n    gtk_list_store_append(store, \u0026iter);\n    gtk_list_store_set(store, \u0026iter, ID, 0, PROGRAM, \"Gedit\", COLOR1, \"DarkCyan\", COLOR2, \"cyan\", -1);\n    gtk_list_store_append(store, \u0026iter);\n    gtk_list_store_set(store, \u0026iter, ID, 1, PROGRAM, \"Gimp\", COLOR1,  \"LightSlateGray\", COLOR2, \"cyan\", -1);\n    gtk_list_store_append(store, \u0026iter);\n    gtk_list_store_set(store, \u0026iter, ID, 2, PROGRAM, \"Inkscape\", COLOR1, \"DarkCyan\", COLOR2, \"cyan\", -1);\n    gtk_list_store_append(store, \u0026iter);\n    gtk_list_store_set(store, \u0026iter, ID, 3, PROGRAM, \"Firefox\", COLOR1, \"LightSlateGray\", COLOR2, \"cyan\", -1);\n    gtk_list_store_append(store, \u0026iter);\n    gtk_list_store_set(store, \u0026iter, ID, 4, PROGRAM, \"Calculator\", COLOR1, \"DarkCyan\", COLOR2, \"cyan\", -1);\n    gtk_list_store_append(store, \u0026iter);\n    gtk_list_store_set(store, \u0026iter, ID, 5, PROGRAM, \"Devhelp\", COLOR1, \"LightSlateGray\", COLOR2, \"cyan\", -1);\n\n    GtkWidget *tree=gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));\n    gtk_widget_set_hexpand(tree, TRUE);\n    gtk_widget_set_vexpand(tree, TRUE);\n    g_object_set(tree, \"activate-on-single-click\", TRUE, NULL);\n\n    GtkTreeSelection *selection=gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));\n    gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);\n\n    GtkCellRenderer *renderer1=gtk_cell_renderer_text_new();\n    g_object_set(renderer1, \"editable\", FALSE, NULL);\n\n    GtkCellRenderer *renderer2=gtk_cell_renderer_text_new();\n    g_object_set(renderer2, \"editable\", TRUE, NULL);\n   \n    //Bind the COLOR column to the \"cell-background\" property.\n    GtkTreeViewColumn *column1=gtk_tree_view_column_new_with_attributes(\"ID\", renderer1, \"text\", ID, \"cell-background\", COLOR1, NULL);\n    gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column1);    \n    GtkTreeViewColumn *column2 = gtk_tree_view_column_new_with_attributes(\"Program\", renderer2, \"text\", PROGRAM, \"cell-background\", COLOR2, NULL);\n    gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column2);\n   \n    GtkWidget *grid=gtk_grid_new();\n    gtk_grid_attach(GTK_GRID(grid), tree, 0, 0, 1, 1);\n\n    gtk_container_add(GTK_CONTAINER(window), grid);\n\n    gchar *css_string=g_strdup(\"treeview{background-color: rgba(0,255,255,1.0); font-size:30pt} treeview:selected{background-color: rgba(255,255,0,1.0); color: rgba(0,0,255,1.0);}\");\n    GError *css_error=NULL;\n    GtkCssProvider *provider=gtk_css_provider_new();\n    gtk_css_provider_load_from_data(provider, css_string, -1, \u0026css_error);\n    gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);\n    if(css_error!=NULL)\n      {\n        g_print(\"CSS loader error %s\\n\", css_error-\u003emessage);\n        g_error_free(css_error);\n      }\n    g_object_unref(provider);\n    g_free(css_string);\n   \n    gtk_widget_show_all(window);\n\n    gtk_main();\n    return 0;   \n  }\n----\n\nAnd this is the Nim version, created with c2nim and some manual tuning:\n\n[[css_colored_listview.nim]]\n[source,nim]\n.css_colored_listview.nim\n----\n# nim c css_colored_listview.nim\nimport gintro/[gtk, glib, gobject]\nimport gintro/gdk except Window # there is a problem with gdk.Window -- we have to investigate!\nconst # maybe we should use Nim's enum here?\n  Id = 0\n  Program = 1\n  Color1 = 2\n  Color2 = 3\n  Columns = 4\n\nproc bye(w: Window) =\n  mainQuit()\n  echo \"Bye...\"\n\nproc toStringVal(s: string): Value =\n  let gtype = typeFromName(\"gchararray\")\n  discard init(result, gtype)\n  setString(result, s)\n\nproc toUIntVal(i: int): Value =\n  let gtype = typeFromName(\"guint\")\n  discard init(result, gtype)\n  setUint(result, i)\n\nproc toBoolVal(b: bool): Value =\n  let gtype = typeFromName(\"gboolean\")\n  discard init(result, gtype)\n  setBoolean(result, b)\n\n# we need the following two procs for now -- later we will not use that ugly cast...\nproc typeTest(o: gobject.Object; s: string): bool =\n  let gt = g_type_from_name(s)\n  return g_type_check_instance_is_a(cast[ptr TypeInstance00](o.impl), gt).toBool\n\nproc listStore(o: gobject.Object): gtk.ListStore =\n  assert(typeTest(o, \"GtkListStore\"))\n  cast[gtk.ListStore](o)\n\nproc updateRow(renderer: CellRendererText; path: cstring; newText: cstring; tree: TreeView) =\n  var iter: TreeIter\n  var value: Value\n  let gtype = typeFromName(\"gchararray\")\n  discard init(value, gtype)\n  let store = listStore(tree.getModel())   \n  value.setString(newText)\n  let treePath = newTreePathFromString(path)\n  discard store.getIter(iter, treePath)\n  store.setValue(iter, 1, value)\n\n# we use the old gtk style with init() as is used in the C original -- maybe better use modern app sytle \nproc main() =\n  gtk.init()\n  let window = newWindow()\n  window.title = \"Select Cell\"\n  window.position = WindowPosition.center\n  window.defaultSize = (500, 500)\n  window.borderWidth = 20\n  connect(window, \"destroy\", bye)\n  var iter: TreeIter\n  var h = [typeFromName(\"guint\"), typeFromName(\"gchararray\"), typeFromName(\"gchararray\"),\n    typeFromName(\"gchararray\")]\n  var store = newListStore(Columns,  cast[pointer]( unsafeaddr h)) # cast is ugly, we should fix it in bindings.\n  let progNames = [\"Gedit\", \"Gimp\", \"Inkscape\", \"Firefox\", \"Calculator\", \"Devhelp\"]\n  for i, n in progNames:\n    store.append(iter) # currently we have to use setValue() as there is no varargs proc as in C original\n    store.setValue(iter, Id, toUIntVal(i))\n    store.setValue(iter, Program, toStringVal(n))\n    store.setValue(iter, Color1, toStringVal(if (i and 1) != 0: \"LightSlateGray\" else: \"DarkCyan\"))\n    store.setValue(iter, Color2, toStringVal(\"cyan\"))\n  var tree  = newTreeViewWithModel(store)\n  tree.setHexpand\n  tree.setVexpand\n  setProperty(tree, \"activate-on-single-click\", toBoolVal(true))\n  var selection = tree.getSelection()\n  selection.setMode(SelectionMode.single)\n  var renderer1 = newCellRendererText()\n  setProperty(renderer1, \"editable\", toBoolVal(false))\n  var renderer2 = newCellRendererText()\n  setProperty(renderer2, \"editable\", toBoolVal(true))\n  connect(renderer2, \"edited\", updateRow, tree)\n  ## Bind the Color column to the \"cell-background\" property.\n  var column1 = newTreeViewColumn()\n  column1.setTitle(\"ID\")\n  column1.packStart(renderer1, true)\n  column1.addAttribute(renderer1, \"text\", Id)\n  column1.addAttribute(renderer1, \"cell-background\", Color1)\n  discard tree.appendColumn(column1)\n  var column2  = newTreeViewColumn()\n  column1.setTitle(\"Program\")\n  column1.packStart(renderer2, true)\n  column1.addAttribute(renderer2, \"text\", Program)\n  column1.addAttribute(renderer2, \"cell-background\", Color2)\n  discard tree.appendColumn(column2)\n  var grid = newGrid() # only one occupied cell makes no sense -- but so we can add more widgets later\n  grid.attach(tree, 0, 0, 1, 1)\n  window.add(grid)\n  const cssString = # note: big font selected intentionally\n    \"\"\"treeview{background-color: rgba(0,255,255,1.0); font-size:30pt} treeview:selected{background-color:\n    rgba(255,255,0,1.0); color: rgba(0,0,255,1.0);}\"\"\"\n  var provider  = newCssProvider()\n  discard provider.loadFromData(cssString)\n  addProviderForScreen(getDefaultScreen(), provider, STYLE_PROVIDER_PRIORITY_APPLICATION)\n  window.showAll\n  gtk.main()\n\nmain()\n----\n\nWhen you compile with `nim c -d:release -d:danger --passC:-flto css_colored_listview.nim`\nyou will get an executable size of 80k, which is big compared with the 20k of the C version, but\nnot too bad. You may note that I have added the updateRow() proc, which is necessary to\nmake editing the program name entries permanent. That proc needs cstring parametes, which\nmay be surprising, as we generally use Nim strings. Not a big problem, maybe intended, we may have to\ncheck the connect() macro in gimpl.nim.\n\n== And one more Listview example -- with custom cairo drawing\n\nThis example is again a Nim version of a C example from C. Eric Cashon\nprovided at https://discourse.gnome.org/t/gtk-how-to-draw-on-top-of-gtktreeview/1783/2.\n\nIt draws an rectangular frame on a selected listview cell. For that to work\nconnectAfter() is used to ensure that the custom cairo drawing occurs after\nthe widget is drawn by GTK.\n\n[[overlay_tree1.nim]]\n[source,nim]\n.overlay_tree1.nim\n----\n# nim c overlayTree1.nim\nimport gintro/[gtk, gdk, glib, gobject, cairo]\nimport strformat\nfrom strutils import parseInt\nconst\n  Id = 0\n  Program = 1\n  Color = 2\n  Color2 = 3\n  Columns = 4\n\nvar\n  rowG = 0\n  columnG = 1\n\nproc bye(w: gtk.Window) =\n  mainQuit()\n  echo \"Bye...\"\n\nproc toStringVal(s: string): Value =\n  let gtype = typeFromName(\"gchararray\")\n  discard init(result, gtype)\n  setString(result, s)\n\nproc toUIntVal(i: int): Value =\n  let gtype = typeFromName(\"guint\")\n  discard init(result, gtype)\n  setUint(result, i)\n\nproc toBoolVal(b: bool): Value =\n  let gtype = typeFromName(\"gboolean\")\n  discard init(result, gtype)\n  setBoolean(result, b)\n\nproc selectCell(treeView: TreeView; path: TreePath; column: TreeViewColumn) =\n  let str = toString(path)\n  echo fmt\"{str} {getTitle(column)}\"\n  rowG = parseInt(str)\n  queueDraw(treeView)\n\nproc drawRectangle(overlay: Overlay; cr: cairo.Context; treeView: TreeView): bool =\n  echo fmt\"Draw Rectangle {rowG} {columnG}\"\n  let path = newTreePathFromIndices(@[rowG.int32])\n  echo path.toString\n  let column = getColumn(treeView, columnG)\n  var rect: gdk.Rectangle\n  var x, y: int\n  treeView.convertBinWindowToWidgetCoords(0, 0, x, y)\n  cr.save\n  cr.translate(x.float, y.float)\n  cr.setLineWidth(2)\n  cr.setSource(0, 0, 0, 1)\n  treeView.getCellArea(path, column, rect)\n  cr.rectangle(rect.x.float + 1, rect.y.float + 1, rect.width.float - 1, rect.height.float - 1)\n  cr.stroke\n  cr.restore\n  return EVENT_PROPAGATE # false\n\nproc main =\n  gtk.init()\n  let window = newWindow()\n  window.setTitle(\"Overlay Tree\")\n  window.setPosition(WindowPosition.center)\n  window.setDefaultSize(500, 500)\n  window.setBorderWidth(20)\n  window.connect(\"destroy\", bye)\n  var iter: TreeIter\n  let h = [typeFromName(\"guint\"), typeFromName(\"gchararray\"), typeFromName(\"gchararray\"),\n    typeFromName(\"gchararray\")]\n  let store = newListStore(Columns, cast[pointer](unsafeaddr h)) # cast is ugly, we should fix it in bindings.\n  let progNames = [\"Gedit\", \"Gimp\", \"Inkscape\", \"Firefox\", \"Calculator\", \"Devhelp\"]\n  for i, n in progNames:\n    store.append(iter) # currently we have to use setValue() as there is no varargs proc as in C original\n    store.setValue(iter, Id, toUIntVal(i))\n    store.setValue(iter, Program, toStringVal(n))\n    store.setValue(iter, Color, toStringVal(\"SpringGreen\"))\n    store.setValue(iter, Color2, toStringVal(\"cyan\"))\n  let tree = newTreeViewWithModel(store)\n  tree.setHexpand\n  tree.setVexpand\n  tree.setProperty(\"activate-on-single-click\", toBoolVal(true))\n  let selection = tree.getSelection\n  selection.setMode(SelectionMode.single)\n  let renderer1 = newCellRendererText()\n  renderer1.setProperty(\"editable\", toBoolVal(false))\n  let renderer2 = newCellRendererText()\n  renderer2.setProperty(\"editable\", toBoolVal(true))\n  tree.connect(\"row-activated\", selectCell)\n  ## Bind the COLOR column to the \"cell-background\" property.\n  let column1 = newTreeViewColumn()\n  column1.setTitle(\"ID\")\n  column1.packStart(renderer1, true)\n  column1.addAttribute(renderer1, \"text\", Id)\n  column1.addAttribute(renderer1, \"cell-background\", Color)\n  discard tree.appendColumn(column1)\n  let column2 = newTreeViewColumn()\n  column2.setTitle(\"Program\")\n  column2.packStart(renderer2, true)\n  column2.addAttribute(renderer2, \"text\", Program)\n  column2.addAttribute(renderer2, \"cell-background\", Color2)\n  discard tree.appendColumn(column2)\n  ## For drawing the outline of the cell.\n  let overlay = newOverlay()\n  overlay.setHexpand\n  overlay.setVexpand\n  overlay.setAppPaintable\n  overlay.addOverlay(tree)\n  overlay.setOverlayPassThrough(tree, true)\n  overlay.connectAfter(\"draw\", drawRectangle, tree)\n  let grid = newGrid()\n  grid.attach(overlay, 0, 0, 1, 1)\n  window.add(grid)\n  const cssString =\n    \"\"\"treeview{background-color: rgba(0,255,255,1.0);\n      font-size:30pt} treeview:selected{background-color:rgba(0,255,255,1.0);\n      color: rgba(0,0,255,1.0);}\"\"\"\n  let provider = newCssProvider()\n  discard provider.loadFromData(cssString)\n  getDefaultScreen().addProviderForScreen(provider, STYLE_PROVIDER_PRIORITY_APPLICATION)\n  window.showAll\n  gtk.main()\n\nmain() # 123 lines\n----\n\n== A Listview example using a CellDataFunction\n\nThis example shows how a CellDataFunction can be used to\ncustomize cells of a Tree- or Listview.\n\n[[celldatafunction.nim]]\n[source,nim]\n.celldatafunction.nim\n----\n# This example shows how to apply a CellDataFunc to a GtkTreeView\n# C example code was provided by A.Krause in chapter 8 of his book\nimport gintro/[gtk, gobject, glib]\n\nconst\n  Color = 0\n  Columns = 1\n  clr = [\"00\", \"33\", \"66\", \"99\", \"CC\", \"FF\"]\n\nproc bye(w: Window) =\n  mainQuit()\n  echo \"Bye...\"\n\nproc toStringVal(s: string): Value =\n  let gtype = gStringGetType() # typeFromName(\"gchararray\")\n  discard init(result, gtype)\n  setString(result, s)\n\nproc toBoolVal(b: bool): Value =\n  let gtype = gBooleanGetType() # typeFromName(\"gboolean\")\n  discard init(result, gtype)\n  setBoolean(result, b)\n\n# our Nim function\nproc cellDataFuncN(column: TreeViewColumn; renderer: CellRenderer;\n                  model: TreeModel; iter: TreeIter, data: TreeViewColumn) =\n  ##  Get the color string stored by the column and make it the foreground color.\n  # for testing that optional args work, we pass a TreeViewColumn and echo its title\n  echo data.title\n  var val: Value\n  model.getValue(iter, Color, val) \n  let text = val.getString\n  val.unset # is this necessary?\n  setProperty(renderer, \"foreground\", toStringVal(\"#FFFFFF\"))\n  setProperty(renderer, \"foreground-set\", toBoolVal(true))\n  setProperty(renderer, \"background\", toStringVal(text))\n  setProperty(renderer, \"background-set\", toBoolVal(true))\n  setProperty(renderer, \"text\", toStringVal(text))\n\n##  Add three columns to the GtkTreeView. All three of the columns will be\n##  displayed as text, although one is a gboolean value and another is\n##  an integer.\nproc setupTreeView(treeview: TreeView) =\n  let renderer = gtk.newCellRendererText()\n  let column = newTreeViewColumn()\n  column.title = \"Standard Colors\"\n  column.packStart(renderer, expand = true)\n  column.add","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstefansalewski%2Fgintro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstefansalewski%2Fgintro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstefansalewski%2Fgintro/lists"}