SineStriker
2023-12-22 a5d13e19dd7f6037e10b649c49805922ae5e0fa6
Prepare to remove style support again
18个文件已修改
6个文件已添加
1个文件已删除
550 ■■■■■ 已修改文件
CMakeLists.txt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/CMakeLists.txt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/dark-style.qss 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/light-style.qss 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/mainwindow.cpp 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/mainwindow.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
qmsetup @ df66d3 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
share/install.cmake 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
share/qmake/QWKStyleSupport.pri.in 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/CMakeLists.txt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/CMakeLists.txt 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext.cpp 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext_p.h 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/cocoawindowcontext.mm 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/cocoawindowcontext_p.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext.cpp 154 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext_p.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/style/styleagent.cpp 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/style/styleagent.h 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/style/styleagent_linux.cpp 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/style/styleagent_mac.mm 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/style/styleagent_p.h 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/style/styleagent_win.cpp 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/windowagentbase.cpp 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/windowagentbase.h 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CMakeLists.txt
@@ -8,12 +8,12 @@
option(QWINDOWKIT_BUILD_STATIC "Build static libraries" OFF)
option(QWINDOWKIT_BUILD_WIDGETS "Build widgets module" ON)
option(QWINDOWKIT_BUILD_QUICK "Build quick module" ON)
option(QWINDOWKIT_BUILD_STYLESUPPORT "Build style support module" OFF)
option(QWINDOWKIT_BUILD_EXAMPLES "Build examples" OFF)
option(QWINDOWKIT_BUILD_DOCUMENTATIONS "Build documentations" OFF)
option(QWINDOWKIT_INSTALL "Install library" ON)
option(QWINDOWKIT_FORCE_QT_WINDOW_CONTEXT "Enable Qt Window Context anyway" OFF)
option(QWINDOWKIT_ENABLE_STYLE_AGENT "Enable building style agent" ON)
# ----------------------------------
# CMake Settings
examples/mainwindow/CMakeLists.txt
@@ -5,7 +5,7 @@
qwk_add_example(${PROJECT_NAME}
    SOURCES ${_src} mainwindow.qrc ../shared/resources/shared.qrc
    QT_LINKS Core Gui Widgets
    LINKS QWKWidgets QWKStyleSupport WidgetFrame
    LINKS QWKWidgets WidgetFrame
)
set_target_properties(${PROJECT_NAME} PROPERTIES
examples/mainwindow/dark-style.qss
@@ -142,12 +142,12 @@
/* Window */
MainWindow[custom-style=true] {
    background-color: transparent;
MainWindow {
    background-color: #1E1E1E;
}
MainWindow[custom-style=false] {
    background-color: #1E1E1E;
MainWindow[custom-style=true] {
    background-color: transparent;
}
QWidget#clock-widget {
examples/mainwindow/light-style.qss
@@ -140,12 +140,12 @@
/* Window */
MainWindow[custom-style=true] {
    background-color: transparent;
MainWindow {
    background-color: #F3F3F3;
}
MainWindow[custom-style=false] {
    background-color: #F3F3F3;
MainWindow[custom-style=true] {
    background-color: transparent;
}
QWidget#clock-widget {
examples/mainwindow/mainwindow.cpp
@@ -10,8 +10,8 @@
#include <QtWidgets/QPushButton>
#include <QtWidgets/QActionGroup>
#include <QWKCore/styleagent.h>
#include <QWKWidgets/widgetwindowagent.h>
#include <QWKStyleSupport/styleagent.h>
#include <widgetframe/windowbar.h>
#include <widgetframe/windowbutton.h>
@@ -33,8 +33,7 @@
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
    installWindowAgent();
    styleAgent = new QWK::StyleAgent(this);
    installStyleAgent();
    auto clockWidget = new ClockWidget();
    clockWidget->setObjectName(QStringLiteral("clock-widget"));
@@ -283,10 +282,15 @@
#endif
}
void MainWindow::installStyleAgent() {
    styleAgent = new QWK::StyleAgent(this);
}
void MainWindow::loadStyleSheet(Theme theme) {
    if (!styleSheet().isEmpty() && theme == currentTheme)
        return;
    currentTheme = theme;
    if (QFile qss(theme == Dark ? QStringLiteral(":/dark-style.qss")
                                : QStringLiteral(":/light-style.qss"));
        qss.open(QIODevice::ReadOnly | QIODevice::Text)) {
examples/mainwindow/mainwindow.h
@@ -28,6 +28,7 @@
private:
    void installWindowAgent();
    void installStyleAgent();
    void loadStyleSheet(Theme theme);
    Theme currentTheme{};
qmsetup
@@ -1 +1 @@
Subproject commit 8afc40a5443899b779957fb006098ce8155aacee
Subproject commit df66d3235f3ec1c2b19221677319968c86d277c0
share/install.cmake
@@ -18,12 +18,10 @@
    set(QMAKE_QWK_CORE_NAME_RELEASE QWKCore)
    set(QMAKE_QWK_WIDGETS_NAME_RELEASE QWKWidgets)
    set(QMAKE_QWK_QUICK_NAME_RELEASE QWKQuick)
    set(QMAKE_QWK_STYLESUPPORT_NAME_RELEASE QWKStyleSupport)
    set(QMAKE_QWK_CORE_NAME_DEBUG QWKCore${CMAKE_DEBUG_POSTFIX})
    set(QMAKE_QWK_WIDGETS_NAME_DEBUG QWKWidgets${CMAKE_DEBUG_POSTFIX})
    set(QMAKE_QWK_QUICK_NAME_DEBUG QWKQuick${CMAKE_DEBUG_POSTFIX})
    set(QMAKE_QWK_STYLESUPPORT_NAME_DEBUG QWKStyleSupport${CMAKE_DEBUG_POSTFIX})
    file(GLOB _qmake_components "${CMAKE_CURRENT_LIST_DIR}/qmake/*.pri.in")
@@ -60,14 +58,12 @@
        QWKCore${CMAKE_DEBUG_POSTFIX}.lib
        QWKWidgets${CMAKE_DEBUG_POSTFIX}.lib
        QWKQuick${CMAKE_DEBUG_POSTFIX}.lib
        QWKStyleSupport${CMAKE_DEBUG_POSTFIX}.lib
    )
    set(MSBUILD_QWK_LIBRARY_LIST_RELEASE
        QWKCore.lib
        QWKWidgets.lib
        QWKQuick.lib
        QWKStyleSupport.lib
    )
    to_dos_separator(MSBUILD_QWK_INSTALL_PREFIX)
share/qmake/QWKStyleSupport.pri.in
File was deleted
src/CMakeLists.txt
@@ -130,10 +130,6 @@
    add_subdirectory(quick)
endif()
if(QWINDOWKIT_BUILD_STYLESUPPORT)
    add_subdirectory(stylesupport)
endif()
# ----------------------------------
# Documentation
# ----------------------------------
src/core/CMakeLists.txt
@@ -62,6 +62,22 @@
    endif()
endif()
if(QWINDOWKIT_ENABLE_STYLE_AGENT)
    list(APPEND _src
        style/styleagent.h
        style/styleagent_p.h
        style/styleagent.cpp
    )
    if(WIN32)
        list(APPEND _src style/styleagent_win.cpp)
    elseif(APPLE)
        list(APPEND _src style/styleagent_mac.mm)
    else()
        list(APPEND _src style/styleagent_linux.cpp)
    endif()
endif()
qwk_add_library(${PROJECT_NAME} AUTOGEN
    SOURCES ${_src}
    LINKS
src/core/contexts/abstractwindowcontext.cpp
@@ -48,6 +48,33 @@
        }
    }
    bool AbstractWindowContext::setWindowAttribute(const QString &key, const QVariant &attribute) {
        auto it = m_windowAttributes.find(key);
        if (it == m_windowAttributes.end()) {
            if (!attribute.isValid()) {
                return true;
            }
            if (m_windowHandle && !windowAttributeChanged(key, attribute, {})) {
                return false;
            }
            m_windowAttributes.insert(key, attribute);
            return true;
        }
        if (it.value() == attribute)
            return true;
        if (m_windowHandle && !windowAttributeChanged(key, attribute, it.value())) {
            return false;
        }
        if (attribute.isValid()) {
            it.value() = attribute;
        } else {
            m_windowAttributes.erase(it);
        }
        return true;
    }
    bool AbstractWindowContext::setHitTestVisible(const QObject *obj, bool visible) {
        Q_ASSERT(obj);
        if (!obj) {
@@ -219,6 +246,24 @@
        if (oldWindow == m_windowHandle)
            return;
        winIdChanged();
        if (m_windowHandle) {
            // Refresh window attributes
            auto attributes = m_windowAttributes;
            m_windowAttributes.clear();
            for (auto it = attributes.begin(); it != attributes.end(); ++it) {
                if (!windowAttributeChanged(it.key(), it.value(), {})) {
                    continue;
                }
                m_windowAttributes.insert(it.key(), it.value());
            }
        }
    }
    bool AbstractWindowContext::windowAttributeChanged(const QString &key,
                                                       const QVariant &attribute,
                                                       const QVariant &oldAttribute) {
        return false;
    }
}
src/core/contexts/abstractwindowcontext_p.h
@@ -37,6 +37,9 @@
        inline QWindow *window() const;
        inline WindowItemDelegate *delegate() const;
        inline QVariant windowAttribute(const QString &key) const;
        bool setWindowAttribute(const QString &key, const QVariant &attribute);
        inline bool isHitTestVisible(const QObject *obj) const;
        bool setHitTestVisible(const QObject *obj, bool visible);
@@ -71,6 +74,8 @@
    protected:
        virtual void winIdChanged() = 0;
        virtual bool windowAttributeChanged(const QString &key, const QVariant &attribute,
                                            const QVariant &oldAttribute);
    protected:
        QObject *m_host{};
@@ -85,6 +90,8 @@
        QObject *m_titleBar{};
        std::array<QObject *, WindowAgentBase::NumSystemButton> m_systemButtons{};
        QVariantHash m_windowAttributes;
        std::unique_ptr<QObject> m_winIdChangeEventFilter;
    };
@@ -100,6 +107,10 @@
        return m_delegate.get();
    }
    inline QVariant AbstractWindowContext::windowAttribute(const QString &key) const {
        return m_windowAttributes.value(key);
    }
    inline bool AbstractWindowContext::isHitTestVisible(const QObject *obj) const {
        return m_hitTestVisibleItems.contains(obj);
    }
src/core/contexts/cocoawindowcontext.mm
@@ -400,4 +400,17 @@
        cocoaWindowEventFilter = std::make_unique<CocoaWindowEventFilter>(this, this);
    }
    bool CocoaWindowContext::windowAttributeChanged(const QString &key, const QVariant &attribute,
                                                    const QVariant &oldAttribute) {
        if (key == QStringLiteral("no-system-buttons")) {
            if (attribute.toBool()) {
                // TODO: set off
            } else {
                // TODO: set on
            }
            return true;
        }
        return false;
    }
}
src/core/contexts/cocoawindowcontext_p.h
@@ -25,6 +25,8 @@
    protected:
        void winIdChanged() override;
        bool windowAttributeChanged(const QString &key, const QVariant &attribute,
                                    const QVariant &oldAttribute) override;
    protected:
        WId windowId = 0;
