Custom widgets in GTK 4 – Drawing

(This is the second part of a series about custom widgets in GTK 4. Part 1).

Drawing the old-fashioned way

Before looking at how widgets do their own drawing, it is worth pointing out that GtkDrawingArea is still a valid option if all you need is some self-contained cairo drawing.

The only difference between GTK 3 and GTK 4 is that you call gtk_drawing_area_set_draw_func() to provide your drawing function instead of connecting a signal handler to the the ::draw signal. Everything else is the same: GTK provides you with a cairo context, and you can just draw to it.

void
draw_func (GtkDrawingArea *da,
           cairo_t        *cr,
           int             width,
           int             height,
           gpointer        data)
{
  GdkRGBA red, green, yellow, blue;
  double w, h;

  w = width / 2.0;
  h = height / 2.0;

  gdk_rgba_parse (&red, "red");
  gdk_rgba_parse (&green, "green");
  gdk_rgba_parse (&yellow, "yellow");
  gdk_rgba_parse (&blue, "blue");

  gdk_cairo_set_source_rgba (cr, &red);
  cairo_rectangle (cr, 0, 0, w, h);
  cairo_fill (cr);

  gdk_cairo_set_source_rgba (cr, &green);
  cairo_rectangle (cr, w, 0, w, h);
  cairo_fill (cr);

  gdk_cairo_set_source_rgba (cr, &yellow);
  cairo_rectangle (cr, 0, h, w, h);
  cairo_fill (cr);

  gdk_cairo_set_source_rgba (cr, &blue);
  cairo_rectangle (cr, w, h, w, h);
  cairo_fill (cr);
}

...

gtk_drawing_area_set_draw_func (area, draw, NULL, NULL);

The rendering model

One of the major differences between GTK 3 and GTK 4 is that we are now targeting GL / Vulkan instead of cairo. As part of this switch, we have moved from an immediate mode rendering model to a retained mode one. In GTK 3, we were using cairo commands to render onto a surface. In GTK 4, we create a scene graph that contains render nodes, and those render nodes can be passed to renderer, or processed in some other way, or saved to a file.

In the widget API, this change is reflected in the difference between

gboolean (* draw) (GtkWidget *widget, cairo_t *cr)

and

 void (* snapshot) (GtkWidget *widget, GtkSnapshot *snapshot)

GtkSnapshot is an auxiliary object that turns your drawing commands into render nodes and adds them to the scene graph.

The CSS style information for a widget describes how to render its background, border, and so on. GTK translates this a series of function calls that add suitable render nodes to the scene graph, before and after the render nodes for the widgets’ content. So your widget automatically complies with the CSS drawing model, without any extra work.

Providing the render nodes for the content is the reponsibility of the widgets snapshot() implementation. GtkSnapshot has convenience API to make it easy.  For example, use gtk_snapshot_append_texture() to render a texture. Use gtk_snapshot_append_layout() to render text. If you want to use custom cairo drawing, gtk_snapshot_append_cairo() lets you do so.

A drawing widget

To implement a  widget that does some custom drawing, you need to implement the snapshot() function that creates the render nodes for your drawing:

void
demo_snapshot (GtkWidget *widget, GtkSnapshot *snapshot)
{
  GdkRGBA red, green, yellow, blue;
  float w, h;

  gdk_rgba_parse (&red, "red");
  gdk_rgba_parse (&green, "green");
  gdk_rgba_parse (&yellow, "yellow");
  gdk_rgba_parse (&blue, "blue");

  w = gtk_widget_get_width (widget) / 2.0;
  h = gtk_widget_get_height (widget) / 2.0;

  gtk_snapshot_append_color (snapshot, &red,
                             &GRAPHENE_RECT_INIT(0, 0, w, h));
  gtk_snapshot_append_color (snapshot, &green,
                             &GRAPHENE_RECT_INIT(w, 0, w, h));
  gtk_snapshot_append_color (snapshot, &yellow,
                             &GRAPHENE_RECT_INIT(0, h, w, h));
  gtk_snapshot_append_color (snapshot, &blue,
                             &GRAPHENE_RECT_INIT(w, h, w, h));
}

