aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMohammadHossein Qanbari <[email protected]>2024-07-15 13:15:01 +0200
committerMohammadHossein Qanbari <[email protected]>2024-07-23 15:43:50 +0200
commitb45629207ee32a3d80b6ea6553e8762eec8a86da (patch)
treef48d40e017bf66b34c7c20670b7bd4bd2a133530
parentef9aa1da98411cd80d5a752839c204c2fe75afa3 (diff)
Material Style: update style theme when system theme is changed
The Material style did not use the system theme when it was set to QQuickMaterialStyle::System. The problem was that QQuickMaterialStyle does not know about system theme changes. To make this possible, it is required to implement the QtQuickControls2MaterialStylePlugin::updateTheme() method. This method overrides the QQuickStylePlugin::updateTheme() virtual method, and it is when the system theme is changed. The material style plugin has access to the QQuickMaterialTheme. Then, the material theme will be notified when the system theme is changed. The material theme did not have access to the material styles as they are attached to the QML objects in the QML engine. To address this, when a material style's theme is set to System, that material style can register itself to the list of the system styles that are stored in the material theme. When the system theme is changed, the material style plugin notifies the material theme and the material theme iterates through the material system styles and updates their themes. To prevent dangling pointer issues, the material styles list (with System theme) uses QPointer type to detect null pointers after they are destructed. They will be removed from the list when they are touched and are null. The testcase creates an ApplicationWindow with value of Material.System for Material.theme property to follow system theme changes. It then toggles the platform theme through the TestHandler and compares the results. The TestHandler class creates a mocked class called MockPlatformTheme that inherits from the QPlatformTheme class and overrides the colorScheme() and requestColorScheme() virtual functions. The reason for overriding these methods is to simulate the platform theme change event. [ChangeLog][Controls][Material] If the Material.theme is set to Material.System, the application theme changes when the system theme is changed. This also works for the child attached styles. If its theme is set to Material.System, regardless of its attached parent style, it will follow the system theme changes. Fixes: QTBUG-127169 Pick-to: 6.8 Change-Id: I29a0c59525f342595a20a908faa85bcae6615bf4 Reviewed-by: Mitch Curtis <[email protected]>
-rw-r--r--src/quickcontrols/material/qquickmaterialstyle.cpp41
-rw-r--r--src/quickcontrols/material/qquickmaterialstyle_p.h1
-rw-r--r--src/quickcontrols/material/qquickmaterialtheme.cpp56
-rw-r--r--src/quickcontrols/material/qquickmaterialtheme_p.h4
-rw-r--r--src/quickcontrols/material/qtquickcontrols2materialstyleplugin.cpp6
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt3
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml26
-rw-r--r--tests/auto/quickcontrols/qquickmaterialstyle/tst_qquickmaterialstyle.cpp71
8 files changed, 197 insertions, 11 deletions
diff --git a/src/quickcontrols/material/qquickmaterialstyle.cpp b/src/quickcontrols/material/qquickmaterialstyle.cpp
index f8ced5821b..4d8a6696c7 100644
--- a/src/quickcontrols/material/qquickmaterialstyle.cpp
+++ b/src/quickcontrols/material/qquickmaterialstyle.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickmaterialstyle_p.h"
+#include "qquickmaterialtheme_p.h"
#include <QtCore/qdebug.h>
#if QT_CONFIG(settings)
@@ -441,7 +442,8 @@ QQuickMaterialStyle::QQuickMaterialStyle(QObject *parent) : QQuickAttachedProper
m_customBackground(globalBackgroundCustom),
m_hasForeground(hasGlobalForeground),
m_hasBackground(hasGlobalBackground),
- m_theme(globalTheme),
+ m_systemTheme(globalTheme == System),
+ m_theme(effectiveTheme(globalTheme)),
m_primary(globalPrimary),
m_accent(globalAccent),
m_foreground(globalForeground),
@@ -462,14 +464,26 @@ QQuickMaterialStyle::Theme QQuickMaterialStyle::theme() const
void QQuickMaterialStyle::setTheme(Theme theme)
{
- if (theme == System)
- theme = QQuickStylePrivate::isDarkSystemTheme() ? Dark : Light;
-
m_explicitTheme = true;
- if (m_theme == theme)
+
+ // If theme is System: m_theme is set to system's theme (Dark/Light)
+ // and m_systemTheme is set to true.
+ // If theme is Dark/Light: m_theme is set to the input theme (Dark/Light)
+ // and m_systemTheme is set to false.
+ const bool systemThemeChanged = (m_systemTheme != (theme == System));
+ // Check m_theme and m_systemTheme are changed.
+ if ((m_theme == effectiveTheme(theme)) && !systemThemeChanged)
return;
- m_theme = theme;
+ m_theme = effectiveTheme(theme);
+ m_systemTheme = (theme == System);
+ if (systemThemeChanged) {
+ if (m_systemTheme)
+ QQuickMaterialTheme::registerSystemStyle(this);
+ else
+ QQuickMaterialTheme::unregisterSystemStyle(this);
+ }
+
propagateTheme();
themeChange();
if (!m_customAccent)
@@ -482,10 +496,14 @@ void QQuickMaterialStyle::setTheme(Theme theme)
void QQuickMaterialStyle::inheritTheme(Theme theme)
{
- if (m_explicitTheme || m_theme == theme)
+ const bool systemThemeChanged = (m_systemTheme != (theme == System));
+ const bool themeChanged = systemThemeChanged || (m_theme != effectiveTheme(theme));
+ if (m_explicitTheme || !themeChanged)
return;
- m_theme = theme;
+ m_theme = effectiveTheme(theme);
+ m_systemTheme = (theme == System);
+
propagateTheme();
themeChange();
if (!m_customAccent)
@@ -502,7 +520,10 @@ void QQuickMaterialStyle::propagateTheme()
for (QQuickAttachedPropertyPropagator *child : styles) {
QQuickMaterialStyle *material = qobject_cast<QQuickMaterialStyle *>(child);
if (material)
- material->inheritTheme(m_theme);
+ // m_theme is the effective theme, either Dark or Light.
+ // m_systemTheme indicates whether the theme is set by
+ // the system (true) or manually (false).
+ material->inheritTheme(m_systemTheme ? System : m_theme);
}
}
@@ -1424,7 +1445,7 @@ void QQuickMaterialStyle::initGlobals()
QByteArray themeValue = resolveSetting("QT_QUICK_CONTROLS_MATERIAL_THEME", settings, QStringLiteral("Theme"));
Theme themeEnum = toEnumValue<Theme>(themeValue, &ok);
if (ok)
- globalTheme = effectiveTheme(themeEnum);
+ globalTheme = themeEnum;
else if (!themeValue.isEmpty())
qWarning().nospace().noquote() << "Material: unknown theme value: " << themeValue;
diff --git a/src/quickcontrols/material/qquickmaterialstyle_p.h b/src/quickcontrols/material/qquickmaterialstyle_p.h
index f7c2b256ba..fd2cdfeb16 100644
--- a/src/quickcontrols/material/qquickmaterialstyle_p.h
+++ b/src/quickcontrols/material/qquickmaterialstyle_p.h
@@ -350,6 +350,7 @@ private:
bool m_hasForeground = false;
bool m_hasBackground = false;
// The actual values for this item, whether explicit, inherited or globally set.
+ bool m_systemTheme = false;
Theme m_theme = Light;
uint m_primary = 0;
uint m_accent = 0;
diff --git a/src/quickcontrols/material/qquickmaterialtheme.cpp b/src/quickcontrols/material/qquickmaterialtheme.cpp
index d121a1a47e..ed8d1e0ef9 100644
--- a/src/quickcontrols/material/qquickmaterialtheme.cpp
+++ b/src/quickcontrols/material/qquickmaterialtheme.cpp
@@ -8,9 +8,48 @@
#include <QtGui/qfont.h>
#include <QtGui/qfontdatabase.h>
#include <QtQuickTemplates2/private/qquicktheme_p.h>
-
+#include <QtCore/qmutex.h>
QT_BEGIN_NAMESPACE
+struct QQuickMaterialThemePrivate
+{
+ static inline void addSystemStyle(QPointer<QQuickMaterialStyle> style);
+ static inline void removeSystemStyle(QPointer<QQuickMaterialStyle> style);
+ static inline void updateSystemStyles();
+
+ static inline std::vector<QPointer<QQuickMaterialStyle>> systemStyles = {};
+ static inline QMutex mutex;
+};
+
+void QQuickMaterialThemePrivate::addSystemStyle(QPointer<QQuickMaterialStyle> style)
+{
+ QMutexLocker locker{&mutex};
+ auto it = std::find(systemStyles.begin(), systemStyles.end(), style);
+ if (it == systemStyles.end())
+ systemStyles.push_back(style);
+}
+
+void QQuickMaterialThemePrivate::removeSystemStyle(QPointer<QQuickMaterialStyle> style)
+{
+ QMutexLocker locker{&mutex};
+ auto it = std::find(systemStyles.begin(), systemStyles.end(), style);
+ if (it != systemStyles.end())
+ systemStyles.erase(it);
+}
+
+void QQuickMaterialThemePrivate::updateSystemStyles()
+{
+ QMutexLocker locker{&mutex};
+ for (auto it = systemStyles.begin(); it != systemStyles.end(); ) {
+ if (it->isNull()) {
+ it = systemStyles.erase(it);
+ } else {
+ (*it)->setTheme(QQuickMaterialStyle::System);
+ ++it;
+ }
+ }
+}
+
void QQuickMaterialTheme::initialize(QQuickTheme *theme)
{
QFont systemFont;
@@ -74,4 +113,19 @@ void QQuickMaterialTheme::initialize(QQuickTheme *theme)
theme->setFont(QQuickTheme::SpinBox, editorFont);
}
+void QQuickMaterialTheme::registerSystemStyle(QQuickMaterialStyle *style)
+{
+ QQuickMaterialThemePrivate::addSystemStyle(QPointer{style});
+}
+
+void QQuickMaterialTheme::unregisterSystemStyle(QQuickMaterialStyle *style)
+{
+ QQuickMaterialThemePrivate::removeSystemStyle(QPointer{style});
+}
+
+void QQuickMaterialTheme::updateTheme()
+{
+ QQuickMaterialThemePrivate::updateSystemStyles();
+}
+
QT_END_NAMESPACE
diff --git a/src/quickcontrols/material/qquickmaterialtheme_p.h b/src/quickcontrols/material/qquickmaterialtheme_p.h
index bdaecd1a87..70356967c5 100644
--- a/src/quickcontrols/material/qquickmaterialtheme_p.h
+++ b/src/quickcontrols/material/qquickmaterialtheme_p.h
@@ -20,11 +20,15 @@
QT_BEGIN_NAMESPACE
class QQuickTheme;
+class QQuickMaterialStyle;
class Q_QUICKCONTROLS2MATERIAL_EXPORT QQuickMaterialTheme
{
public:
static void initialize(QQuickTheme *theme);
+ static void registerSystemStyle(QQuickMaterialStyle *style);
+ static void unregisterSystemStyle(QQuickMaterialStyle *style);
+ static void updateTheme();
};
QT_END_NAMESPACE
diff --git a/src/quickcontrols/material/qtquickcontrols2materialstyleplugin.cpp b/src/quickcontrols/material/qtquickcontrols2materialstyleplugin.cpp
index 4911a3e0f2..79ae0e2c5f 100644
--- a/src/quickcontrols/material/qtquickcontrols2materialstyleplugin.cpp
+++ b/src/quickcontrols/material/qtquickcontrols2materialstyleplugin.cpp
@@ -21,6 +21,7 @@ public:
QString name() const override;
void initializeTheme(QQuickTheme *theme) override;
+ void updateTheme() override;
QQuickMaterialTheme theme;
};
@@ -42,6 +43,11 @@ void QtQuickControls2MaterialStylePlugin::initializeTheme(QQuickTheme *theme)
this->theme.initialize(theme);
}
+void QtQuickControls2MaterialStylePlugin::updateTheme()
+{
+ theme.updateTheme();
+}
+
QT_END_NAMESPACE
#include "qtquickcontrols2materialstyleplugin.moc"
diff --git a/tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt b/tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt
index 0fe3fe7b9a..4a5f42ee82 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt
+++ b/tests/auto/quickcontrols/qquickmaterialstyle/CMakeLists.txt
@@ -26,6 +26,9 @@ qt_internal_add_test(tst_qquickmaterialstyle
tst_qquickmaterialstyle.cpp
LIBRARIES
Qt::Gui
+ Qt::GuiPrivate
+ Qt::QuickControls2MaterialPrivate
+ Qt::QmlPrivate
TESTDATA ${test_data}
)
diff --git a/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml b/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml
index cdd8fb41e6..001567d3a9 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml
+++ b/tests/auto/quickcontrols/qquickmaterialstyle/data/tst_material.qml
@@ -9,6 +9,8 @@ import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Controls.Material.impl as MaterialImpl
+import Qt.test
+
TestCase {
id: testCase
width: 200
@@ -1365,4 +1367,28 @@ TestCase {
let headerItem = window.listView.headerItem
compare(headerItem.Material.theme, Material.Dark)
}
+
+ Component {
+ id: systemThemeComponent
+
+ ApplicationWindow {
+ width: 200
+ height: 200
+ visible: true
+ Material.theme: Material.System
+ }
+ }
+
+ function test_systemTheme() {
+ let window = createTemporaryObject(systemThemeComponent, testCase)
+ verify(window)
+
+ const toggleTheme = (theme) => (theme === Material.Dark) ? Material.Light : Material.Dark
+
+ TestHelper.platformTheme = toggleTheme(TestHelper.platformTheme)
+ tryCompare(window.Material, "theme", TestHelper.platformTheme)
+
+ TestHelper.platformTheme = toggleTheme(TestHelper.platformTheme)
+ tryCompare(window.Material, "theme", TestHelper.platformTheme)
+ }
}
diff --git a/tests/auto/quickcontrols/qquickmaterialstyle/tst_qquickmaterialstyle.cpp b/tests/auto/quickcontrols/qquickmaterialstyle/tst_qquickmaterialstyle.cpp
index 48a3e2138a..b294b0d815 100644
--- a/tests/auto/quickcontrols/qquickmaterialstyle/tst_qquickmaterialstyle.cpp
+++ b/tests/auto/quickcontrols/qquickmaterialstyle/tst_qquickmaterialstyle.cpp
@@ -1,17 +1,88 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformtheme.h>
+#include <QtQuickControls2Material/private/qquickmaterialstyle_p.h>
#include <QtQuickTest/quicktest.h>
+namespace {
+static inline Qt::ColorScheme toColorScheme(QQuickMaterialStyle::Theme theme)
+{
+ switch (theme) {
+ case QQuickMaterialStyle::Theme::System:
+ return Qt::ColorScheme::Unknown;
+ case QQuickMaterialStyle::Light:
+ return Qt::ColorScheme::Light;
+ case QQuickMaterialStyle::Dark:
+ return Qt::ColorScheme::Dark;
+ default:
+ Q_UNREACHABLE_RETURN(Qt::ColorScheme::Unknown);
+ }
+}
+
+static inline QQuickMaterialStyle::Theme toMaterialTheme(Qt::ColorScheme colorScheme)
+{
+ switch (colorScheme) {
+ case Qt::ColorScheme::Unknown:
+ return QQuickMaterialStyle::System;
+ case Qt::ColorScheme::Light:
+ return QQuickMaterialStyle::Light;
+ case Qt::ColorScheme::Dark:
+ return QQuickMaterialStyle::Dark;
+ default:
+ Q_UNREACHABLE_RETURN(QQuickMaterialStyle::System);
+ }
+}
+}
+
+
+class MockPlatformTheme : public QPlatformTheme
+{
+ Qt::ColorScheme colorScheme() const override
+ {
+ return m_colorScheme;
+ }
+ void requestColorScheme(Qt::ColorScheme theme) override
+ {
+ m_colorScheme = theme;
+ QWindowSystemInterfacePrivate::ThemeChangeEvent tce{nullptr};
+ QGuiApplicationPrivate::processThemeChanged(&tce);
+ }
+
+private:
+ Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown;
+};
+
+
class Setup : public QObject
{
Q_OBJECT
+ Q_PROPERTY(QQuickMaterialStyle::Theme platformTheme READ platformTheme WRITE setPlatformTheme CONSTANT FINAL)
public slots:
void applicationAvailable()
{
QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuWindows);
}
+
+ void qmlEngineAvailable(QQmlEngine*)
+ {
+ qmlRegisterSingletonInstance("Qt.test", 1, 0, "TestHelper", this);
+
+ QGuiApplicationPrivate::platform_theme = new MockPlatformTheme;
+ }
+
+public:
+ void setPlatformTheme(QQuickMaterialStyle::Theme theme)
+ {
+ QGuiApplicationPrivate::platform_theme->requestColorScheme(toColorScheme(theme));
+ }
+
+ QQuickMaterialStyle::Theme platformTheme() const
+ {
+ return toMaterialTheme(QGuiApplicationPrivate::platform_theme->colorScheme());
+ }
};
QUICK_TEST_MAIN_WITH_SETUP(tst_qquickmaterialstyle, Setup)