toga icon indicating copy to clipboard operation
toga copied to clipboard

Upgrade to GTK4 and libadwaita for Linux

Open danyeaw opened this issue 11 months ago • 18 comments

What is the problem or limitation you are having?

The current implementation backend for Linux uses GTK3. While this was a great choice when Toga was first created, it is now in maintenance only mode. Apps written in GTK3 now look out of place on a new GNOME desktop and no longer have the modern feel that users are expecting when creating a new app. GTK4 and libadwaita have the following advantages:

  1. More modern looking including accent colors, window decorations, and dialogs
  2. Adaptability for different screen sizes and orientations (including Linux phones)
  3. Better performance with rendering done with render nodes and GPU offloading instead of frame rendering
  4. Event controllers
  5. Easier support for Composite Widgets

GTK4 was initially released 4 years ago, and it is in all distros at this point (even Debian stable). Libadwaita 1.0 is also everywhere, although some of the newer dialogs and widgets are supported up to version 1.6 which is the latest.

Describe the solution you'd like

Unfortunately, the GTK3 to GTK4 change is a breaking change and it would be a large project to upgrade all the Toga widgets. One approach to work on this would be to slowly transition by supporting both, and then remove the GTK3 calls once it has reached feature parity:

if Gtk.get_major_version() == 3:
    ...  # GTK3 calls
else:
    ...  # GTK4 equivalent

We could also set an environmental variable for development, and then load the right library version based on it:

import gi

gtk_version = "4.0" if os.getenv("TOGA_UPGRADE_GTK") == "4" else "3.0"

gi.require_version("Gtk", gtk_version)
gi.require_version("Gdk", gtk_version)

Describe alternatives you've considered

We could keep GTK3, maybe even theme it to make it look more modern like https://github.com/rafaelmardojai/firefox-gnome-theme.

Additional context

GTK3 Toga Hello World: image image

GTK4 and libadwaita Hello World: image image

Checklist of GTK4 Support:

  • [x] app
  • [ ] fonts
  • [ ] icons
  • [ ] images
  • [ ] keys
  • [ ] paths
  • [x] window
  • [ ] activityindicator
  • [ ] base
  • [ ] box
  • [ ] button
  • [ ] canvas
  • [ ] dateinput
  • [ ] detailedlist
  • [ ] divider
  • [ ] imageview
  • [X] label
  • [ ] multilinetextinput
  • [ ] numberinput
  • [ ] optioncontainer
  • [ ] paswordinput
  • [ ] progressbar
  • [ ] scrollcontainer
  • [ ] selection
  • [ ] slider
  • [ ] splitcontainer
  • [ ] switch
  • [ ] table
  • [ ] textinput
  • [ ] timeinput
  • [ ] tree
  • [ ] webview

danyeaw avatar Jan 03 '25 15:01 danyeaw

Completely agreed that a migration to GTK4 is desirable, and ultimately inevitable; see #1935 for some previous discussion of this topic (and potential migration paths), and #1978 for an incomplete attempt at a port. That PR hasn't been updated in over a year.

There were a couple of impediments preventing completion of that PR - see the PR comments for details, but the biggest was that we couldn't run CI with GTK4 because we were using Ubuntu 20.04 for CI testing, and there was no GTK4 libraries for Ubuntu 20.04. We've since updated to use Ubuntu 24.04, so GTK4 testing is at least plausible now - although we'd need to keep an eye on compatibility with Ubuntu 22.04, as it provides GTK 4.6, which has a number of APIs that were deprecated before GTK 4.10.

freakboy3742 avatar Jan 04 '25 03:01 freakboy3742

Wow, it looks like @MuhammadMuradG got a great start on this, nice!

As you mentioned, the timing may be right with the latest Ubuntu LTS have a pretty new version of GTK4 (4.10) and libadwaita at 1.5.0. Are we constrained to support Ubuntu 22.04 as well?

Since it is very difficult for so many changes on a branch to come home, I would recommend an incremental approach like I discussed above. If there aren't objections to trying that, I could work on a minimal example with something like the widgets needed for Hello World and their tests to be runnable with GTK3 and GTK4. I'm sure it would be pretty quick especially by cherry picking some of the work on #1978.

danyeaw avatar Jan 04 '25 22:01 danyeaw

Are we constrained to support Ubuntu 22.04 as well?