...

widget_class->snapshot = demo_snapshot;

This example produces four color nodes:

If your drawing needs a certain size, you should implement the measure() function too:

void
demo_measure (GtkWidget      *widget,
              GtkOrientation  orientation,
              int             for_size,
              int            *minimum_size,
              int            *natural_size,
              int            *minimum_baseline,
              int            *natural_baseline)
{
  *minimum_size = 100;
  *natural_size = 200;
}

...

widget_class->measure = demo_measure;

GTK keeps the render nodes produced by your snapshot() function and reuses them until you tell it that your widget needs to be drawn again by calling gdk_widget_queue_draw().

Going deeper

The GTK documentation has an overview of the GTK drawing model, if you are interested in reading more about this topic.

Outlook

In the next post, we’ll look how widgets in GTK 4 handle child widgets.

Custom widgets in GTK 4 – Introduction

With GTK 4 getting closer to completion, now is a good time to provide an overview of how custom widgets will look in GTK 4.

This series of posts will look at the major aspects of writing a widget, and how they changed compared to GTK 3. The articles will provide a high-level overview; for a detailed checklist for porting an application to GTK 4, look at the  migration guide.

Introduction

The general direction of our API changes has been to emphasize delegation over subclassing. One of the motivations for this is to make writing your own widgets easier and less error-prone. As a consequence, you will see a lot more auxiliary objects that take over aspects of functionality from core widget classes. And many widgets are now final classes – deriving directly from GtkWidget is expected.

Another general trend in our API is that “everything is a widget.” In GTK 3, we slowly broke up complex widgets into their constituent parts, first with CSS nodes and then with gadgets. In GTK 4, for example the trough and the slider of a GtkScale are fully formed sub-widgets which can maintain their own state and receive input like any other widget.

A big loser in the GTK 4 transition is the GtkContainer base class. It has become much less important. Any widget can have child widgets now. Child properties have been replaced by layout children and their properties. And all focus handling has been moved from GtkContainer to GtkWidget.

Another big loser is GtkWindow. In GTK 3, all the “popups”  (entry completion, menus, tooltips, etc) were using a GtkWindow underneath. In GTK 4, most of them have been converted to popovers, and the GtkPopover implementation has been untangled from GtkWindow. In addition, many pieces of toplevel-specific functionality have been broken out in separate interfaces called GtkRoot and GtkNative.

Outlook

In the next post, we’ll look how widgets in GTK 4 do their own drawing with render nodes.

GTK 3.98.2

When we released 3.98.0, we promised more frequent snapshots, as the remaining GTK 4 features are landing. Here we are a few weeks later, and 3.98.1 and 3.98.2 snapshots have quietly made it out.

So, what is new ?

Features

There is still work left to do, but a few more big features have landed.

The first is that we have completed the reimplementation of GtkPopovers as xdg-popup surfaces, and split up the GdkSurface API into separate GdkToplevel and GdkPopup interfaces (there’s a GdkDragSurface interface too), which reflect the different roles of surfaces:

  • Toplevels are sovereign windows that are placed by the user and can be maximized, fullscreened, etc.
  • Popups are positioned relative to a parent surface and often grab input, e.g. when used for menus.

In GTK, popovers have lost their :relative-to property, since they are now part of the regular hierarchy like any other widget, and GtkWindow has lost its :window-type property, since all instances of GTK_WINDOW_POPUP have been converted to popovers, and windows are just used for proper toplevels.

Another major feature is the new infrastructure for keyboard shortcuts. In the past, GTK has had a plethora of APIs to implement key bindings, mnemonics and accelerators. In GTK 4, all of this is handled by event controllers. GtkShortcutController is a bit more complex than typical event controllers, since it handles all the different kinds of shortcuts with a unified API.