src/core/contexts/win32windowcontext.cpp
@@ -785,6 +785,160 @@
            }
        }
        return false; // Not handled
    }
    bool Win32WindowContext::windowAttributeChanged(const QString &key, const QVariant &attribute,
                                                    const QVariant &oldAttribute) {
        const auto hwnd = reinterpret_cast<HWND>(window->winId());
        const DynamicApis &apis = DynamicApis::instance();
        if (key == QStringLiteral("mica")) {
            if (!isWin11OrGreater()) {
                return false;
            }
            if (attribute.toBool()) {
                // We need to extend the window frame into the whole client area to be able
                // to see the blurred window background.
                static constexpr const MARGINS margins = {-1, -1, -1, -1};
                apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
                if (isWin1122H2OrGreater()) {
                    // Use official DWM API to enable Mica, available since Windows 11 22H2
                    // (10.0.22621).
                    const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_MAINWINDOW;
                    apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType,
                                                sizeof(backdropType));
                } else {
                    // Use undocumented DWM API to enable Mica, available since Windows 11
                    // (10.0.22000).
                    const BOOL enable = TRUE;
                    apis.pDwmSetWindowAttribute(hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable));
                }
            } else {
                if (isWin1122H2OrGreater()) {
                    const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_AUTO;
                    apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType,
                                                sizeof(backdropType));
                } else {
                    const BOOL enable = FALSE;
                    apis.pDwmSetWindowAttribute(hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable));
                }
                static constexpr const MARGINS margins = {0, 0, 0, 0};
                apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
            }
            return true;
        } else if (key == QStringLiteral("mica-alt")) {
            if (!isWin1122H2OrGreater()) {
                return false;
            }
            if (attribute.toBool()) {
                // We need to extend the window frame into the whole client area to be able
                // to see the blurred window background.
                static constexpr const MARGINS margins = {-1, -1, -1, -1};
                apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
                // Use official DWM API to enable Mica Alt, available since Windows 11 22H2
                // (10.0.22621).
                const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TABBEDWINDOW;
                apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType,
                                            sizeof(backdropType));
            } else {
                const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_AUTO;
                apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType,
                                            sizeof(backdropType));
                static constexpr const MARGINS margins = {0, 0, 0, 0};
                apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
            }
            return true;
        } else if (key == QStringLiteral("acrylic-material")) {
            if (!isWin10OrGreater()) {
                return false;
            }
            if (attribute.userType() == QMetaType::QColor) {
                // We need to extend the window frame into the whole client area to be able
                // to see the blurred window background.
                static constexpr const MARGINS margins = {-1, -1, -1, -1};
                apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
                if (isWin11OrGreater()) {
                    const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TRANSIENTWINDOW;
                    apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType,
                                                sizeof(backdropType));
                } else {
                    auto gradientColor = attribute.value<QColor>();
                    ACCENT_POLICY policy{};
                    policy.dwAccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND;
                    policy.dwAccentFlags = ACCENT_ENABLE_ACRYLIC_WITH_LUMINOSITY;
                    // This API expects the #AABBGGRR format.
                    policy.dwGradientColor =
                        DWORD(qRgba(gradientColor.blue(), gradientColor.green(),
                                    gradientColor.red(), gradientColor.alpha()));
                    WINDOWCOMPOSITIONATTRIBDATA wcad{};
                    wcad.Attrib = WCA_ACCENT_POLICY;
                    wcad.pvData = &policy;
                    wcad.cbData = sizeof(policy);
                    apis.pSetWindowCompositionAttribute(hwnd, &wcad);
                }
            } else {
                if (isWin11OrGreater()) {
                    const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_AUTO;
                    apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType,
                                                sizeof(backdropType));
                } else {
                    ACCENT_POLICY policy{};
                    policy.dwAccentState = ACCENT_DISABLED;
                    policy.dwAccentFlags = ACCENT_NONE;
                    WINDOWCOMPOSITIONATTRIBDATA wcad{};
                    wcad.Attrib = WCA_ACCENT_POLICY;
                    wcad.pvData = &policy;
                    wcad.cbData = sizeof(policy);
                    apis.pSetWindowCompositionAttribute(hwnd, &wcad);
                }
                static constexpr const MARGINS margins = {0, 0, 0, 0};
                apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
            }
            return true;
        } else if (key == QStringLiteral("dwm-blur")) {
            if (attribute.toBool()) {
                // We need to extend the window frame into the whole client area to be able
                // to see the blurred window background.
                static constexpr const MARGINS margins = {-1, -1, -1, -1};
                apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
                if (isWin8OrGreater()) {
                    ACCENT_POLICY policy{};
                    policy.dwAccentState = ACCENT_ENABLE_BLURBEHIND;
                    policy.dwAccentFlags = ACCENT_NONE;
                    WINDOWCOMPOSITIONATTRIBDATA wcad{};
                    wcad.Attrib = WCA_ACCENT_POLICY;
                    wcad.pvData = &policy;
                    wcad.cbData = sizeof(policy);
                    apis.pSetWindowCompositionAttribute(hwnd, &wcad);
                } else {
                    DWM_BLURBEHIND bb{};
                    bb.fEnable = TRUE;
                    bb.fTransitionOnMaximized = TRUE;
                    bb.dwFlags = DWM_BB_ENABLE | DWM_BB_TRANSITIONONMAXIMIZED;
                    apis.pDwmEnableBlurBehindWindow(hwnd, &bb);
                }
            } else {
                if (isWin8OrGreater()) {
                    ACCENT_POLICY policy{};
                    policy.dwAccentState = ACCENT_DISABLED;
                    policy.dwAccentFlags = ACCENT_NONE;
                    WINDOWCOMPOSITIONATTRIBDATA wcad{};
                    wcad.Attrib = WCA_ACCENT_POLICY;
                    wcad.pvData = &policy;
                    wcad.cbData = sizeof(policy);
                    apis.pSetWindowCompositionAttribute(hwnd, &wcad);
                } else {
                    DWM_BLURBEHIND bb{};
                    bb.fEnable = FALSE;
                    bb.dwFlags = DWM_BB_ENABLE;
                    apis.pDwmEnableBlurBehindWindow(hwnd, &bb);
                }
                static constexpr const MARGINS margins = {0, 0, 0, 0};
                apis.pDwmExtendFrameIntoClientArea(hwnd, &margins);
            }
            return true;
        }
        return false;
    }
    QWK_USED static constexpr const struct {
src/core/contexts/win32windowcontext_p.h
@@ -40,6 +40,8 @@
    protected:
        void winIdChanged() override;
        bool windowAttributeChanged(const QString &key, const QVariant &attribute,
                                    const QVariant &oldAttribute) override;
    public:
        bool windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result);