Toga needs to work on 22.04, as it is still under active maintenance for another 2 years. However, I'd be OK with that being GTK3-based support, with ongoing development focusing on GTK4, as soon as we have the testbed passing on GTK4.

Since it is very difficult for so many changes on a branch to come home, I would recommend an incremental approach like I discussed above. If there aren't objections to trying that, I could work on a minimal example with something like the widgets needed for Hello World and their tests to be runnable with GTK3 and GTK4. I'm sure it would be pretty quick especially by cherry picking some of the work on #1978.

I definitely don't have any objections to working on a GTK4 migration. It hasn't been a high personal priority, but that's mostly because of my list of high priority projects is already too long :-)

My recollection is that #1978 was pretty far along, with CI testing being the biggest sticking point. There's obviously 12 months worth of merging to be done, which won't be a small task - but I suspect the size of that merge task would be about the same as a "cherry pick changes into new branch" approach.

As for an "incremental" approach - I'm not sure I completely follow what that would involve (at least, in terms of being able to offer gtk4 as an end-user solution). Would it be plausible to run the existing code under GTK4 and have the testbed pass, just with a whole lot of deprecated API warnings which we could then tackle using an incremental approach? If that's the case - I guess a "add a GTK4 CI config and see what explodes" PR would be worthwhile.

freakboy3742 avatar Jan 05 '25 00:01 freakboy3742

Would it be plausible to run the existing code under GTK4 and have the testbed pass, just with a whole lot of deprecated API warnings which we could then tackle using an incremental approach?

No, unfortunately, GTK4 is not backwards compatible. To make use of GTK4, the app would have to be created with GTK4 and all Widgets that need changes would have to be upgraded.

So to work on this as a longer term project, we can support both with if Gtk.get_major_version() == 3 checks and an environmental variable to change which version of GTK to load. In CI, we can run tests with both versions. The benefit of this is then we don't have to big bang change everything at once, we can upgrade widgets to support GTK4 without replacing GTK3.

Longer term, we may need something like this anyway to support two separate use cases:

  1. Higher compatibility version of GTK that supports users on older LTS distros who have packaged their app using a system package
  2. The latest version of GTK that is packaged with Flatpak and integrates with the latest version of GNOME.

danyeaw avatar Jan 05 '25 14:01 danyeaw

Would it be plausible to run the existing code under GTK4 and have the testbed pass, just with a whole lot of deprecated API warnings which we could then tackle using an incremental approach?

No, unfortunately, GTK4 is not backwards compatible. To make use of GTK4, the app would have to be created with GTK4 and all Widgets that need changes would have to be upgraded.

My apologies - I was aware that GTK3 and GTK4 couldn't co-exist in the same app; my question was more about what the "minimum viable GTK4 port" would look like. How many breaking API changes do we need to accomodate (i.e., GTK3 APIs that don't exist at all in GTK4) just to get something working at all? Is there a world where "mostly GTK3 API code" runs on GTK4 (accepting that it might raise a whole lot of GTK API deprecation warnings)?

So to work on this as a longer term project, we can support both with if Gtk.get_major_version() == 3 checks and an environmental variable to change which version of GTK to load. In CI, we can run tests with both versions. The benefit of this is then we don't have to big bang change everything at once, we can upgrade widgets to support GTK4 without replacing GTK3.

Agreed. I'm envisaging a general approach that looks something like:

  1. A PR that adds a CI configuration and the minimum number of changes to allow the testbed to run at all on GTK4, with GTK3 remaining the default unless the user opts-in.
  2. A PR to get the testbed tests passing, accepting that the current GTK3-based API will raise a lot of deprecation warnings.
  3. A series of PRs updating individual widgets to remove/replace the use of APIs that were deprecated in GTK4
  4. A PR to switch the default GTK version from 3 to 4.

In practice, 1 and 2 might end up being the same PR; 3 will end up being a lot of smaller PRs; and we could merge 4 at any point that the work from 1-3 seems "viable enough" to be worth switching the default.

Longer term, we may need something like this anyway to support two separate use cases:

  1. Higher compatibility version of GTK that supports users on older LTS distros who have packaged their app using a system package
  2. The latest version of GTK that is packaged with Flatpak and integrates with the latest version of GNOME.