Thankfully, most of the complexity is hidden. For widget implementors, the important APIs are the variants of gtk_widget_class_add_shortcut(), which are used to add key bindings. For applications, mnemonics and global accels (with gtk_application_set_accels_for_action()) work the same as before. Additionally, it is possible to create shortcut controllers and shortcuts in ui files.

A set of smaller features has landed in the form of a few GtkTextTag properties that expose new pango features such as overlines, visible rendering of spaces and control over hyphenation. These can now be controlled in a GtkTextView via tags. In entries, they can already be controlled by directly adding pango attributes.

Completions

When I wrote about 3.98, I said that the Drag-and-Drop refactoring was complete. That turned out to be not quite correct, and another round of DND work has landed since. These changes were informed by developer feedback on the Drag-and-Drop API. Yay for user testing!

We introduced separate GtkDropTarget and GtkDropTargetAsync event controllers, with the former being simplified to avoid all async API, which makes it very easy to handle local cases.

We also cleaned up internals of the DND implementation to group DND events into event sequences, handle them in just the same way as normal motion events,  and introduced GtkDropControllerMotion, which is an event controller that is designed to handle things like tab switching during a DND operation.

Finally, we could remove the remnants of X11-style property and selection APIs; GtkSelectionData and GdkAtom are gone.

Cleanups and fixes

As always, there’s a large number of smaller cleanups and fixes that have happened.

The biggest group of cleanups happened in the file chooser, where a number of marginally useful APIs (extra widgets, overwrite confirmation, :local-only, GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER, etc) have been dropped. To make up for it, the portal implementation of the native file chooser supports selecting folders now.

Another big cleanup was that GdkEvent is now an immutable boxed type. This was mainly an internal cleanup; the effect on application-level APIs is small, since event controllers have replaced direct event handling for the most part.

One new such event controller is GdkEventControllerFocus, which was split of from the key event controller to provide just focus handling.

GtkMenuButton lost its ability to have mnemonics when it was turned from a GtkButton subclass into a plain widget. This functionality has been reinstated, with a :use-underline property.

The HighContrast and HighContrastInverse themes that are included in GTK are now derived from Adwaita, for a much reduced maintainance burden and improved quality. Trying these themes out in gtk4-widget-factory is now easier, since we added a style menu.

The new HighContrast theme has also been backported to GTK 3.

Whats ahead

We will continue our snapshots and hope to get more developer feedback on the new APIs and features described above.

Here are things that we still want to integrate before GTK 4:

  • Row-recycling list and grid views
  • Revamped accessibility infrastructure
  • Animation API

If you want to follow the GTK 4 work, go here.

GTK 3.98

A few days ago, I’ve released a GTK 3.98 tarball. This is another step towards GTK 4. It is a little bit behind schedule, and does not quite include all the things we wanted to get into it, but it gets a lot closer to what we want to ship in GTK 4.

Almost 9 months have passed since the 3.96 snapshot, so there are quite a few new things to look at. Too many to cover them all, but here are some highlights:

Performance

The GL renderer has seen a steady flow of optimizations and performance improvements.

After the Westcoast hackfest last year, the GtkTextView scrolling performance has been greatly improved by making it cache render nodes for the visible range. At the same hackfest, the text caret blinking was changed to be smoothly animated, which is not relevant for performance at all, but looks cool.

Since the new year, a big focus has been on improving the performance of the CSS machinery. The CSS value implementation has been optimized to avoid computing values whenever possible. CSS lookups are using a Bloom filter now. And the IO for icon loading has been moved to a thread.

Most of the recent work was made possible by the sysprof profiling support that was added after the performance hackfest, and has recently been enhanced to report more information. To use it, simply start a GTK application with GTK_TRACE=1 in the environment, and load the resulting syscap file with sysprof.

DND

The DND refactoring has been completed. The GTK API for DND has been turned into event controllers: GtkDragSource and GtkDropTarget. Support for file transfers via file transfer portal has been added for both DND and the clipboard.  The underlying new infrastructure for data transfers has been covered in detail before.

