4

In our Qt-based C++ application, I'm trying to automatically switch application styles based on whether the user has configured dark or bright theme.

I have figured out the notification of when a change happens (see below, for other's reference).

My main problem is the reliable detection of whether currently a dark or bright theme is used on linux (for windows see below); on XFCE, the check for QPalette color roles as mentioned in this answer works, but this does not work on Gnome and KDE Plasma for me (tested under both Ubuntu 22.04 and Fedora 36, my app built against Qt versions 6.5beta2 and 6.4.2, respectively); there the colors still seem to be taken from what I've set as XFCE theme on the same machine (and when starting xfce4-appearance-settings and changing the theme there, my app picks up the change). I would however like to adapt to the current desktop's dark mode setting.

So, my question is: How do I reliably detect application dark mode of the currently used desktop on Qt? I'm not averse to implementing a little custom platform-specific code if nothing is available directly in Qt, but it would be great if it would work without using additional libraries.

A note I saw for QApplication::setPalette I thought might be relevant here, namely "Some styles do not use the palette for all drawing, for instance, if they make use of native theme engines.", what are these all about? I did not see a link to a documentation for this feature, and a quick search for the term "qt native theme engine" also didn't seem to yield any useful results.

Since on Linux, some events are reliably triggered whenever a system theme change happens (see below), I suppose Qt can detect the theme change, it just doesn't expose data about it publicly?

Getting notified of theme changes

  • On Linux: via listening for QEvent::StyleChange events of the application's QMainWindow; two caveats and one side note:

    • That event does, despite QWidget::changeEvent documentation explicitly mentioning it, not trigger a changeEvent (for me), but has to be caught via the more generic QWidget::event
    • StyleChange only seems to be triggered since some Qt 6.4 version (in my tests, it was not triggered by 5.15.2 and 6.3.1, but triggered for 6.4.2 and 6.5.0beta2).
    • I've also noticed that there's a ThemeChange event that is also triggered (at the same time as the StyleChange; not sure what the difference is between these two though, and in what case one would be triggered but not the other... I suppose StyleChange is used for any change to the style of a widget, so that it's also called when applying some style sheet settings, while ThemeChange really indicates a change of the system theme? Though ThemeChange seems to be considered a non-public Event type, at least it doesn't appear in the documentation (marked \omitvalue)
  • On Windows, via checking for changes to the registry key HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize (the StyleChange unfortunately isn't triggered there at all - a Qt bug?). .

Detecting dark theme on Windows

  • Application bright/dark mode determined by registry key HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize\AppsUseLightTheme (1 for bright, 0 for dark). This check is also done in Qt's plugins/platforms/windows/qwindowstheme (queryDarkMode`), but I think this is not exposed publicly as generic interface anywhere?
codeling
  • 11,056
  • 4
  • 42
  • 71
  • 1
    Concerning _Some styles do not use the palette for all drawing, for instance, if they make use of native theme engines._ This is something I noticed on Windows. The selection of `QAbstractItemView` is shown in light blue like e.g. in the Windows File Explorer. However, if I retrieve selection color from `QPalette` I get a much darker blue which is definitely not the selection color used in rendering. :-( – Scheff's Cat Feb 15 '23 at 11:02

2 Answers2

2

Welcome to Hell! Qtc has ignored/abandoned the desktop platform for about a decade. You are boldly going where so many others have bled.

As a result of the above you are going to have to get up close and personal with DBus. There is no one & done answer.

https://doc.qt.io/qt-5/qtdbus-index.html https://doc.qt.io/qt-5/qdbusconnectioninterface-members.html

Assuming you have a Ubuntu family VM or dev system.

qdbus

Command 'qdbus' not found, but can be installed with:

sudo apt install qtchooser

You can find a not bad answer about identifying dbus services here.

https://unix.stackexchange.com/questions/46301/a-list-of-available-d-bus-services

I don't know if the DBus classes survived into Qt 6.x. I didn't see doc for them on a quick search.

FireFox had to solve this very problem for their browser.

https://bugzilla.mozilla.org/show_bug.cgi?id=1734934

Read the full discussion before stealing some code from the patch. In particular

elementary OS is an example. But this issue is gonna be a lot more common since GNOME adopted the freedesktop colorscheme preference instead of dark GTK themes for the next version. KDE Plasma has a PR to adopt this as well.

Some GTK based desktops still cling to the GTK themes stuff. KDE pretty much has no rules. Pay attention to the discussion about libAdwaita. It changes/ed light to dark without sharing that information in any common place.

I don't know what/where Elementary is storing their settings.

Once you list the org.freedesktop.* services, there is one that will indicate light/dark and if it is set, things are supposed to adhere to that.

You can't really trust any system settings on Gnome because gnome-tweaks creates higher priority settings rather than changing the system level settings. You may get lucky and identify org.gnome.* services which will provide you a place to DBus listen/view. I didn't dive through all of the doc.

Manjaro is taking over the Linux desktop world. You need to see how Manjaro/Arch stores these values. Ubuntu has gotten too bloated and unstable for many desktop users. I switched to Manjaro a couple of years ago and only run Ubuntu in VMs for Yocto builds.

Beware QStyle based applications. https://doc.qt.io/qt-6/qstyle.html

This isn't a Qt-specific thing. Many applications set their own application style which does not follow system theme-ing. They do it for uniform support across platforms. If that statement is unclear to you, take a look at TextMaker.

https://www.softmaker.com/en/softmaker-office-textmaker

The user chooses an application theme setting at install time and the application ignores all system themes.

By "default" Qt applications were supposed to inherit the system theme. This hasn't really been maintained in the library. If it was, you could create a default QStyle object (which would inherit the system theme) and then pluck out the values you needed.

On the plus side, Mac (the recent BSD based stuff) and all Linux distros should have DBus. Somewhere in the DBus list of services should be one that identifies the current desktop (Gtk, KDE, etc.) and from there you should be able to identify what you need to look at.

Per Scheff's Cat comment Feb 15th. This QStyle thing is where you run afoul of QPalette.

https://forum.qt.io/topic/22506/solved-are-qwindowsstyles-gone-in-qt-5

They have moved somewhere in Qt 6, but there used to be a bunch of them.

https://het.as.utexas.edu/HET/Software/html/qwindowsvistastyle.html

At one point Qt really only inherited on KDE desktops. I am not certain it even does that anymore. Late in Qt 4.x they came up with all of these QStyle classes that were "close" to the "default" desktop colors at that point in time.

Tweaked themes will be a problem.

https://itsfoss.com/properly-theme-kde-plasma/

Please keep in mind that many of the themes out there are released by young developers who "thought" they followed the guide correctly.

https://develop.kde.org/docs/extend/plasma/theme/quickstart/

I know you were hoping for a one & done answer, but there isn't one. That's why you see commercial products like TextMaker go their own way.

If you do come up with a class/function that really does work on most platforms you owe it to the coding universe to post that!

user3450148
  • 851
  • 5
  • 13
  • Ha-ha, you imply with newer QML UI that resolves any easier? – Alexander V Feb 25 '23 at 18:42
  • Nooooo. Professionals don't use QML. That failed experiment is a train wreck that got hit by a plane crash and is rolling downhill towards a daycare center at noon play. – user3450148 Feb 26 '23 at 19:36
  • LOL. Not that myself biggest proponent but being paid salary for that implies you are incorrect. – Alexander V Feb 27 '23 at 03:59
  • No, being paid to do something doesn't make one a professional anymore than winning $20 shooting hoops in a park makes one a professional basketball player. Professionals are many levels above "just getting paid to do something." – user3450148 Feb 27 '23 at 12:55
  • 1
    You have no idea of how many wonderful QML apps being created now in embedded automotive scientific and many more areas. Continue being true professional then. – Alexander V Feb 27 '23 at 18:52
  • I do and it is very few. https://www.logikalsolutions.com/wordpress/information-technology/application-frameworks/ – user3450148 Feb 28 '23 at 20:13
  • Please take a look at the [net sales](https://www.qt.io/hubfs/_website/QtV2/QtV2%20Investors/2022/Annual%20Report%202022%20files/Qt-AnnualReport-2022-EN.pdf?hsLang=en) and stop the bad press. – ManuelSchneid3r Mar 13 '23 at 09:45
  • 1
    Net sales mean nothing given they exponentially raised licensing fees. Toyota banded with other automakers to create a completely OpenSource platform for automotive. The Qt/QML version of Ford Sync left customers with such a wretched experience Ford had to stop making cars. Medical device manufacturers are also dropping them due to a bug database tipping the scales near 22K. You cannot possibly create a valid Risk Analysis FDA document when the known bug database is as tall as Mt. Everest. – user3450148 Mar 13 '23 at 20:32
-1

If you want to check for dark mode simply, use this strategy:

app.palette().window().color().value()>app.palette().windowText().color().value()

if its true then its light mode and if its false then its dark mode and for qml

SystemPalette{id:mySysPalette}
Material.theme : (mySysPalette.windowText.hsvValue<mySysPalette.window.hsvValue) ? Material.Light : Material.Dark
Ebrahim Karimi
  • 732
  • 8
  • 24
  • Apparently you did not (fully) read my question - comparing the app's palette window to windowText color roles is already mentioned there (even if only shortly, with a link to an answer to another question describing it in more detail). This "solution" does however not seem to work for popular linux desktops (Gnome, KDE Plasma), as also mentioned in my question... – codeling Sep 02 '23 at 20:24
  • @codeling im using ubuntu with gnome and it works perfectly fine! – Ebrahim Karimi Sep 02 '23 at 20:31
  • I might test this again. Which version of Ubuntu/Gnome are you using? As stated above, I tested on VM's of both Fedora 36 and Ubuntu 22,04, and it didn't work back then; it might be the the parallel installation of XFCE, Gnome and Plasma sessions somehow influences this...? – codeling Sep 02 '23 at 20:53
  • @codeling Ubuntu 22.04.3 LTS with Gnome 42.9 and Qt 6.5.2. I Posted the answer here because it was my first link on google and thought maybe someone else could use it – Ebrahim Karimi Sep 02 '23 at 21:31