Expanding Amtk to support GUIs with headerbar

I initially created the Amtk library to still be able to conveniently create a traditional UI without using deprecated GTK+ APIs, for GNOME LaTeX. But when working on Devhelp (which has a modern UI with a GtkHeaderBar) I noticed that some pieces of information were duplicated in order to create the menus and the GtkShortcutsWindow.

So I’ve expanded Amtk to support GUIs with GtkHeaderBar and GtkShortcutsWindow, and ported Devhelp to Amtk. I’m quite happy with the result, there are fewer lines of code, it avoids information duplication, all the extra information about the GActions are centralized (so inconsistencies are easier to catch), and on top of that Amtk has been designed to be able to share the extra GActions information in a library, so it’ll be possible to provide a higher-level API for the libdevhelp.

Read the Amtk introduction for more details.

PS: oh, and I’ve moved Amtk to its own git repository now, it was initially developed inside Tepl.

GObject design pattern: attached class extension

I wanted to share one recurrent API design that I’ve implemented several times and that I’ve found useful. I’ve coined it “attached class extension”. It is not a complete description like the design patterns documented in the Gang of Four book (I didn’t want to write 10 pages on the subject), it is more a draft. Also the most difficult is to come up with good names, so comments welcome 😉

Intent

Adding a GObject property or signal to an existing class, but modifying that class is not possible (because it is part of another module), and creating a subclass is not desirable.

Also Unknown As

“One-to-one class extension”, or simply “class extension”, or “extending class”, or “Siamese class”.

Motivation

First example: in the gspell library, we would like to extend the GtkTextView class to add spell-checking. We need to create a boolean property to enable/disable the feature. Subclassing GtkTextView is not desirable because the GtkSourceView library already has a subclass (and it should be possible in an application to use both GtkSourceView and gspell at the same time ((Why not implementing spell-checking in GtkSourceView then? Because gspell also supports GtkEntry.)) ).

Before describing the “attached class extension” design pattern, another solution is described, to have some contrast and thus to better understand the design pattern.

Since subclassing is not desirable in our case, as always with Object-Oriented Programming: composition to the rescue! A possible solution is to create a direct subclass of GObject that takes by composition a GtkTextView reference (with a construct-only property). But this has a small disadvantage: the application needs to create and store an additional object. One example in the wild of such pattern is the GtkSourceSearchContext class which takes a GtkSourceBuffer reference to extend it with search capability. Note that there is a one-to-many relationship between GtkSourceBuffer and GtkSourceSearchContext, since it is possible to create several SearchContext instances for the same Buffer. And note that the application needs to store both the Buffer and the SearchContext objects. This pattern could be named “one-to-many class extension”, or “detached class extension”.

The solution with the “attached class extension” or “one-to-one class extension” design pattern also uses composition, but in the reverse direction: see the implementation of the gspell_text_view_get_from_gtk_text_view() function, it speaks for itself. It uses g_object_get_data() and g_object_set_data_full(), to store the GspellTextView object in the GtkTextView. So the GtkTextView object has a strong reference to the GspellTextView; when the GtkTextView is destroyed, so is the GspellTextView. The nice thing with this design pattern is that an application wanting to enable spell-checking doesn’t need to store any additional object, the application has normally already a reference to the GtkTextView, so, by extension, it has also access to the GspellTextView. With this implementation, GtkTextView can store only one GspellTextView, so it is a one-to-one relationship.

Other Examples

I’ve applied this design pattern several other times in gspell, Amtk and Tepl. To give a few other examples:

  • GspellEntry: adding spell-checking to GtkEntry. GspellEntry is not a subclass of GtkEntry because there is already GtkSearchEntry.
  • AmtkMenuShell that extends GtkMenuShell to add convenience signals. GtkMenuShell is the abstract class to derive the GtkMenu and GtkMenuBar subclasses. The convenience signals must work with any GtkMenuShell subclass.
  • AmtkApplicationWindow, an extension of GtkApplicationWindow to add a statusbar property. Subclassing GtkApplicationWindow in a library is not desirable, because several libraries might want to extend GtkApplicationWindow and an application needs to be able to use all those extensions at the same time (the same applies to GtkApplication).

Amtk – Actions, Menus and Toolbars Kit for GTK+

GtkUIManager has been deprecated without a good replacement for applications that want to keep a traditional UI (with a menubar, toolbar and statusbar). So I’ve written a new shared library called Amtk, currently developed inside the Tepl repository. It is a basic GtkUIManager replacement based on GAction. If you are interested, read the Amtk introduction (it explains the problems with what GTK+ currently provides and that Amtk solves) and the API reference.

Note that the library is not yet finished, factory functions are missing, release early release often etc. But I think that what remains to be done is not a lot of work (for my needs at least).