RFC: Proposed change for extension preferences

For GNOME 42, we want to adopt libadwaita for extension’s prefs dialogs so that we draw in the correct platform stylesheet and dark-mode support.

I opened a merge request for that earlier, but there are some bits that deserve some wider discussion:

  • dialogs are based on AdwPreferencesWindow instead of plain windows; that provides a convenient and standardized API for multi-page preferences, but also complicates some things extensions may want to do (namely put random things in the headerbar)

  • there’s a new alternative to the existing buildPrefsWidget() API that gives extensions full access to the dialog: fillPrefsWindow(). I belief that approach is better than the old pre-CSD one, but I’m not too excited about the name (bikeshed ahead!)



As one of the complainers that it would be nice if I could use an AdwPreferencesWindow, I’m of course in favor of this ; )

However, it’s true that for extensions that currently heavily customize the headerbar, that may be a drawback. It must be noted that by having access to the AdwPreferencesWindow, it’s possible to hack a way in the widget tree to get access to the headerbar and put widgets there. I must stress this is pretty much unsupported though and will break if libadwaita changes the hierarchy of the window.

There’s also some concerns for extensions that have a very specific layout (I’m thinking for example of Fly-Pie), because the window has margins and uses an AdwClamp, they can’t use the full available size. They can still wipe the window clean with set_content() though, but again I’m not sure that’s supported, as the AdwPreferencesWindow may have expectations about its children (I haven’t checked the source code).

I think it would nice to review the most popular extensions’ preferences window to get a sense of what difficulties they might face and which path they could follow forward.

In the case of my extension, I’m putting a view switcher in the headerbar that will be replaced by the built-in one and a button, so I have no big concerns.

I think it looks really good! I like that it even avoids adding the error page unless a construct error occurs.

These are both good points, though I think the only full solution to that is allowing extensions to return a complete top-level widget.

On the other hand, Adwaita is quite new and shiny so we might find people eager to adapt to Adwaita-friendly UX and lose interest in (most) custom widgetry and so on.

I am pretty happy with this suggestion. In fact, I asked on the extensions matrix channel back in July whether there is a way to use libadwaita for the extension preferences! Here are some thoughts (and minor concerns) from the Fly-Pie perspective:

  • Fly-Pie uses the HeaderBar primarily to show a custom view switcher, so I would be happy if that was created automatically. The only additional thing in Fly-Pie’s HeaderBar is an about-button. I would have to find a way of putting this somewhere else if it is not possible to put this in the HeaderBar anymore.
  • The layout of the tutorial page and achievements page of Fly-Pie’s preferences dialog would definitely benefit from using libadwaita.
  • The appearance settings page and the menu editor page would need some work. Or I remove the padding of the AdwClamp (setting the thresholds to really high numbers combined with clamp > * { margin:0px; } would be sufficient, I guess).
  • Currently, Fly-Pie uses one big ui file for the preferences widget (plus some template files). Would it be possible to continue this or would I need a ui file for each page? If I understand it correctly, that’s what fillPrefsWindow() is made for?

Currently, Fly-Pie supports 3.36, 3.38, 40, and 41. This means that I have to actually maintain two sets of ui files for the settings dialog (GTK3 and GTK4, each more than 5k LoC) + quite a lot of spaghetti-code which works for both GTK versions. If I now have to use libadwaita for GNOME 42, this would result in a third set of ui files. While this is possible, it’ll make the addition of new features to Fly-Pie pretty cumbersome!

As said before, I support this step as it will lead to much more consistent preferences dialogs of GNOME Shell extensions, however I would kindly ask you to reduce the number of such breaking changes to a minimum in the future :wink: (or change as much as possible at once, if this change to libadwaita would have been possible together with the transition from GTK3 to GTK4, it would have been much better)! I assume that many extension developers will simply drop support for GNOME Shell versions <42 which will negatively impact the experience of many users…

That was what my original MR did, but, as Florian pointed out, it was quite messy.

fillPrefsWindow() will give you the AdwPreferencesWindow, you then have to use add() on it for each page of the preferences. It’s possible to use one UI file if you don’t use templates by getting the pages by their IDs.