GDK

The move of GDK towards Wayland concepts is continuing. This cleanup is not 100% complete yet.

Child surfaces have been removed. GDK only supports toplevel and popup surfaces now. The client-side window implementation has been removed too. Global positions and related APIs such as gdk_surface_move() are no longer available.

Grabs are no longer exposed as API. As a replacement, popup surfaces can be configured to hide on outside clicks.

XI2 is now mandatory when building the X11 backend, and support for the xim input method has been removed in favor of IBus.

The Wayland backend no longer relies on libwayland-cursor to load cursor themes, and loads individual cursors on demand.

GTK removals

Many classes have been made explicitly non-subclassable, and the widget hierarchy has been simplified, by making widgets derive directly from GtkWidget where possible.

GtkMenu, GtkMenuBar, GtkToolbar and related classes have been removed. They are being replaced by GMenu and popover-based variants. Popover menus can now do traditional, nested menus, and also show accelerators.

Context menus are no longer created with ::populate-popup signals, but also use menu models and actions. Creating those actions has been made easier with APIs like gtk_widget_class_install_action(), to create them in class_init.

GtkGestureMultiPress has been renamed to GtkGestureClick, to make it more obvious what this event controller is for.

GTK additions

We did not just remove things. Some new things have been added too.

The GtkNative interface has been introduced for widgets that have their own surface. This has been split off from the GtkRoot interface, which is exclusively for toplevel widgets without a parent.

A constraint-based layout manager has been added. It would be great to see people try this out. Please give us feedback if you do.

GtkTextView and other text widgets have gained a simple undo stack that can be used with Ctrl-Z.

The Emoji chooser widget has been made public.

Whats ahead

After 3.98, I’m planning to do more frequent snapshots, as the remaining outstanding items are landing. What are those items, you are asking ?

Here are the things that we still want to integrate before GTK 4:

– Event controllers for keyboard shortcuts
– Movable popovers
– Row-recycling list and grid views
– Revamped accessibility infrastructure
– Animation API

 

 

Data transfer in GTK4

The traditional methods for user-initiated data transfers between desktop apps are the clipboard or Drag-and-Drop. GTK+ has supported these methods since the beginning of time, but up until GTK3, the APIs we had for this kind of data transfer were thinly disguised copies of the corresponding X11 apis: selections, and properties and atoms. This is not too surprising, since the entire GDK api was modeled on X11. Unfortunately, the implementation includes horrors such as incremental transfers and string format conversions.

For GTK4, we’re leaving these things behind as we are moving things in GDK around to be closer to the Wayland API. Data transfer is one the areas in most urgent need of this modernization. Thankfully, it is almost complete at this point, so it is worth taking a look at what has changed, and how things will work in the future.

Concepts

If the data your application wants to send is not a string, it is probably some object, such as GFile, a GdkTexture, or a GdkRGBA. The app on the receiving side may be not use GTK or GLib, and thus won’t know these types. And even if it does, there’s no way to get the objects from one process to the other in one piece.

At the core, the data transfer works be sending a file descriptor from the source app, and the target app reading a stream of bytes from it. The protocols for clipboard and DND use mime types such as text/uri-list, image/png or application/x-color to identify the format of the byte stream.

Sending an object involves negotiating a data format that both sides support, serializing the object on the source side into a byte stream of that format, transferring the data, and deserializing the object on the target side.

The local case

Before moving on to concrete APIs, it is worth pausing a moment to consider another common case of data transfer: inside a single application. It is quite common to use the same clipboard and DND mechanisms to send data from the one side of the application to the other. Since we are not crossing process boundaries in this case, we can avoid the byte stream and the associated serialization and deserialization overhead, and transfer the data by just passing a reference to the object.

APIs

The objects we’ve alluded to in the previous section are described by GTypes such as G_TYPE_FILE or GDK_TYPE_TEXTURE, while, as mentioned, the data exchange formats in Wayland and X11 protocols are described by mime types.

