Layout managers in GTK 4

Containers and layout policies have been a staple of GTK’s design since the very beginning. If you wanted your widget to lay out its children according to a specific policy, you had to implement GtkContainer for handling the addition, removal, and iteration of the child widgets, and then you had to implement the size negotiation virtual functions from GtkWidget to measure, position, and size each child.

One of the major themes of the GTK 4 development cycle is to delegate more functionality to ancillary objects instead of encoding it into the base classes provided by GTK. For instance, we moved the event handling from signal handlers described by GtkWidget into event controllers, and rendering is delegated to GtkSnapshot objects. Another step in that direction is decoupling the layout mechanism from GtkWidget itself to an ancillary type, GtkLayoutManager.

Layout Managers

A layout manager is the object responsible for measuring and sizing a widget and its children. Each GtkWidget owns a GtkLayoutManager, and uses it in place of the measure() and allocate() virtual functions—which are going away. The gist of the change: instead of subclassing a GtkWidget to implement its layout policy, you subclass GtkLayoutManager, and then assign the layout manager to a widget.

Just like in the old GtkWidget code, you will need to override a virtual function to measure the layout, called measure(), which replaces the get_preferred_* family of virtual functions of GTK 3:

static void
layout_measure (GtkLayoutManager *layout_manager,
                GtkWidget        *widget,
                GtkOrientation    orientation,
                int               for_size,
                int              *minimum,
                int              *natural,
                int              *minimum_baseline,
                int              *natural_baseline)

After measuring, you need to assign the size to the layout; this happens in the allocate() virtual function, which replaces the venerable size_allocate() virtual function of previous GTK major versions:

static void
layout_allocate (GtkLayoutManager *layout_manager,
                 GtkWidget        *widget,
                 int               width,
                 int               height,
                 int               baseline)

On the more esoteric side, you can also override the get_request_mode() virtual function, which allows you to declare whether the layout manager requests a constant size, or if one of its sizes depend on the opposite one, like height-for-width or width-for-height:

static GtkSizeRequestMode
layout_get_request_mode (GtkLayoutManager *layout_manager,
                         GtkWidget        *widget)

As you may notice, each virtual function gets passed the layout manager instance, as well as the widget that is using the layout manager.

Of course, this has bigger implications on various aspects of how GTK widgets work, the most obvious being that all the complexity for the layout code can now stay confined into its own object, typically not derivable, whereas the widgets can stay derivable and become simpler.

Another feature of this work is that you can change layout managers at run time, if you want to change the layout policy of a container; you can also have a per-widget layout policy, without adding more complexity to the widget code.

Finally, layout managers allow us to get rid of one of the special cases of GTK, namely: container child properties.

Child properties

Deep in the guts of GtkContainer sits what’s essentially a copy of the GObject property-related code, and whose only job is to implement “child” properties for types deriving from GtkContainer. These container/child properties exist only as long as a child is parented to a specific class of container, and are used for a variety of reasons—but, generally, to control layout options, like the packing direction in boxes and box-like containers; the fixed positioning inside GtkFixed; or the expand/fill rules for notebook tab widgets.

Child properties are hard to use, as they require ad hoc API instead of the usual GObject one, and thus require special casing in GtkBuilder, gtk-doc, and language bindings. Child properties are also attached to the actual direct child of the container, so if a widget interposes a child—like, say, GtkScrolledWindow or GtkListBox do—then you need to keep a reference to that child around in order to change the layout that applies to your own widget.

In GTK’s master branch we got rid of most of them—either by simply removing them when there’s actual widget API that implements the same functionality, or by creating ancillary GObject types and moving child properties to those types. The end goal is to remove all of them, and the relative API from GtkContainer, by the time GTK 4 rolls out. For layout-related properties, GtkLayoutManager provides its own API so that objects are created and destroyed automatically once a child is added to, or removed from, a widget using a layout manager, respectively. The object created is introspectable, and does not require special casing when it comes to documentation or bindings.

You start from deriving your own type from the GtkLayoutChild class, and adding properties just like you would for any other GObject type. Then, you override GtkLayoutManager‘s create_layout_child() virtual function:

static GtkLayoutChild *
create_layout_child (GtkLayoutManager *manager,
                     GtkWidget *container,
                     GtkWidget *child)
{
  // The simplest implementation
  return g_object_new (your_layout_child_get_type (),
                       "layout-manager", manager,
                       "child-widget", child,
                       "some-property", some_property_initial_state,
                       NULL);
}

After that, you can access your layout child object as long as a widget is still a child of the container using the layout manager; if the child is removed from its parent, or the container changes the layout manager, the layout child is automatically collected.

New layout managers

Of course, just having the GtkLayoutManager class in GTK would not do us any good. GTK 4 introduces various layout managers for application and widget developers:

  • GtkBinLayout implements the layout policy of GtkBin, with the added twist that it supports multiple children stacked on top of each other, similarly to how GtkOverlay works. You can use each widget’s alignment and expansion properties to control their location within the allocated area, and the GtkBinLayout will always ask for as much space as it’s needed to allocate its largest child.
  • GtkBoxLayout is a straight port of the layout policy implemented by GtkBox; GtkBox itself has been ported to use GtkBoxLayout internally.
  • GtkFixedLayout is a port of the fixed layout positioning policy of GtkFixed and GtkLayout, with the added functionality of letting you define a generic transformation, instead of a pure 2D translation for each child; GtkFixed has been modified to use GtkFixedLayout and use a 2D translation—and GtkLayout has been merged into GtkFixed, as its only distinguishing feature was the implementation of the GtkScrollable interface.
  • GtkCustomLayout is a convenience layout manager that takes functions that used to be GtkWidget virtual function overrides, and it’s mostly meant to be a bridge while porting existing widgets towards the layout manager future.