That’s what I will do indeed, and in fact what I’ve been doing since GNOME 40. However, I don’t think it negatively impacts the users: nothing changes for them, it’s just that they don’t get the newest changes until they update, the extension continuing working on older versions exactly the same. I have to admit that it’s easier for me since my extension is pretty much feature-complete so most updates are just optimizations, new translations or improved preferences UI.

For entirely feature-complete extensions this is true. However if new features are added, bugs are fixed, or translations are added, users would be more happy if they could actually use the features they see in some release announcement :slight_smile:.

Another thing is that you also drastically limit the amount of potential contributors: If users could not benefit from their own contributions because their version of GNOME Shell is not supported anymore, they will not contribute. When Fly-Pie 7 was released (which did not support GNOME 3.3x), I received messages from people who wanted to contribute translations but were not able to as their system was not supported anymore.

He he, I figured :grinning_face_with_smiling_eyes:

it’s possible to hack a way in the widget tree to get access to the headerbar and put widgets there. I must stress this is pretty much unsupported though and will break if libadwaita changes the hierarchy of the window.

Right. There’s always a way, but it’s not necessarily a bad thing to nudge extension authors towards something more standardized.

Yes, but I’d like that bit to stay under control of the code that shows and attaches the window and handles errors.

The choice is between a plain window like now (but with access to the headerbar) or the dedicated adwaita API. Focusing on simple one-page prefs and multi-page prefs that follow the (new) standard pattern is a legitimate choice in my opinion.

No, sorry. It’s not really a breaking change in the sense that extensions that stick to the existing API and simply return a widget from buildPrefsWidget() will almost(*) certainly continue to work with no modifications.

If you are poking around internals to get access to the headerbar/window, then you have to be prepared that those internals may change. The alternative isn’t an API guarantee, but an indefinite code freeze.

(*) AdwPreferencesPage has built-in scrolling which may conflict with scrolling inside the widget. I handle the case where buildPrefsWidget() returns a GtkScrolledWindow, but scrolling somewhere down the hierarchy may be affected.

I agree

There is an easier way, if you don’t mind the nuclear option. You simply return a GtkLabel from buildPrefsWidget(), spawn a new subprocess/window and then close the original. GSConnect has been doing this for several years (and still use Gtk3).

It’s true that it’s not a breaking change in that sense. However, I believe that the goal of this change is to encourage extension developers to actually use widgets of libadwaita (else there won’t be much standardization). And if extension developers use new widgets, their code will no longer be backwards compatible and therefore they will most likely drop support for older versions. And this is - in my humble opinion - a rather bad thing (as I said above: More buggy extensions, less contributors, etc.). In this sense it is a somewhat breaking change. And I think that this can happen once in a while, but shouldn’t happen too often.

Do not suggest something like that to me! :wink:

1 Like

I think there are almost 6-10 extensions using CSD in prefs (my extension included). Currently extensions are using window for:

  • Resizing window (More than 20 extensions).
  • Adding GtkStackPage to the CSD (Almost 5 extensions).
  • Adding Menu button to the CSD for linking to the git page (Almost 5 extensions), donate (Almost 4 extensions) and about dialog (2 extensions).
  • Adding search button to the CSD (1 extension).
  • Adding popup menu to the CSD (Almost 3 extensions).

I think the MR is good since extensions can use both buildPrefsWidget() and fillPrefsWindow() functions in prefs.js while the fillPrefsWindow() has the priority.

1 Like

that seems to be a common use of the headerbar, and imho most extension prefs written that way would benefit from switching to a GtkNotebook instead. That’s what a lot of (other) extensions use to do multi-page preferences; it works well and keeps the headerbar clear.


But I thought AdwPreferencesWindow does include a view switcher in the headerbar automatically?


That is correct.

(Posts have a minimum character count of 20, so lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.)

1 Like

Right, sorry, I meant for extension authors currently customizing the headerbar, if they wanted to make their prefs code “AdwPreferencesWindow-ready” without losing backwards compatibility. Assuming a GtkNotebook will still work as the preferences content for an AdwPreferencesWindow, then it’s a means of writing multi-page prefs interfaces without either headerbar-widget access or AdwPreferencesWindow being a requirement.

But if you’re building new/rewritten prefs code that targets AdwPreferencesWindow, and you don’t need backwards compatibility, then by all means take advantage of the automatic switching.