The first API we have introduced to deal with these types is the GdkContentFormats object. It can contain a list of formats, which may be GTypes or mime types. We use GdkContentFormats objects to describe the formats that an application can offer data in, as well as the formats that an application can receive data it.

You may wonder why we are mixing GTypes and mime types in the same object. The answer is that we want to handle both the cross-process and the local case with the same APIs. And while we need to match mime types for the cross-process case, we need the GTypes for the local case.

Conversion

We still need a way to associate GTypes and mime types that we can convert into each other. This is handled by the GdkContentSerializer and GdkContentDeserializer APIs. These are essentially registries of conversion functions: GdkContentSerializer knows how to convert GTypes into mime types, and GdkContentDeserializer handles the other direction.

GDK has predefined conversions for common types, but the system is extensible using gdk_content_register_serializer and gdk_content_register_deserializer.

Content

Now we know how to describe formats and convert between them, but to  put this all together, we still need a convenient api that takes an object on the one side, and provides a bytes stream on the other.  For this, we added the GdkContentProvider API.

A GdkContentProvider lets you combine an object with a format description on the input side, and provides an async writer API on the output side that can be connected to the file descriptor that we want to send the data over.

Typical content providers are created like this:

gdk_content_provider_new_for_value (gvalue)
gdk_content_provider_new_for_bytes (gbytes, mimetype)

The GValue that it takes contains both an object and information about its type, so we don’t need extra type information, if we provide the object as  a GBytes (essentially a just a bit of memory), we need to provide the type information separately.

The Clipboard

GTK3 has a GtkClipboard object, which provides the implemention for copy/paste operations. Having this object in GTK is not ideal, since it needs different implementations on the platforms that are supported by GTK. Therefore, GTK4 moves the object to GDK, and consequently renames it to GdkClipboard. It has also been ported to the new data transfer apis that are described above. To put data on the clipboard in GTK4, you use one of the ‘set’ apis:

gdk_clipboard_set_content()
gdk_clipboard_set_value()
gdk_clipboard_set_text()

Ultimately, all of these functions end up associating a GdkContentProvider with the clipboard.

To read data from the clipboard in GTK4, you use one of the async ‘read’ apis:

gdk_clipboard_read_async()
gdk_clipboard_read_value_async()
gdk_clipboard_read_text_async()

Drag-and-Drop

The GTK3 Drag-and-Drop api involves listening to a number of signals on GtkWidget, and calling a few special setup functions for drag sources and destinations. It is flexible, but generally considered confusing, and we won’t describe it in detail here.

In GTK4,  the Drag-and-Drop api has been reorganized around the concepts of content providers and event controllers. To initiate a Drag-and-Drop operation, you create a GtkDragSource event controller that reacts to drag gestures (you can also start ‘one-off’ Drag-and-Drop operations by just calling gdk_drag_begin yourself), and you give it a GdkContentProvider for the data that you want to transfer. To receive Drag-and-Drop operations, you create a GtkDropTarget event controller and call an async read method when it emits the ::drop-done signal:

gdk_drop_read_value_async()
gdk_drop_read_text_async()

GTK BoF at Guadec

As every year, we had a GTK BoF at Guadec in Thessaloniki. This year, we had a pretty good turnout — everybody was interested in GTK4 plans.

But since Emmanuele was busy in the morning, we started the discussion with some other topics.

GLib

We collected a few suggestions for useful GLib additions from the room.

  • API for OS information (basically, the data that is in /etc/os-release). This seemed uncontrolversial; Robert is going to implement it
  • An ordered map. This is implemented ad-hoc in many places by combining a hash table with a list or array.  There seemed to be agreement that it would be worthwhile to provide this in GLib, if somebody does the work to propose an API

The discussion of ordered maps also touched on generic container interfaces; Philipp described how that could be done, see details here.

Still on the topic of containers, Alex described a problem with transfer annotations. We discussed various ideas, but there may not be a perfect solution.

