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();