Libadwaita 1.3

Another cycle, another release. Let’s take a look at what’s new.

Banners

Screenshot of AdwBanner

AdwBanner is a brand new widget that replaces GtkInfoBar.

Jamie started implementing it before 1.2 was released, but we were already in the API freeze so it was delayed to this cycle instead.

While it looks more or less the same as GtkInfoBar, it’s not a direct replacement. AdwBanner has a title and optionally one button. That’s it. It does not have a close button, it cannot have multiple buttons or arbitrary children. In exchange, it’s easier to use, behaves consistently and has an adaptive layout:

Wide screenshot of AdwBanner. The button is on the right, the title is centered.

Medium-width screenshot of AdwBanner. The button is on the right, the title is left-aligned as it wouldn't fit centered.

Narrow screenshot of AdwBanner. The title is center-aligned, the button is centered below it.

Like GtkInfoBar, AdwBanner has a built-in revealer and can be shown and hidden with an animation.

There are situations where it cannot be used, but in most of those cases GtkInfobar was already the wrong choice and they should be redesigned. For example, Epiphany was using them for the “save password” prompt, and is using a popover for that now.

Tab Overview

A work-in-progress grid-based tab overview A work-in-progress carousel-based tab overview

AdwTabOverview is a new tab overview widget for AdwTabView that finally makes it possible to use tabs on mobile devices without implementing a mobile switcher manually.

Back when I wrote HdyTabView, I mentioned a tab overview widget in the works, and even had demo screenshots. Of course, basically everything from that demo got rewritten since then, and the carousel for narrow mode got scrapped completely, but now we’re getting into The Ship of Theseus territory.

This required a pretty big rework of AdwTabView to allow tabs to have thumbnails when they are not visible, and in particular it does not use a GtkStack internally anymore.

By default the selected tab has a live thumbnail and other thumbnails are static, but apps can opt into using live thumbnails for specific pages. They can also control the thumbnail alignment in case the thumbnail gets clipped. Thumbnails themselves are currently not public, but it might be interesting to use them for e.g. tooltips at some point.

Overview is not currently used very widely – it’s available in Console, and there is a merge request adding it to Epiphany, but I didn’t have the energy to finish the latter this cycle.

Tab Button

Screenshot of AdwTabButton. It's showing two tab buttons. One displays 3 open tabs, the other one 15 and has an attention indicator

AdwTabButton is much less interesting, it’s just a button that shows the number of open tabs in a given AdwTabView. It’s intended to be used as the button that opens the tab overview on mobile devices.

Unlike tab overview, this widget is more or less a direct port of what Epiphany has been using since 3.34. It does have one new feature though – it can display an indicator if a tab needs attention.

Accessibility

You might have noticed that widgets like AdwViewStack, AdwTabView or AdwEntryRow were not accessible in libadwaita 1.2.x. The reason for that is that GTK didn’t provide public API to make that possible. While GtkAccessible existed, it wasn’t possible to implement it outside GTK itself.

This cycle, Lukáš Tyrychtr has implemented the missing pieces, as well as fixed a few other issues. And so libadwaita widgets are now properly accessible.

Animation Additions

One of AdwAnimation features is that it automatically follows the system setting for disabling animations. While this is the expected behavior in most cases, there are a few where it gets in the way instead.

One of these cases is in apps where the animations are the app’s primary content, such as Elastic, and so I added a property that allows a specific animation to ignore the setting.

The animation in the demo is now using it as well, for the same reason as Elastic, and in the future it will allow us to have working spinners while animations are disabled system-wide as well.

A screenshot of an animation graph from Elastic

Elastic also draws an animation graph before running the actual animation, and while for timed animations it’s relatively easy since easing functions are available through the public API, it wasn’t possible for spring animations. It is now, via calculate_value() and calculate_velocity().

All of this uncovered a few bugs with spring animations – for example, the velocity was completely wrong for overdamped springs, and Manuel Genovés fixed them.

Unrelated to the above, a common complaint about AdwPropertyAnimationTarget was that it prints a critical if the object containing the property it’s animating is finalized before the target. While it was easy to avoid in C, it was nearly impossible from bindings. And so we don’t print that critical anymore.

Other Changes

  • Christopher Davis added a way to make AdwActionRow subtitle selectable.
  • Matt Jakeman added title-lines and subtitle-lines properties to AdwExpanderRow, matching AdwActionRow.
  • AdwEntryRow now has a grab_focus_without_selecting() method, matching GtkEntry.
  • The API to set an icon on AdwActionRow and AdwExpanderRow are now deprecated, since they were mostly unused. Apps that need icons can add a GtkImage as a prefix widget instead.
  • AdwMessageDialog now has the async choose() method, matching the new GTK dialogs like GtkAlertDialog. The response signal is still there and is not deprecated, but in some cases the new method may be more convenient, particularly from bindings:

    [GtkCallback]
    private async void clicked_cb () {
        var dialog = new Adw.MessageDialog (
            this,
            "Replace File?",
            "A file named “example.png” already exists. Do you want to replace it?"
        );
    
        dialog.add_response ("cancel", "_Cancel");
        dialog.add_response ("replace", "_Replace");
    
        dialog.set_response_appearance (
            "replace",
            DESTRUCTIVE
        );
    
        var response = yield dialog.choose (null);
    
        if (response == "replace") {
            // handle replacing
        }
    }
    
  • Corey Berla added missing drag-n-drop related API to AdwTabBar to make it work properly in Nautilus.
  • Since GTK now allows to change texture filtering, AdwAvatar properly scales custom images, so they don’t appear pixelated when downscaled or blurry when upscaled. This only works if the custom image is a GdkTexture – if your app is using a different GdkPaintable, you will need to do the equivalent change yourself.
  • Jason Francis implemented dark style and high contrast support when running on Windows.
  • Selected items in lists and grids are now using accent color instead of grey, same as Nautilus in 43. Sidebars and menus still look the same as before.

New Dependencies

The accessibility additions, scaled texture render nodes in AdwAvatar, as well as mask render nodes (that I didn’t mention because it’s an internal change) and deprecation fixes mean that libadwaita 1.3 requires GTK 4.10 instead of 4.6.


As always, thanks to all the contributors, and thanks to my employer, Purism, for letting me work on libadwaita and GTK to make this release happen.