Matthias pointed out that there is still some Unicode data in Pango. We briefly discussed how nice it would be to have an agreed-on, mmappable binary format for Unicode data, so that everybody could share it. Short of that, moving the last bits of data to GLib was uncontroversial.

Dark mode

Since the “dark mode” BoF joined us, we switched to discussing dark mode next. There was a more discussion of this topic in the vendor theme BoF the next day; the GTK discussion focused on technical details of how to implement dark mode.

There are various options:

  • Add extra metadata to theme index files to mark themes as dark
  • Add a “dark-theme-name” setting and treat dark and light themes as independent
  • Keep the existing convention of appending “-dark” to theme names to find the dark variant of a theme

The pragmatic solution of keeping the existing convention seemed to have support in the room. Matthias started exploring some application support APIs here.

GTK

Eventually we swiched to talking about the state of and progress towards GTK4. The high-level summary is that there is still a list of  features that need to be completed for GTK4:

  • A scalable list view that recycles row widgets. This includes a broader switch to using list models in more places. To make it complete, it should also include a grid view using the technologies. Benjamin is working on this
  • Infrastructure and APIs for animations. This will be similar to the way animations work in CSS, and part of the work is to port not just our existing CSS animation support, but also stack switching animations, the revealer, progress bars and spinners to the new framework. Emmanuele is working on this.
  • Complete the menu/popover rework. Some people tried the new popover menubar. The feedback was that we should probably go back to nesting submenus, at least for menubars, and push forward with dropping menus, since some of the ways in which menus are special (such as keep-up triangles, scrolling) are hard to keep working (or keep working well). Matthias is getting back to working on this after Guadec.
  • Shortcuts – replace mnemonics, accelerators, and key bindings with event controllers. There is a fairly complete branch with code and APIs that several people have worked on; help with reviewing and testing it would be appreciated.
  • The new Drag-and-Drop API needs to be completed.

The good news is that this list is fairly short and has names next to most items. The bad news is that each item is a considerable amount of work. Therefore, it is not a good idea to promise a tight timeline towards the 4.0 release before we have all of them merged. Thus, the following is tentative, but (we hope) somewhat realistic:

  • another GTK 3.9x snapshot before the end of this year
  • a feature-complete 3.99 release around the same time as GNOME 3.36 in spring 2020
  • a 4.0 release around the same time as GNOME 3.38 in fall  2020

Inevitably, we also discussed other things that would be nice to have. None of these are on the GTK4 roadmap; but if somebody shows up to do the work, they can happen:

  • A “widget repository” or “hig” library in order to not overload GTK with too specific or experimental widgets
  • A “UI designer” widget. This could live in a separate library as well
  • Better support for split headerbars and state transitions

We also discussed things outside GTK proper that will keep applications from porting to GTK4. This includes commonly used libraries such as GtkSourceView, vte and webkitgtk, which all will need GTK4 ports before applications that depend on them can be ported. Some of this work is already underway; but any help in this area is appreciated!

Another potential blocker for GTK4 porting is platform support. The GL renderer works well on Linux; the Vulkan renderer needs some fixups. On Windows we currently use cairo fallback, which may be good enough for 4.0. Alternatively, we could merge existing work for using the GL renderer with ANGLE. The situation is less pleasant on OS X, where we don’t have a working backend; if you want to help us here, the first still would be to adapt the GDK backend to changes in GDK.

Hacking time

In the afternoon, the room drifted from discussion to hacking, and various GTK-related works-in-progress could be spotted on peoples laptops: work to speed up GtkBuilder template loading, nested popover menus, a half-finished GtkSourceView port.

You will hopefully see these (and others) in GTK master soon.

GTK 3.96.0

This week, we released GTK 3.96.0. Again, it has been a while since the last release, so it is worth summarizing whats new in this release. There is really too much here to cover it all, so this post will only highlight the most important changes.