Given our limited resources, I'm not overly concerned about maintaining compatibility with GTK3 in the long-term. As soon as we have a viable GTK4 port, I'd be entirely comfortable saying that what we have at that point is as good as the GTK3 implementation is ever going to get, and focus all future development on GTK4. I don't think it's reasonable to require an open source project such as us to perform parallel implementation support for a GTK API that the GTK developers themselves no longer support; it's also unreasonable for a user to insist on using an old Linux version and require access to the very latest features.

My ultimate goal would be to drop GTK3 support entirely, as soon as either:

  1. There are no actively supported distros that don't ship with GTK4.10+ (That's Ubuntu 22.04, Debian Bookworm, and RHEL 8)
  2. Maintaining parallel support of GTK3 becomes especially onerous for some reason.

freakboy3742 avatar Jan 06 '25 00:01 freakboy3742

Checklist of GTK4 Support

Maybe this could be combined with this table in the public documentation?

mhsmith avatar Feb 04 '25 17:02 mhsmith

Hi @mhsmith, that table is users of Toga focused and I'm not sure that we want to even advertise that GTK4 is an available backend. What do you think?

danyeaw avatar Feb 04 '25 19:02 danyeaw

Hi @mhsmith, that table is users of Toga focused and I'm not sure that we want to even advertise that GTK4 is an available backend. What do you think?

I think advertising to users would potentially be a good thing in this case - that avoids the "Oh! I didn't know you supported GTK4! When did that happen?!" factor. We need to make it very clear that it's experimental - but we do that with Web and Textual, so there's precedent.

We probably want to wait until we've got support that is at least as complete as the Web and Textual backends, though - so, "able to run the full BeeWare Tutorial" as a benchmark. However, that bar is pretty low, and only needs layouts, Button, Label, TextInput, and dialogs. Once we've got that, I think a GTK4 column in that table would make sense.

Having the checklist on this ticket is also worthwhile, though - Github is our place of coordination for development, so having a visual summary here is also advisable IMHO.

freakboy3742 avatar Feb 04 '25 22:02 freakboy3742