src/core/style/styleagent.cpp
New file
@@ -0,0 +1,46 @@
#include "styleagent.h"
#include "styleagent_p.h"
#include <QtCore/QVariant>
namespace QWK {
    StyleAgentPrivate::StyleAgentPrivate() {
    }
    StyleAgentPrivate::~StyleAgentPrivate() = default;
    void StyleAgentPrivate::init() {
    }
    void StyleAgentPrivate::notifyThemeChanged(StyleAgent::SystemTheme theme) {
        if (theme == systemTheme)
            return;
        systemTheme = theme;
        Q_Q(StyleAgent);
        Q_EMIT q->systemThemeChanged();
    }
    StyleAgent::StyleAgent(QObject *parent) : StyleAgent(*new StyleAgentPrivate(), parent) {
        Q_D(StyleAgent);
        d->setupSystemThemeHook();
    }
    StyleAgent::~StyleAgent() {
        Q_D(StyleAgent);
        d->removeSystemThemeHook();
    }
    StyleAgent::SystemTheme StyleAgent::systemTheme() const {
        Q_D(const StyleAgent);
        return d->systemTheme;
    }
    StyleAgent::StyleAgent(StyleAgentPrivate &d, QObject *parent) : QObject(parent), d_ptr(&d) {
        d.q_ptr = this;
        d.init();
    }
}
src/core/style/styleagent.h
New file
@@ -0,0 +1,44 @@
#ifndef STYLEAGENT_H
#define STYLEAGENT_H
#include <memory>
#include <QtCore/QObject>
#include <QtGui/QWindow>
#include <QWKCore/qwkglobal.h>
namespace QWK {
    class StyleAgentPrivate;
    class QWK_CORE_EXPORT StyleAgent : public QObject {
        Q_OBJECT
        Q_DECLARE_PRIVATE(StyleAgent)
    public:
        explicit StyleAgent(QObject *parent = nullptr);
        ~StyleAgent() override;
        enum SystemTheme {
            Unknown,
            Light,
            Dark,
            HighContrast,
        };
        Q_ENUM(SystemTheme)
    public:
        SystemTheme systemTheme() const;
    Q_SIGNALS:
        void systemThemeChanged();
    protected:
        StyleAgent(StyleAgentPrivate &d, QObject *parent = nullptr);
        const std::unique_ptr<StyleAgentPrivate> d_ptr;
    };
}
#endif // STYLEAGENT_H
src/core/style/styleagent_linux.cpp
New file
@@ -0,0 +1,13 @@
#include "styleagent_p.h"
#include <QtCore/QVariant>
namespace QWK {
    void StyleAgentPrivate::setupSystemThemeHook() {
    }
    void StyleAgentPrivate::removeSystemThemeHook() {
    }
}
src/core/style/styleagent_mac.mm
New file
@@ -0,0 +1,13 @@
#include "styleagent_p.h"
#include <QtCore/QVariant>
namespace QWK {
    void StyleAgentPrivate::setupSystemThemeHook() {
    }
    void StyleAgentPrivate::removeSystemThemeHook() {
    }
}
src/core/style/styleagent_p.h
New file
@@ -0,0 +1,39 @@
#ifndef STYLEAGENTPRIVATE_H
#define STYLEAGENTPRIVATE_H
//
//  W A R N I N G !!!
//  -----------------
//
// This file is not part of the QWindowKit API. It is used purely as an
// implementation detail. This header file may change from version to
// version without notice, or may even be removed.
//
#include <QtCore/QHash>
#include <QWKCore/styleagent.h>
namespace QWK {
    class StyleAgentPrivate : public QObject {
        Q_DECLARE_PUBLIC(StyleAgent)
    public:
        StyleAgentPrivate();
        ~StyleAgentPrivate() override;
        void init();
        StyleAgent *q_ptr;
        StyleAgent::SystemTheme systemTheme = StyleAgent::Dark;
        virtual void setupSystemThemeHook();
        virtual void removeSystemThemeHook();
        void notifyThemeChanged(StyleAgent::SystemTheme theme);
    };
}
#endif // STYLEAGENTPRIVATE_H
src/core/style/styleagent_win.cpp
New file
@@ -0,0 +1,84 @@
#include "styleagent_p.h"
#include <QtCore/QSet>
#include <QtCore/QVariant>
#include <QtGui/QColor>
#include <QWKCore/private/qwkwindowsextra_p.h>
#include <QWKCore/private/nativeeventfilter_p.h>
namespace QWK {
    using StyleAgentSet = QSet<StyleAgentPrivate *>;
    Q_GLOBAL_STATIC(StyleAgentSet, g_styleAgentSet)
    class SystemSettingEventFilter : public AppNativeEventFilter {
    public:
        bool nativeEventFilter(const QByteArray &eventType, void *message,
                               QT_NATIVE_EVENT_RESULT_TYPE *result) override {
            Q_UNUSED(eventType)
            if (!result) {
                return false;
            }
            const auto msg = static_cast<const MSG *>(message);
            switch (msg->message) {
                case WM_THEMECHANGED:
                case WM_SYSCOLORCHANGE:
                case WM_DWMCOLORIZATIONCOLORCHANGED: {
                    // TODO: walk through `g_styleAgentSet`
                    break;
                }
                case WM_SETTINGCHANGE: {
                    if (!msg->wParam && msg->lParam &&
                        std::wcscmp(reinterpret_cast<LPCWSTR>(msg->lParam), L"ImmersiveColorSet") ==
                            0) {
                        // TODO: walk through `g_styleAgentSet`
                    }
                    break;
                }
                default:
                    break;
            }
            return false;
        }
        static SystemSettingEventFilter *instance;
        static inline void install() {
            if (instance) {
                return;
            }
            instance = new SystemSettingEventFilter();
        }
        static inline void uninstall() {
            if (!instance) {
                return;
            }
            delete instance;
            instance = nullptr;
        }
    };
    SystemSettingEventFilter *SystemSettingEventFilter::instance = nullptr;
    void StyleAgentPrivate::setupSystemThemeHook() {
        g_styleAgentSet->insert(this);
        SystemSettingEventFilter::install();
        // Initialize `systemTheme` variable
    }
    void StyleAgentPrivate::removeSystemThemeHook() {
        if (!g_styleAgentSet->remove(this))
            return;
        if (g_styleAgentSet->isEmpty()) {
            SystemSettingEventFilter::uninstall();
        }
    }
}
src/core/windowagentbase.cpp
@@ -50,6 +50,16 @@
    WindowAgentBase::~WindowAgentBase() = default;
    QVariant WindowAgentBase::windowAttribute(const QString &key) const {
        Q_D(const WindowAgentBase);
        return d->context->windowAttribute(key);
    }
    bool WindowAgentBase::setWindowAttribute(const QString &key, const QVariant &attribute) {
        Q_D(WindowAgentBase);
        return d->context->setWindowAttribute(key, attribute);
    }
    void WindowAgentBase::showSystemMenu(const QPoint &pos) {
        Q_D(WindowAgentBase);
        d->context->showSystemMenu(pos);
src/core/windowagentbase.h
@@ -28,6 +28,12 @@
        };
        Q_ENUM(SystemButton)
        QVariant windowAttribute(const QString &key) const;
        bool setWindowAttribute(const QString &key, const QVariant &attribute);
    Q_SIGNALS:
        void systemThemeChanged();
    public Q_SLOTS:
        void showSystemMenu(const QPoint &pos); // Only available on Windows now
        void centralize();