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).

gspell maintenance

The gspell bug tracker is perfect again, there are only feature requests (marked as enhancements).

I’ve fixed two bugs recently, the second one was not that easy to fix:

  • One crash (a failed assertion) probably due to a bug in an underlying library.
  • A responsiveness problem when editing long lines. It turned out that the spell-checking code for GtkTextView was very slow (200 ms to re-check the long line). So I’ve written a new implementation, which is 20x faster! So with 10 ms it’s now responsive.

And I regularly do other various maintenance tasks in gspell, as can be seen in the Git repository.

gspell is now used by at least 6 applications (see the list on the wiki page), and with both GtkTextView and GtkEntry support I’m sure a lot more applications will use it in the future.

If you like the work I’m doing, the gspell fundraising is still open. Your donations encourage me to continue to take care of gspell, to make it a rock-solid library and well-maintained in the long term. Thanks!

Spell-checking for GtkEntry in gspell

It’s done! Everything that I wanted to do initially for the fundraising of gspell is implemented (for the milestone 1).

The main steps were:

  1. Basic infrastructure, insert underlines to misspelled words.
  2. Add the context menu, and have common code between GtkTextView and GtkEntry.
  3. Do not check the word currently typed. Ditto, have common code between GtkTextView and GtkEntry, to define the policy at only one place.
  4. Better word boundaries: take into account apostrophes and dashes.
  5. Take care of a few other details, like forcing to disable spell-checking when the GtkEntry is in password mode.

The fundraising has a second milestone with “Various Other Improvements”, which is currently at 17%, so I’ll do a few more small tasks. For example fixing bug #772406 (a unit test fails with aspell, while it works fine with hunspell, the kind of problems that arise when multiple backends can be used and work slightly differently) and what is described in bug #761921 comment #1 (a first possible step for a better Windows support).

gspell and LaTeXila – progress report

In September I’ve launched two small fundraisings on gspell and LaTeXila. The two goals are now reached, thanks!

I’ve started working on those two projects, here is a progress report.

gspell – adding GtkEntry support

The basic infrastructure is there, and red wavy underlines are inserted to highlight misspelled words. The code is written in a way that it is unit-testable, and unit tests have been written.

Statistics so far:

21 files changed, 1921 insertions(+), 14 deletions(-)

Next steps:

  • The context menu (right-click menu).
  • Do not check the word currently typed.
  • Better word boundaries, to take into account apostrophes and dashes.

For all those next steps, the idea is to have common code between the GtkTextView and GtkEntry support.

LaTeXila – port to GAction/GMenu?

This progress report is a bit more technical.

To create the menu and toolbars, LaTeXila still uses the deprecated GtkUIManager and GtkAction. What I’ve done this week is investigation work, to figure out if it is possible to use GMenu (the new way menus are now usually created). GAction is great and will be used, but for GMenu it is more complicated.

The conclusion is that it will not be possible to use GMenu in LaTeXila. Because in LaTeXila, when hovering a menu item, a longer description is displayed in the status-bar – which makes the user interface self-discoverable – and this is not possible with GMenu (see this comment in bugzilla for more details). Thankfully GTK+ has a more basic way to create menus, with GtkMenuBar, GtkMenuItem etc. So that’s what LaTeXila will use.

Another reason to create a GtkMenuBar manually is to be able to add a sub-menu with the list of recently used files, with GtkRecentChooserMenu. This doesn’t exist as a GMenu, it would need to be implemented.

Also, the nice thing about GtkUIManager is that the information was encoded just once for both the menus and the toolbars. For a certain menu or toolbar item, the needed information is: the short description, the long description, the icon, the keyboard shortcut and the function to call when the action is activated. With GMenu, some of the information needs to be duplicated for creating a toolbar. So that’s another reason why not using GMenu (at least not directly).

So what I will do is to create a simplified GtkUIManager based on GAction. In other words, store the information just once, and have a convenient way to create menus and toolbars. To create a menu or toolbar item, all of what will be needed is to provide the action name; it will fetch the required missing information from a central store.

gspell and LaTeXila fundraisings – thanks!

The gspell fundraising has reached its initial goal! So thanks a lot for your support!

Expect GtkEntry support in the next version of gspell, which is planned for March 2017.

I’ve added a second milestone for the gspell fundraising, because there are a lot of other possible things to do.