I have ActivityIndicator ready to land, but will hold off on opening a PR before my other ones are merged (because if I understand correctly, I'm doing too much).

This comment is just to avoid duplicate effort; the work is at https://github.com/johnzhou721/toga/tree/gtk4ai and I'm planning to batch add more widgets to GTK4 (since I have plenty of time on my hands).

johnzhou721 avatar Jun 03 '25 23:06 johnzhou721

Hi @johnzhou721 , if you want to open 1 PR at a time for GTK4 support, I can do the reviews for you. The other core devs just need to balance their time with some other priorities.

danyeaw avatar Jun 04 '25 02:06 danyeaw

@danyeaw Nice to meet you! Will do.

johnzhou721 avatar Jun 04 '25 02:06 johnzhou721

Refs #3529.

johnzhou721 avatar Jun 04 '25 02:06 johnzhou721

Hey -- right now i see Gtk classes being used everywhere for GTK4 code. From some limited resarch, I've found that libadwaita is a simple way to make Gtk apps look native on GNOME -- it is mentioned in the title, but does Toga have plans to actually use the Adw.ApplicationWindow etc. classes instead of the raw Gtk ones? That'd mean we don't look native on anywhere except for GNOME, however.

I want to start a discussion here about how to frame the usage of Adwaita APIs as a concrete change proposal.

[sidenote: I think I've debugged the remaining issues with the TextInput PR.]

johnzhou721 avatar Nov 03 '25 23:11 johnzhou721

Hi @johnzhou721, GTK 4 provides general-purpose widgets for apps intended to run on any desktop, while platform libraries such as libadwaita (GNOME), Granite (Elementary OS), and libhelium (tauOS) provide styling and custom widgets that respect their respective platforms.

Apps built with libadwaita are intended to run best on GNOME, while GTK 4 apps that don’t come with a platform library can be run anywhere - but they also won't look very modern.

My preference is that Toga would support libadwaita - since I think Toga targeting apps that look amazing in GNOME would be better than just looking so-so on Linux.

danyeaw avatar Nov 04 '25 00:11 danyeaw

@danyeaw Ack. Also there's a Qt backend incoming and if we get that to good support we can use Qt 6 to look so-so on all Linux, achieving the goal.

Anyways -- do you think we should have a TOGA_ADWAITA flag for the Qt backend or just use Adw as a default on GTK4? In either cases I think we should wait for the TextInput stuff to settle down -- sorry for oft discussion but would you mind if I made a PR against your PR resolving the test failures? Thanks.

johnzhou721 avatar Nov 04 '25 01:11 johnzhou721

Hi @johnzhou721 - that sounds great!

danyeaw avatar Nov 04 '25 01:11 danyeaw

@danyeaw Unfortunately I do not have time to implement those changes -- just declaring I have no intentions to work on that.

johnzhou721 avatar Nov 04 '25 03:11 johnzhou721

@danyeaw Clarifying -- my message yesterday referred to TOGA_ADWAITA implementations. I've made the PR against your PR already, sorry for the confusion.

johnzhou721 avatar Nov 05 '25 02:11 johnzhou721

Okay -- I think just supporting LibAdwaita isn't really enough. Like libadwaita adds new things like Dialogs, but also provides some replacements like Adw.Spinner instead of Gtk.Spinner and Adw.Window instead of Gtk.Window.

Given that we need libadwaita to implement certain widgets, I believe we'd be better off just using libadwaita for the GTK4 backend instead of a separate flag to use LibAdwaita or not. Especially because base GTK4 widgets already look very GNOME-y anyways.

johnzhou721 avatar Nov 09 '25 18:11 johnzhou721

I'm not sure I see how that changes anything.

The reason we decided on a single Gtk backend rather than a GTK3 and GTK4 backend is that the overhead of a completely new backend is high, and the vast majority of code was going to end up being the same.

Based on what you're saying, the same situation applies here. 90% of widgets are going to be the same; a small number of widgets will be "more native" if you use the Adwaita versions. To me, that suggests that for those handful of widgets, we have a branch of logic to use the "more native" adwaita versions if they're available; with an option to explicitly opt in/out of the "upgraded" versions if necessary.

However, that also seems very much like a problem we should worry about after we've got a GTK4 backend fully implemented. Trying to solve every problem at once is a sure way to never end up with a working version of anything.

freakboy3742 avatar Nov 10 '25 00:11 freakboy3742

@freakboy3742 Problem is, some features can't be done without libadwaita, such as dialogs (https://docs.gtk.org/gtk4/class.Dialog.html is deprecated and the suggested replacement is just use Gtk.Window which is very genertic). So I'm not sure if we should offer a non-adwaita option at all, since implementing dialogs from scracth would take some time.

Anyways: Let's get the support checklist updated after #3881 (completed).

johnzhou721 avatar Nov 14 '25 03:11 johnzhou721

@freakboy3742 Problem is, some features can't be done without libadwaita, such as dialogs (https://docs.gtk.org/gtk4/class.Dialog.html is deprecated and the suggested replacement is just use Gtk.Window which is very genertic).

Ok - but we can only cook with the ingredients we've been given. If GTK4 says "use Window"... then that's the native API we should use. If it looks "generic"... well that's a GTK 4 problem.

So I'm not sure if we should offer a non-adwaita option at all, since implementing dialogs from scracth would take some time.

The question for me is whether there are any GTK desktop environments that don't use libadwaita. There's no point supporting a use case that doesn't exist, and limited value in supporting a use case that is marginal at best.

freakboy3742 avatar Nov 14 '25 04:11 freakboy3742

So I'm not sure if we should offer a non-adwaita option at all, since implementing dialogs from scracth would take some time.

The question for me is whether there are any GTK desktop environments that don't use libadwaita. There's no point supporting a use case that doesn't exist, and limited value in supporting a use case that is marginal at best.

There are a few such DEs but as @danyeaw mentioned, there's libs like Granite for Elementary OS and libhelium for tauOS -- still from some research using https://en.wikipedia.org/wiki/Category:Desktop_environments_based_on_GTK, ElementaryOS is the only major GTK4 DE, the rest are still on GTK3 (and sometimes GTK2); From a quick look at the docs, both libraries provide dialog functionality, so I believe implementing custom dialog based on Gtk.Window is a waste of time.

(Although ElementaryOS' Granite uses the deprecated Gtk.Dialog API, it's pointless to try to reimplement our own dialog functionality because they've got an issue upstream, and they're the best people to follow their own style guidelines and provide an updated Dialog class.)

So I think we should start porting stuff to use libadwaita by default (since GNOME is huge...), and track providing support for other more obscure DEs as a future issue. We can then have flags to enable/disable using other DE-specific libs.

I'll also note that we probably shouldn't remove GTK3 support for a long time -- there's still a glut of DEs based on GTK3.

johnzhou721 avatar Nov 14 '25 21:11 johnzhou721

So I think we should start porting stuff to use libadwaita by default (since GNOME is huge...), and track providing support for other more obscure DEs as a future issue. We can then have flags to enable/disable using other DE-specific libs.

That sounds like a reasonable initial approach for GTK4 support. However, we'll need to ensure that:

  1. The libadwaita libraries are listed in the dependency list
  2. The error handling if libadwaita is not available points clearly to the problem
  3. An issue is opened for "non-adwaita GTK4 support".

I'll also note that we probably shouldn't remove GTK3 support for a long time -- there's still a glut of DEs based on GTK3.

That goes without saying - if only because the backend still works, and Ubuntu 24.04 won't ever get updated versions of GTK 4.

freakboy3742 avatar Nov 16 '25 21:11 freakboy3742

@freakboy3742 I've found that there's actually raw GTK4 APIs for dialog, but... again it changed API completely in GTK 4.10. So I agree that we should still not implement the base GTK4 case since it's too much effort to adapt to the 2 GTK versions.

I plan to start on migrating things to libadwaita immediately upon I resubmit the Canvas migration code for GTK4 and when I settle down with the system-pyside6 stuff. libadwaita also have different Window APIs that we need to adapt to (adding headerbar manually is the big one). I think this gives us enough reason to scrub base GTK4 support entirely after we migrate to libadwaita for those things, since 3 versions of code is too much and the other platform libs feel similar to libadwaita as well.

johnzhou721 avatar Nov 22 '25 22:11 johnzhou721

@freakboy3742 I've found that there's actually raw GTK4 APIs for dialog, but... again it changed API completely in GTK 4.10.

At this point, there's really only 3 cases we care about:

  1. GTK 3.24 (since that's what is ships with all the distros we support)
  2. GTK 4.8 (since that's what is in Debian 12)
  3. GTK 4.10+ (since that's what is in all other distros we support)

Case (2) is already in the "raises warnings" bucket because of style handling. If there's no way to support dialogs on GTK 4.8 without a completely standalone implementation, then I'm ok bumping the hard minimum to GTK 4.10.

I plan to start on migrating things to libadwaita immediately upon I resubmit the Canvas migration code for GTK4

I'm OK if "v1" of GTK4 support targets libadwaita, as long as we don't make any decisions that preclude supporting non-libadwaita options in future.

freakboy3742 avatar Nov 23 '25 22:11 freakboy3742

@freakboy3742 Then let's bump the minimum version to GTK 4.10+ (as part of the Dialog PR) and do a plain GTK dialog and a libadwaita dialog, that way apps will at least look okay on other distros that does not use libadwaita but uses Gtk4 because it doesn't follow completely different idioms. That way "support other distro libs" becomes a lower priority which includes us to solve other issues like porting other widgets for now before we come back to it.

johnzhou721 avatar Nov 23 '25 23:11 johnzhou721

Ok - now I'm confused about what you're suggesting. Are you planning to support non-libadwaita distros or not in v1?

freakboy3742 avatar Nov 23 '25 23:11 freakboy3742

@freakboy3742 I'm suggesting that we should make non-libadwaita distros work in v1 by having a "GTK4 base" case, even if the result isn’t very idiomatic due to not using the distro-specific libs.

If we don’t have a “base GTK4” case, non-libadwaita distros won’t be supported at all in v1, because GNOME-specific design will look out of place there. Then, when we try to support other GTK4 distro libs, we'd need to adapt to each new one as it appears, since none of the existing options would look very native.

But if we do support a “base GTK4.10+” case, non-libadwaita distros can still have a semi-native look for dialogs etc., since the primitive GTK4 design should be more or less compatible with them. This means supporting non-libadwaita distros perfectly could take bit of a backseat.

I'm sorry if I wasn't being clear in the previous message. I'm hoping that this helps.

johnzhou721 avatar Nov 24 '25 00:11 johnzhou721

Sure - I was confused because your previous comment pushed back against the idea of having 3 branches (GTK3, base GTK4, GTK4+adwaita); and your next response suggested that very approach.

I understand not having a base GTK4 implementation means that non-adwaita desktops won't be able to use the GTK4 backend - that's why I said it would be OK for v1 to focus on adwaita-only, as long as we didn't make any decisions that precluded adding non-adwaita support in future. But if you're motivated to implement both, I won't stop you.

freakboy3742 avatar Nov 24 '25 04:11 freakboy3742