I’ve been experimenting with using the new content interface as a way of handling themes, and have got to a point where other people might find it interesting. The good news is that it looks like the new version of the content interface is sufficient for this use case.
For the experiment, I put together a set of snap packages whose snapcraft.yaml files can be found here:
https://gist.github.com/jhenstridge/7c1518dfb264014e28558fd03a1ed68a
… and binaries here:
Since there are no store assertions, it is necessary to connect up the content interfaces after installing the snaps:
snap connect gtk3-demo:gtk3-themes ubuntu-themes:gtk3-themes
snap connect gtk3-demo:icon-themes ubuntu-themes:icon-themes
snap connect gtk3-demo:gtk3-themes adwaita-theme:gtk3-themes
snap connect gtk3-demo:icon-themes adwaita-theme:icon-themes
snap connect gtk3-demo:gtk3-themes evopop-theme:gtk3-themes
(yes, this is connecting the plugs to multiple slots simultaneously). At this point, the gtk3-demo
snap can use any of the themes provided by the theme snaps. For example, I can launch it with the EvoPop theme:
GTK_THEME=EvoPop snap run gtk3-demo
To get this to work, I use the content interface plugs to populate $SNAP/share/themes
and $SNAP/share/icons
with the data from the themes:
$ snap run --shell gtk3-demo
...
$ ls $SNAP/share/themes
Adwaita Adwaita-dark Ambiance EvoPop EvoPop-Azure Radiance
$ ls $SNAP/share/icons
Adwaita Humanity Humanity-Dark ubuntu-mono-dark ubuntu-mono-light
If I install more themes providing the gtk3-themes
slot and connect them up to the app, they will be visible too. This has a few benefits:
- there is no need for application snaps to have knowledge of individual themes: the single plug acts as an extension point for all themes.
- since we can connect all installed theme snaps up at once, we can support multi-user systems where different users have picked different themes. There’s no need to e.g. replug all the interfaces on login.
So where do we go from here?
Good experience for default themes
Without store support, we can’t easily determine which snap provides which themes. However, we could package the default themes of major Linux distros together and make that snap the default provider for the plug on the app.
When snapd gains support for automatically installing snaps required to satisfy content plugs, it would also handle this default themes snap.
This would not preclude the packaging of additional themes as their own snaps: we just wouldn’t have a way to automatically install said snaps. This might not be so bad in the short term, since users will generally need to install additional themes manually anyway.
Developer experience
As you can see in my sample gtk3-demo snapcraft.yaml
file, I had to add extra boilerplate plug definitions to support this. It would be nice to avoid this, since it adds another opportunity to introduce errors.
I think we could improve this with a fairly simple modification to Snapcraft:
- allow
plugs
andslots
sections under each part insnapcraft.yaml
. - when constructing the
meta/snap.yaml
file, iterate through each part level plug or slot:- if a plug/slot of the same name doesn’t exist, copy it up to the top level
- if there is an existing definition, perform a deep comparison with the top level version and error out if they differ.
This would make it possible for a cloud part to introduce new plug/slot definitions into projects that use them.
What about Qt apps?
There’s two basic modes that a Qt app can run in:
- use the
libqgtk3.so
platform theme plugin - use some other platform theme plugin
Case (1) is what you’d normally see when running Qt apps on GTK based desktops (GNOME 3, Unity, Mate, Budgie, etc). Since it defers to the GTK theme, it will work just as well as any GTK app.
Case (2) is what you’d see on a KDE desktop. Instead, the theme is implemented by a shared library that needs to be provided via a Qt plugins directory hierarchy.
If we can share individual files via the content interface, we might be able to present all platform theme shared libraries in a single directory. If not, then we’d probably need to rely on desktop-launch
to construct a QT_PLUGIN_PATH
based on the directories made available by the content interface.