The LaTeXila fundraising is going well too, it is currently at 80%. So thanks to the people who donated so far!

If you write documents in LaTeX, and care about having a good LaTeX editor, well maintained and well integrated to GNOME, then consider donating to LaTeXila 😉 There will hopefully be other milestones in the future, for example to improve the auto-completion (e.g. complete the label in \ref commands), or implementing a full-screen mode.

Two Small Fundraisings – on gspell and LaTeXila

We live in a world where it’s increasingly possible to have a part-time job and being paid for other small tasks on the side; think about Uber or airbnb.

I have a half-time job, and I care about Free Software. So the natural thing to do for me is to find ways to be funded for the contributions I do.

That’s why I was really pleased to hear Alexander Larsson, at the end of his talk at GUADEC about Flatpak:


[…]
And also an interesting thing – I think actually super important – is payment. I want people to be able to pay for Free Software.

Which was met with applause. But unfortunately such support in Flatpak and GNOME Software are not going to happen anytime soon, maybe in a few years with some hope.

In the meantime, I’m launching two small fundraisings!

If this is successful, the idea is to add further milestones, after the work is done on the first one.

gspell

gspell is a library that I created last year to share the spell-checking code between gedit and LaTeXila. The code comes from the gedit spell plugin, as such there is only the support for GtkTextView. The goal of the fundraiser is to add support for GtkEntry, a single line text entry field.

Go to the gspell fundraising page if you’re interested!

LaTeXila

GTK+ 3.22, which will be released this Wednesday, will be the last GTK+ 3 stable version (see this announcement). After that, the deprecated functionality of GTK+ 3 will be removed. LaTeXila is not a new application, it is developed since 2009. A fundraising on a new piece of software can concentrate on features. For LaTeXila, what is more important is that it is well maintained, to be still relevant in the 2020’s. So the fundraiser is to make the code ready for GTK+ 4, at least as a next step.

Code maintenance is not really what we can call interesting, but it is useful. We can see this interesting/useful dichotomy quite often in the computer science world: for a researcher, is proof of correctness interesting? Yes. But in practice it is rarely applied. Is writing unit tests interesting? No, but it is very useful.

Go to the LaTeXila fundraising page if you’re convinced 😉

Thoughts on the Linux Mint X-Apps forks

You may be aware that Linux Mint has forked several GNOME applications, either directly from GNOME (Totem -> Xplayer, Evince -> Xreader, Eye of GNOME -> Xviewer), or indirectly via MATE (gedit -> pluma -> XEd).

GNOME is like the Debian of the Linux desktops. But is it a good thing? In the current state of the code, I don’t think so and I’ll explain why, with a solution: creating more shared libraries.

At the end of the day, it’s just a matter of design and usability concerns. We can safely say that the main reason behind the forks is that the Linux Mint developers don’t like the new design of GNOME applications with a GtkHeaderBar.

And there are perfectly valid reasons to not like headerbars. For gedit for example, see the list of usability regressions at this wiki page.

Currently the trend is GtkHeaderBar, but what will it be in 5 years, 10 years? Let’s face it, GNOME is here just following the new trend that came with smartphones and tablets.

So, a GNOME application developer needs to know that:

  • A GUI is an ever-changing thing, exactly like the clothes that you bought last year are already obsolete, right?
  • When the GUI changes too much, other developers don’t like it and fork the project. For valid reasons or not, this doesn’t matter.

The four X-Apps forks account for roughly 200k lines of code. In the short-term it works, Linux Mint has apps with a traditional UI. But hey, porting the code to GTK+ 4 will be another beast, because the four X-Apps still use the deprecated GtkUIManager and GtkAction APIs, among other things.

But when we look at the codebase, there are a lot of code that could be shared between a GNOME app and its fork(s). So there is a solution: creating more shared libraries. The shared libraries would contain the backend code, of course, but also some basic blocks for the UI. The application would just need to glue things up together, assembling objects, binding GObject properties to GSettings, create the main GtkWindow and a few other things.

The difference would be that instead of forking 200k lines of code, it would be forking maybe 20k lines, which is more manageable to maintain in the long term.

In the case of gedit, making its code more re-usable is exactly what I do since several years, but for another reason: being able to create easily specialized text editors or small IDEs.

