diff --git a/src/gui/application.cpp b/src/gui/application.cpp index f05f222a3..fb3464761 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -126,21 +126,6 @@ namespace { // ---------------------------------------------------------------------------------- -#ifdef Q_OS_WIN -class WindowsNativeEventFilter : public QAbstractNativeEventFilter { -public: - bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override { - const auto msg = static_cast(message); - if(msg->message == WM_SYSCOLORCHANGE || msg->message == WM_SETTINGCHANGE) { - if (const auto ptr = qobject_cast(QGuiApplication::instance())) { - emit ptr->paletteChanged(ptr->palette()); - } - } - return false; - } -}; -#endif - bool Application::configVersionMigration() { QStringList deleteKeys, ignoreKeys; @@ -237,9 +222,6 @@ Application::Application(int &argc, char **argv) // Ensure OpenSSL config file is only loaded from app directory QString opensslConf = QCoreApplication::applicationDirPath() + QString("/openssl.cnf"); qputenv("OPENSSL_CONF", opensslConf.toLocal8Bit()); - - // Set up event listener for Windows theme changing - installNativeEventFilter(new WindowsNativeEventFilter()); #endif // TODO: Can't set this without breaking current config paths @@ -384,6 +366,8 @@ Application::Application(int &argc, char **argv) _theme->setSystrayUseMonoIcons(ConfigFile().monoIcons()); connect(_theme, &Theme::systrayUseMonoIconsChanged, this, &Application::slotUseMonoIconsChanged); + connect(this, &Application::systemPaletteChanged, + _theme, &Theme::systemPaletteHasChanged); #if defined(Q_OS_WIN) _shellExtensionsServer.reset(new ShellExtensionsServer); @@ -1127,6 +1111,9 @@ bool Application::event(QEvent *event) qCInfo(lcApplication) << errorParsingLocalFileEditingUrl; showHint(errorParsingLocalFileEditingUrl.toStdString()); } + } else if (event->type() == QEvent::ApplicationPaletteChange) { + qCInfo(lcApplication) << "application palette changed"; + emit systemPaletteChanged(); } return SharedTools::QtSingleApplication::event(event); } diff --git a/src/gui/application.h b/src/gui/application.h index 9ec6e121b..f2c41fe45 100644 --- a/src/gui/application.h +++ b/src/gui/application.h @@ -96,6 +96,7 @@ signals: void folderRemoved(); void folderStateChanged(OCC::Folder *); void isShowingSettingsDialog(); + void systemPaletteChanged(); protected slots: void slotParseMessage(const QString &, QObject *); diff --git a/src/gui/main.cpp b/src/gui/main.cpp index e91fb3be9..da946bc3b 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -35,8 +35,11 @@ #include #include #include +#include +#include #include #include +#include using namespace OCC; @@ -82,6 +85,12 @@ int main(int argc, char **argv) QQuickStyle::setStyle(style); QQuickStyle::setFallbackStyle(QStringLiteral("Fusion")); +#if defined Q_OS_WIN + if (QOperatingSystemVersion::current().version() < QOperatingSystemVersion::Windows11.version()) { + QApplication::setStyle(QStyleFactory::create("Fusion")); + } +#endif + OCC::Application app(argc, argv); #ifndef Q_OS_WIN diff --git a/src/gui/networksettings.ui b/src/gui/networksettings.ui index db6db42be..e8ee66ad6 100644 --- a/src/gui/networksettings.ui +++ b/src/gui/networksettings.ui @@ -13,11 +13,14 @@ Form + + true + - Qt::Vertical + Qt::Orientation::Vertical diff --git a/src/libsync/theme.cpp b/src/libsync/theme.cpp index d10d50be4..3c65e31bb 100644 --- a/src/libsync/theme.cpp +++ b/src/libsync/theme.cpp @@ -32,6 +32,11 @@ #include #include #include +#include + +#ifdef Q_OS_WIN +#include +#endif #include "nextcloudtheme.h" @@ -62,10 +67,18 @@ bool shouldPreferSvg() return QByteArray(APPLICATION_ICON_SET).toUpper() == QByteArrayLiteral("SVG"); } +#ifdef Q_OS_WIN +bool isWindows11OrGreater() { + return QOperatingSystemVersion::current().version() >= QOperatingSystemVersion::Windows11.version(); +} +#endif + } namespace OCC { +Q_LOGGING_CATEGORY(lcTheme, "nextcloud.gui.theme", QtInfoMsg) + Theme *Theme::_instance = nullptr; Theme *Theme::instance() @@ -354,30 +367,40 @@ Theme::Theme() #if defined(Q_OS_WIN) // Windows does not provide a dark theme for Win32 apps so let's come up with a palette // Credit to https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle + reserveDarkPalette = qApp->palette(); - reserveDarkPalette.setColor(QPalette::Window, QColor(53, 53, 53)); reserveDarkPalette.setColor(QPalette::WindowText, Qt::white); - reserveDarkPalette.setColor(QPalette::Disabled, QPalette::WindowText, - QColor(127, 127, 127)); + reserveDarkPalette.setColor(QPalette::Button, QColor(127, 127, 127)); + reserveDarkPalette.setColor(QPalette::Light, QColor(20, 20, 20)); + reserveDarkPalette.setColor(QPalette::Midlight, QColor(78, 78, 78)); + reserveDarkPalette.setColor(QPalette::Dark, QColor(191, 191, 191)); + reserveDarkPalette.setColor(QPalette::Mid, QColor(95, 95, 95)); + reserveDarkPalette.setColor(QPalette::Text, Qt::white); + reserveDarkPalette.setColor(QPalette::BrightText, Qt::red); + reserveDarkPalette.setColor(QPalette::ButtonText, Qt::white); reserveDarkPalette.setColor(QPalette::Base, QColor(42, 42, 42)); + reserveDarkPalette.setColor(QPalette::Window, QColor(53, 53, 53)); + reserveDarkPalette.setColor(QPalette::Shadow, QColor(20, 20, 20)); + reserveDarkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); + reserveDarkPalette.setColor(QPalette::HighlightedText, Qt::white); + reserveDarkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); + reserveDarkPalette.setColor(QPalette::LinkVisited, QColor(42, 130, 218)); reserveDarkPalette.setColor(QPalette::AlternateBase, QColor(66, 66, 66)); + reserveDarkPalette.setColor(QPalette::NoRole, QColor(127, 127, 127)); reserveDarkPalette.setColor(QPalette::ToolTipBase, Qt::white); reserveDarkPalette.setColor(QPalette::ToolTipText, QColor(53, 53, 53)); - reserveDarkPalette.setColor(QPalette::Text, Qt::white); + reserveDarkPalette.setColor(QPalette::PlaceholderText, QColor(44, 44, 44)); + reserveDarkPalette.setColor(QPalette::Accent, QColor(127, 127, 200)); + reserveDarkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127)); - reserveDarkPalette.setColor(QPalette::Dark, QColor(35, 35, 35)); - reserveDarkPalette.setColor(QPalette::Shadow, QColor(20, 20, 20)); - reserveDarkPalette.setColor(QPalette::Button, QColor(53, 53, 53)); - reserveDarkPalette.setColor(QPalette::ButtonText, Qt::white); reserveDarkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127)); - reserveDarkPalette.setColor(QPalette::BrightText, Qt::red); - reserveDarkPalette.setColor(QPalette::Link, QColor(42, 130, 218)); - reserveDarkPalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); reserveDarkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80)); - reserveDarkPalette.setColor(QPalette::HighlightedText, Qt::white); reserveDarkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127)); + reserveDarkPalette.setColor(QPalette::Disabled, QPalette::WindowText, + QColor(127, 127, 127)); + connectToPaletteSignal(); #endif #ifdef APPLICATION_SERVER_URL_ENFORCE @@ -946,27 +969,74 @@ QColor Theme::defaultColor() void Theme::connectToPaletteSignal() { if (!_paletteSignalsConnected) { - if (const auto ptr = qobject_cast(QGuiApplication::instance())) { + if (const auto ptr = qobject_cast(qApp)) { connect(ptr->styleHints(), &QStyleHints::colorSchemeChanged, this, &Theme::darkModeChanged); _paletteSignalsConnected = true; } } } -bool Theme::darkMode() +QVariantMap Theme::systemPalette() const { - connectToPaletteSignal(); - switch (qGuiApp->styleHints()->colorScheme()) - { - case Qt::ColorScheme::Dark: - return true; - case Qt::ColorScheme::Light: - return false; - case Qt::ColorScheme::Unknown: - return Theme::isDarkColor(QGuiApplication::palette().window().color()); + auto systemPalette = QGuiApplication::palette(); +#if defined(Q_OS_WIN) + if (darkMode() && !isWindows11OrGreater()) { + systemPalette = reserveDarkPalette; + qApp->setPalette(reserveDarkPalette); } +#else - return false; +#endif + + return QVariantMap { + { QStringLiteral("base"), systemPalette.base().color() }, + { QStringLiteral("alternateBase"), systemPalette.alternateBase().color() }, + { QStringLiteral("text"), systemPalette.text().color() }, + { QStringLiteral("toolTipBase"), systemPalette.toolTipBase().color() }, + { QStringLiteral("toolTipText"), systemPalette.toolTipText().color() }, + { QStringLiteral("brightText"), systemPalette.brightText().color() }, + { QStringLiteral("buttonText"), systemPalette.buttonText().color() }, + { QStringLiteral("button"), systemPalette.button().color() }, + { QStringLiteral("highlightedText"), systemPalette.highlightedText().color() }, + { QStringLiteral("placeholderText"), systemPalette.placeholderText().color() }, + { QStringLiteral("windowText"), systemPalette.windowText().color() }, + { QStringLiteral("window"), systemPalette.window().color() }, + { QStringLiteral("dark"), systemPalette.dark().color() }, + { QStringLiteral("highlight"), systemPalette.highlight().color() }, + { QStringLiteral("light"), systemPalette.light().color() }, + { QStringLiteral("link"), systemPalette.link().color() }, + { QStringLiteral("midlight"), systemPalette.midlight().color() }, + { QStringLiteral("mid"), systemPalette.mid().color() }, + { QStringLiteral("linkVisited"), systemPalette.linkVisited().color() }, + { QStringLiteral("shadow"), systemPalette.shadow().color() }, + }; +} + +bool Theme::darkMode() const +{ + const auto isDarkFromStyle = [] { + switch (qGuiApp->styleHints()->colorScheme()) + { + case Qt::ColorScheme::Dark: + return true; + case Qt::ColorScheme::Light: + return false; + case Qt::ColorScheme::Unknown: + return Theme::isDarkColor(QGuiApplication::palette().window().color()); + } + + return false; + }; + +#ifdef Q_OS_WIN + static const auto darkModeSubkey = QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"); + if (!isWindows11OrGreater() && + Utility::registryKeyExists(HKEY_CURRENT_USER, darkModeSubkey) && + !Utility::registryGetKeyValue(HKEY_CURRENT_USER, darkModeSubkey, QStringLiteral("AppsUseLightTheme")).toBool()) { + return true; + } +#endif + return isDarkFromStyle(); } void Theme::setOverrideServerUrl(const QString &overrideServerUrl) @@ -1010,4 +1080,14 @@ void Theme::setStartLoginFlowAutomatically(bool startLoginFlowAuto) } } +void Theme::systemPaletteHasChanged() +{ + qCInfo(lcTheme()) << "system palette changed"; +#ifdef Q_OS_WIN + if (darkMode() && !isWindows11OrGreater()) { + qApp->setPalette(reserveDarkPalette); + } +#endif +} + } // end namespace client diff --git a/src/libsync/theme.h b/src/libsync/theme.h index b414c9fa3..a7e817de1 100644 --- a/src/libsync/theme.h +++ b/src/libsync/theme.h @@ -67,6 +67,7 @@ class OWNCLOUDSYNC_EXPORT Theme : public QObject Q_PROPERTY(QColor defaultColor READ defaultColor CONSTANT) + Q_PROPERTY(QVariantMap systemPalette READ systemPalette NOTIFY systemPaletteChanged) Q_PROPERTY(bool darkMode READ darkMode NOTIFY darkModeChanged) public: enum CustomMediaType { @@ -600,13 +601,15 @@ public: static constexpr const char *themePrefix = ":/client/theme/"; - bool darkMode(); + [[nodiscard]] QVariantMap systemPalette() const; + [[nodiscard]] bool darkMode() const; public slots: void setOverrideServerUrl(const QString &overrideServerUrl); void setForceOverrideServerUrl(bool forceOverride); void setVfsEnabled(bool enabled); void setStartLoginFlowAutomatically(bool startLoginFlowAuto); + void systemPaletteHasChanged(); protected: #ifndef TOKEN_AUTH_ONLY @@ -624,6 +627,7 @@ protected: signals: void systrayUseMonoIconsChanged(bool); + void systemPaletteChanged(const QPalette &palette); void darkModeChanged(); void overrideServerUrlChanged(); void forceOverrideServerUrlChanged(); diff --git a/theme/Style/Style.qml b/theme/Style/Style.qml index 912aa10b8..2bb1905d3 100644 --- a/theme/Style/Style.qml +++ b/theme/Style/Style.qml @@ -8,20 +8,17 @@ QtObject { readonly property int pixelSize: fontMetrics.font.pixelSize readonly property bool darkMode: Theme.darkMode - property SystemPalette nativePalette: SystemPalette { - } - // Colors readonly property color ncBlue: Theme.wizardHeaderBackgroundColor readonly property color ncHeaderTextColor: Theme.wizardHeaderTitleColor - readonly property color ncTextColor: nativePalette.text + readonly property color ncTextColor: Theme.systemPalette.windowText readonly property color ncTextBrightColor: "white" readonly property color ncSecondaryTextColor: "#808080" readonly property color lightHover: Theme.darkMode ? Qt.lighter(backgroundColor, 2) : Qt.darker(backgroundColor, 1.05) readonly property color darkerHover: Theme.darkMode ? Qt.lighter(backgroundColor, 2.35) : Qt.darker(backgroundColor, 1.25) readonly property color menuBorder: Theme.darkMode ? Qt.lighter(backgroundColor, 2.5) : Qt.darker(backgroundColor, 1.5) - readonly property color backgroundColor: nativePalette.window - readonly property color buttonBackgroundColor: nativePalette.button + readonly property color backgroundColor: Theme.systemPalette.window + readonly property color buttonBackgroundColor: Theme.systemPalette.button readonly property color positiveColor: Qt.rgba(0.38, 0.74, 0.38, 1) readonly property color currentUserHeaderColor: UserModel.currentUser ? UserModel.currentUser.headerColor : ncBlue