This release is another milestone on our way towards GTK 4. And while there are still some unfinished things, this release is much closer to we hope to achieve with GTK 4.

GSK

GSK has seen a number of bug fixes and new tests that are made much easier using a new debug tool, gtk4-node-editor. It can load and display serialized render node trees, such as this one that was saved from the GTK inspector, and compare the output of different renderers.

The 3D transformation support has been brought up to the level where we can do animated transitions like the cube spin below.

GDK

The trend to move toward Wayland inspired APIs has continued, with more X11-only apis being moved to the X11 backend or just removed. Use of child surfaces and global coordinates has been greatly reduced, but this work remains incomplete.

The refactoring of Drag-and-Drop has also continued, with the introduction of GdkDrag and GdkDrop objects. The GTK part of this refactoring is still incomplete.

Events have been simplified and are now used just for input. Other event have been replaced by signals and properties on GdkSurface. In detail, expose events have been replaced by the ::render signal, configure events have been replaced by the ::size-changed signal. Map events have been replaced by the :mapped property, and gdk_event_handler_set() has been replaced by the ::event signal.

The Wayland backend has gained support for the Settings portal for GtkSettings, and uses the text-input-unstable-v3 protocol for its input method support.

GTK

Widgets

One big change for custom widgets is the introduction of GtkLayoutManager, which is a new delegate object that takes over size allocation. Layout managers can optionally use layout children for holding layout properties. This replaces the layout-related child properties in GTK containers such as GtkBox or GtkGrid.

A number of layout managers are available:

  • GtkBinLayout, for simple single-child containers
  • GtkBoxLayout, for children that are arranged linearly
  • GtkGridLayout, for children that are arranged in a grid
  • GtkFixedLayout, for freely positioned and transformed children
  • GtkCustomLayout, as a quick way to turn traditional measure and size_allocate vfuncs into a layout manager

More layout manager implementations will appear in the future. Most prominently, work is underway on a constraints-based layout manager.

GtkAssistant, GtkStack and GtkNotebook have publicly
accessible page objects for their children. The page objects
are also exposed via a list model. They non-layout related child properties of these containers have been converted into regular properties on these page objects.

Since all existing child properties have been converted to regular properties, moved to layout properties or moved to such page objects, support for child properties has been dropped from GtkContainer.

The core GtkEntry functionality has been moved into a new GtkText widget, which also implements an expanded GtkEditable interface. All existing entry subclasses in GTK have been turned into GtkEditable implementations wrapping a GtkText widget. This also includes a new GtkPasswordEntry.

Other Changes

GTK widgets can transform their children using projective linear
transformations. This functionality is available in CSS and
as a GskTransform argument to gtk_widget_allocate. GtkFixed is
the first container that exposes this functionality. For further examples,
see the swing transition of GtkRevealer, the rotate transitions
of GtkStack or the Fixed Layout example in gtk4-demo.

A number of list models have been introduced, for internal use
and as public API: GtkMapListModel, GtkSliceListModel, GtkSortListModel, GtkSelectionModel, GtkSingleSelection. These will become more widely used when we introduce a list model-based GtkListView.

GtkBuilder can specify object-valued properties inline, instead of referring to them by ID, and the simplify command of gtk4-builder-tool has gained an option to automatically convert GTK 3 UI definition files to GTK 4.

Coming soon

For more information on the things that are still still coming for GTK 4, find us on Discourse, IRC, or look here.

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.

Theme changes, revisited

A quick update on last weeks post about theme changes:

We’ve made a 3.24.4 release, to fix up a few oversights in 3.24.3. This release does not include the new theme yet, we will push that to the next release.

We’ve also made another NewAdwaita tarball, which includes refinements based on some of the suggestions we received since last week.

Try it out, and tell us about it!

Theme changes in GTK 3

Adwaita has been the default GTK+ theme for quite a while now (on all platforms). It has served us well, but Adwaita hasn’t seen major updates in some time, and there is a desire to give it a refresh.