Beside avoiding code duplication, creating a shared library has the nice side effect that it is much better documented (usually), and with an API browser like Devhelp, it’s a breeze to discover and understand a new codebase, it permits to have a nice overview of the classes. It’s of course possible to have such documentation for application code, but in practice few developers do that, although it would be a big step towards lowering the barrier to entry for newcomers.

When untangling some code from an application and putting it in a shared library, it is also easier to make the code unit testable (and unit tested!), or at least write a mini interactive test in case of frontend code. Making the code more stable, getting closer to bug-free code and thus more successful software.

Developing a shared library doesn’t necessarily mean to provide backward compatibility during 10 years. Nothing prevents you from bumping the major version of the library every 6 months if needed, making the new version parallel-installable with the previous major versions. So that applications are not forced to update the code when there is an API break.

But creating a library is more difficult, API design is hard. But in my opinion it is worth it. GNOME is not only a desktop environment with an application suite, it is also a development platform.

Libtool convenience library to unit test private functions

Only the public API of a library is exported, so when a program is dynamically linked to the DSO (Dynamic Shared Object), it can only use the public functions.

So how do you unit test the private classes of your library?

There is a simple solution, having a Libtool convenience library that contains the whole code of the library, but without an -export linker flag, so that all functions can still be accessed. You then link that *.la file to your unit tests and also to the real library where you add the appropriate -export flag.

You can see that in action in GtkSourceView and gspell. For example in gspell:

When you run make in verbose mode with make V=1, you see that the libtool commands statically link the convenience static library (a file with the *.a extension) into the unit test programs and into the DSO.

That way the *.c files are compiled only once, and adding a unit test to the testsuite/Makefile.am is straightforward.

gspell news bis

Some more gspell news (see the previous iteration):

  • gspell has been re-licensed from GPLv2+ to LGPLv2.1+.
  • gspell no longer depends on the libxml2 and GtkSourceView libraries.
  • gspell 1.0 will be released at the same time as GNOME 3.20 (in a few weeks), with a stable API.
  • There has been several iterations on the API, making it easier to use. See below.
  • In gedit 3.18 there was a regression: the current word being typed was spell checked… which is now fixed, because it was annoying to have a red wavy underline appearing constantly while typing…
  • Words with apostrophes – like “doesn’t” – are now supported!
  • And a testsuite has been written.

API design

See the gspell API reference. There are two things worth mentioning that are generally useful when designing APIs.

First, a constraint has been directly translated into the API itself instead of allowing an illogical combination in an application.

More specifically, for the same GtkTextBuffer, it doesn’t make sense to use a different GspellChecker between an inline checker and an “external” checker (e.g. a dialog window). Since it’s the same GtkTextBuffer, it should be the same GspellChecker to share the session dictionary (when you click on Ignore All, the word is added to the session dictionary). So instead of having a spell-checker property on both the inline checker and the external checker, now a GspellChecker must be attached to a GtkTextBuffer with gspell_text_buffer_set_spell_checker(). That latter function takes a GtkTextBuffer argument, there is no GspellTextBuffer subclass since there is already one in the GtkSourceView library. That’s where g_object_set_data() becomes useful. And by attaching the GspellChecker to the GtkTextBuffer, we no longer need to keep the GspellChecker object around in the application code, we can get it with gspell_text_buffer_get_spell_checker().

The other thing worth mentioning is that at another place a class is more lax and accepts a certain state instead of documenting “please check the value of …”. More precisely, GspellChecker now accepts a NULL language in the case there are no dictionaries installed.

One of my goals when developing gspell is also to learn writing good APIs, so if you see some possible improvements, don’t hesitate! It’s too late for the 1.x versions, but I can keep a bug around on bugzilla with the 2.0 target. And of course, any other comments or testing are welcome.

Thanks to Paolo Borelli for the API review, and thanks to the numerous GNOME translators! gspell 1.0 will be rock solid!

gspell news

  • gspell is now fully hosted on gnome.org!
  • There is now a mailing list.
  • In addition to LaTeXila, gspell is now also used by gedit (5,800 lines removed, yay!).
  • The 0.1.x version has branched and is meant to be installed alongside GNOME 3.18.
  • If everything goes fine, the 1.0 version will be released at the same time as GNOME 3.20, with a stable API.

API reviews are more than welcome!

Stay tuned.