How can I correctly apply a change of the global color scheme (light/dark)?
-
Good question, I've also pondered the same.
Figuring out all the styling related stuff is complicated. We have:
- Qt style engine
- QSS styling
- QPalette
I've never really gathered how it all works together, I understand that the QStyle is basically the implementation to paint the widgets and so on. Not sure how the Palette and QSS mix together however. Rocket science where there should not be any rocket science.
Anyway Personally I've tried to something like this
https://github.com/ensisoft/detonator/blob/dev/editor/app/utility.cpp#L291
It mostly works.
-
I use this property to detect and follow OS change to light/dark. https://doc.qt.io/qt-6/qstylehints.html#colorScheme-prop
Please note restrictions listed.
-
Detecting the change is not the problem – the problem is that the new scheme is not applied everywhere.
Seems like setting a custom style sheet prevents the respective widget from updating itself automatically (e.g. for my above example, I set a style sheet for the dock widgets to make their titles a bit larger).
I haven't figured out yet what I have to do to trigger the style update …
-
Hi,
Style sheets have their own style and you are responsible for handling this use case by hand by updating your style sheet.
Depending on what you are doing with your stylesheet, you might want to implement a proxy style that does the changes you want on top of what the underlying does.
-
Thanks for this hint!
Sticking to the above example, I e.g. set
dock->setStyleSheet(QStringLiteral("QDockWidget { font-weight: bold; font-size: %1pt; }").arg( (int) double(dock->font().pointSize()) * 1.2));
to all dock widgets to make their titles larger. I didn't find any approach to achieve this using something else (QFont etc.), and the style sheets approach worked.
Would one be able to do that using such a proxy style you speak of? Or could I somehow manually set the new application-wide palette and then re-apply my style sheet? If I restart the program, the look is as expected, so applying the style sheets for the dark scheme does work as well as it does for the light one.
-
@l3u_ I grappled with this problem for a while, but eventually reached a satisfying state across Windows, macOS, KDE and Gnome. Though - I didn't use style sheets.
There are two points to this, that might be problems in your implementation.
The first is that the default handling of the
ApplicationPaletteChange
event in the application object is to propagate it to all top-level windows of the application, where each window actually resolves and updates its' palette, and then further propagates it to all of the widgets on it asPaletteChange
events.The implication of this is that ideally you'd want to do any additional touch-ups after all of that has happened. However in your application class you determine the current scheme and emit
paletteChanged
before the defaultQApplication
handling happens, so whatever is happening in connected slots is likely too soon.The second point is that for some platform integrations, changing the system-wide dark mode is not only a palette change, it could actually also be a style change. One of such platforms is exactly KDE Plasma - the light and dark variants of the Breeze theme are actually different
QStyle
implementations, and if you use the same Qt installation as the one KDE itself runs against you are actually using Plasma's platform theme plugin and styles even if you are a pure Qt app (hard to notice that sometimes, "Breeze" and "Fusion" are quite similar visually). This means that you should also be sensitive to theThemeChange
event. There is also aApplicationPaletteChange
event around typically, but the order doesn't seem to be deterministic in my experience so I just applied custom theming on both.The later point might be the problem spot with style sheets, as each stylesheet actually creates a proxy style behind the scenes for you. I'd probably try to re-apply stylesheets after one of the above events happened, but I'm not sure about it.
-
Thanks for the explanation! Actually, I don't do anything with that
paletteChanged
signal, I just put it there because I thought I would use it to update e.g. icons on buttons and such. But I didn't come so far because I noticed that most of my program wasn't changed after the color scheme change.So if setting a style sheet creates a proxy style … would simply applying it again update it, using the current (new) style?!
-
Thanks for the explanation! Actually, I don't do anything with that
paletteChanged
signal, I just put it there because I thought I would use it to update e.g. icons on buttons and such. But I didn't come so far because I noticed that most of my program wasn't changed after the color scheme change.So if setting a style sheet creates a proxy style … would simply applying it again update it, using the current (new) style?!
@l3u_ said in How can I correctly apply a change of the global color scheme (light/dark)?:
So if setting a style sheet creates a proxy style … would simply applying it again update it, using the current (new) style?!
It should, yes, but as said I have no experience with it.
-
Hey, simply adding a
setStyleSheet(styleSheet());
in the QApplication subclass's
if (event->type() == QEvent::ApplicationPaletteChange) {
clause almost does the trick :-) Only the icons and colors set depending on the initial style remain. The rest is already updated with this. Apparently, this causes all the styled widgets to update themselves.Let's see if I can get the rest to work also …