We are still in the process of implementing GtkGridLayout and make GtkGrid use it internally, following the same pattern as GtkBoxLayout and GtkBox. Other widgets inside GTK will get their own layout managers along the way, but in the meantime they can use GtkCustomLayout.

The final step is to implement a constraint-based layout manager, which would let us create complex, responsive user interfaces without resorting to packing widgets into nested hierarchies. Constraint-based layouts deserve their own blog post, so stay tuned!

Entries in GTK 4

One of the larger refactorings that recently landed in GTK master is re-doing the entry hierarchy. This post is summarizing what has changed, and why we think things are better this way.

Entries in GTK 3

Lets start by looking at how things are in GTK 3.

GtkEntry is the basic class here. It implements the GtkEditable interface. GtkSpinButton is a subclass of GtkEntry. Over the years, more things were added. GtkEntry gained support for entry completion, and for embedding icons, and for displaying progress. And we added another subclass, GtkSearchEntry.

Some problems with this approach are immediately apparent. gtkentry.c is more than 11100 lines of code. It it not only very hard to add more features to this big codebase, it is also hard to subclass it – and that is the only way to create your own entries, since all the single-line text editing functionality is inside GtkEntry.

The GtkEditable interface is really old – it has been around since before GTK 2. Unfortunately, it has not really been successful as an interface – GtkEntry is the only  implementation, and it uses the interface functions internally in a confusing way.

Entries in GTK 4

Now lets look at how things are looking in GTK master.

The first thing we’ve done is to move the core text editing functionality of GtkEntry into a new widget called GtkText. This is basically an entry minus all the extras, like icons, completion and progress.

We’ve made the GtkEditable interface more useful, by adding some more common functionality (like width-chars and max-width-chars) to it, and made GtkText implement it. We also added helper APIs to make it easy to delegate a GtkEditable implementation to another object.

The ‘complex’ entry widgets (GtkEntry, GtkSpinButton, GtkSearchEntry) are now all composite widgets, which contain a GtkText child, and delegate their GtkEditable implementation to this child.

Finally, we added a new GtkPasswordEntry widget, which takes over the corresponding functionality that GtkEntry used to have, such as showing a Caps Lock warning

or letting the user peek at the content.

Why is this better?

One of the main goals of this refactoring was to make it easier to create custom entry widgets outside GTK.

In the past, this required subclassing GtkEntry, and navigating a complex maze of vfuncs to override. Now, you can just add a GtkText widget, delegate your GtkEditable implementation to it, and have a functional entry widget with very little effort.

And you have a lot of flexibility in adding fancy things around the GtkText component. As an example, we’ve added a tagged entry to gtk4-demo that can now be implemented easily outside GTK itself.

Will this affect you when porting from GTK 3?

There are a few possible gotcha’s to keep in mind while porting code to this new style of doing entries.

GtkSearchEntry and GtkSpinButton are no longer derived from GtkEntry. If you see runtime warnings about casting from one of these classes to GtkEntry, you most likely need to switch to using GtkEditable APIs.

GtkEntry and other complex entry widgets are no longer focusable – the focus goes to the contained GtkText instead. But gtk_widget_grab_focus() will still work, and move the focus the right place. It is unlikely that you are affected by this.

The Caps Lock warning functionality has been removed from GtkEntry. If you were using a GtkEntry with visibility==FALSE for passwords, you should just switch to GtkPasswordEntry.

If you are using a GtkEntry for basic editing functionality and don’t need any of the extra entry functionality, you should consider using a GtkText instead.

Testing Discourse for GTK

For the past 20 years or so, GTK used IRC and mailing lists for discussions related to the project. Over the years, use of email for communication has declined, and the overhead of maintaining the infrastructure has increased; sending email to hundreds or thousands of people has become increasingly indistinguishable from spam, in the eyes of service providers, and GNOME had to try and ask for exceptions—which are not easy to get, and are quite easy to be revoked. On top of that, the infrastructure in use for managing mailing lists is quite old and crumbly, and it’s unnecessarily split into various sub-categories that make following discussions harder than necessary.

After discussions among the GTK team, with the GNOME infrastructure maintainers, and with the GTK community at large, we decided to start a trial run of Discourse as a replacement for mailing lists, first and foremost, and as a way to provide an official location for the GTK community to discuss the development of, and with, GTK—as well as the rest of the core GNOME platform: GLib, Pango, GdkPixbuf, etc.

You can find the Discourse instance on discourse.gnome.org. On it, you can use the Platform and Core categories for discussions about the core GNOME platform; you can use the appropriate tags for your topics, and subscribe to the ones you’re interested in.

We’re planning to move some of the pages on the wiki to Discourse as well, especially the ones where we expect feedback from the community.

We’re still working on how to migrate users of the various mailing lists related to GTK, in order to close the lists and have a single venue instead of splitting the community; in the meantime, if you’re subscribed to one or more of these lists:

  • gtk-devel-list
  • gtk-app-devel-list
  • gtk-list
  • gtk-i18n-list

then you may want to have a look at Discourse, and join the discussions there.