Updating Adwaita is a challenge, since most GTK applications are using the stable 3.x series, and some of them include Adwaita-compatible theming for their own custom widgets. Given the stable nature of this release series, we don’t want to cause theme compatibility issues for applications. At the same time, 3.x is the main GTK version in use today, and we want to ensure that GTK applications don’t feel stale or old fashioned.

A trial

A number of approaches to this problem have been considered and discussed. Out of these, a tentative plan has been put forward to trial a limited set of theme changes, with the possibility of including them in a future GTK 3 release.

Our hope is that, due to the limited nature of the theme changes, they shouldn’t cause issues for applications. However, we don’t want to put our faith in hope alone. Therefore, the next three weeks are being designated as a testing and consultation period, and if things go well, we hope to merge the theme into the GTK 3.24.4 release.

It should be emphasised that these changes are confined to Adwaita itself. GTK’s CSS selectors and classes have not been changed since GTK 3.22, and the changes in Adwaita won’t impact other GTK themes.

The Adwaita updated theme is being made available as a separate tarball in parallel with the GTK 3.24.3 release, and can be downloaded here. GTK application developers are invited to try 3.24.3 along with the new version of Adwaita, and report any issues that they encounter. The GTK team and Adwaita authors will also be conducting their own tests. Details of how to test the new theme in various ways are described here.

We are hoping to strike a balance between GTK’s stability promises on the one hand, and the desire to provide up-to-date applications on the other. It is a delicate balance to get right and we are keen to engage with GTK users as part of this process!

Theme changes

The rest of this post summarises which changes are have been made to the theme. This will hopefully demonstrate the limited extent of these changes. It will also help developers know what to look for when testing.

Colors

Many of the Adwaita colors have been very slightly tweaked. The new colors are more vivid than the previous versions, and so give Adwaita more energy and vibrancy. The new colors also form part of a more extensive palette, which is being used for application icons. These colours can also be used in custom application styling.

The color changes are subtle, so any compatibility issues between the new and the old versions should not be serious. Blue is still blue (just a slightly different shade!) Red is still red. Visually, the dark and light versions of the theme remain largely the same.

Adwaita’s dark variant, showing the slight color changes between old (left) and new (right).

Note that the red of the button has been toned down a bit in the dark theme.

Header bars and buttons

Most widgets have not been specifically changed in the updated version of Adwaita. However, two places where there are widget-specific changes are header bars and buttons. In both cases, an effort has been made to be lighter and more elegant.

Buttons have had their solid borders replaced with shadows. Their background is also flatter and their corners are more rounded. Their shape has also been changed very slightly.

Header bars have been updated to complement the button changes. This has primarily been done by darkening their background, in order to give buttons sufficient contrast. The contrast between header bars’ focused and unfocused states has also been increased. This makes it easier for users to identify the focused window.

At first glance, these changes are some of the most significant, but they are achieved with some quite minor code changes.

The header bar in GNOME’s Calendar app (old version on top, new version on the bottom):

Switches

Aside from header bars and buttons, the only other widget to be changed is switches. When GTK first introduced switches, they were a fairly new concept on the desktop. For this reason, they included explicit “ON” and “OFF” labels, in order to communicate how the switches operated. Since then, switch widgets have become ubiquitous, and users have become familiar with switches that don’t contain labels.

The latest Adwaita changes bring the theme into line with other platforms and make switches more compact and modern in appearance, by removing the labels and introducing a more rounded shape.

Elsewhere, no change

Aside from the changes described above, very little has changed in Adwaita. The vast majority of widgets remain the same, albeit with very slightly altered colours. Generally, UI layouts shouldn’t alter and users should feel comfortable with the changes.

Spot the difference (the old version of Adwaita is on the left and the new version is on the right):

Conclusion

Please try the new theme. We hope you like it!

And we appreciate your feedback—in particular if you are a GTK application developer. You can provide it on irc (in the #gtk+ channel on GimpNet) or via the gtk-devel-list mailing list, or by filing an issue in gitlab.