From 327055934009dc416be6234db8e4d26fab81fb80 Mon Sep 17 00:00:00 2001 From: SineStriker <55847490+SineStriker@users.noreply.github.com> Date: ćšć, 21 12æ 2023 23:21:28 +0800 Subject: [PATCH] Merge pull request #8 from stdware/stylesupport --- src/core/contexts/cocoawindowcontext.mm | 18 src/core/contexts/abstractwindowcontext_p.h | 10 src/stylesupport/styleagent_mac.mm | 29 + CMakeLists.txt | 1 examples/mainwindow/light-style.qss | 6 src/core/contexts/win32windowcontext.cpp | 490 ----------------- share/qmake/QWKStyleSupport.pri.in | 11 src/core/CMakeLists.txt | 17 src/stylesupport/CMakeLists.txt | 49 + examples/mainwindow/mainwindow.cpp | 83 ++ src/stylesupport/styleagent.h | 47 + src/core/contexts/abstractwindowcontext.cpp | 53 + examples/mainwindow/CMakeLists.txt | 2 src/core/windowagentbase.h | 3 src/stylesupport/styleagent_win.cpp | 242 ++++++++ examples/mainwindow/mainwindow.h | 8 src/core/shared/qwkwindowsextra_p.h | 386 +++++++++++++ src/core/shared/systemwindow_p.h | 0 src/core/windowagentbase.cpp | 10 src/stylesupport/styleagent.cpp | 83 ++ src/stylesupport/styleagent_p.h | 44 + src/widgets/widgetwindowagent.cpp | 23 src/stylesupport/styleagent_linux.cpp | 20 src/stylesupport/qwkstylesupportglobal.h | 18 src/CMakeLists.txt | 4 examples/mainwindow/dark-style.qss | 6 share/install.cmake | 4 27 files changed, 1,068 insertions(+), 599 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ffbee08..283a654 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ 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) diff --git a/examples/mainwindow/CMakeLists.txt b/examples/mainwindow/CMakeLists.txt index c29f2b8..ea3c66b 100644 --- a/examples/mainwindow/CMakeLists.txt +++ b/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 WidgetFrame + LINKS QWKWidgets QWKStyleSupport WidgetFrame ) set_target_properties(${PROJECT_NAME} PROPERTIES diff --git a/examples/mainwindow/dark-style.qss b/examples/mainwindow/dark-style.qss index 095b900..ef77219 100644 --- a/examples/mainwindow/dark-style.qss +++ b/examples/mainwindow/dark-style.qss @@ -142,7 +142,11 @@ /* Window */ -MainWindow { +MainWindow[custom-style=true] { + background-color: transparent; +} + +MainWindow[custom-style=false] { background-color: #1E1E1E; } diff --git a/examples/mainwindow/light-style.qss b/examples/mainwindow/light-style.qss index eef4066..6c15e62 100644 --- a/examples/mainwindow/light-style.qss +++ b/examples/mainwindow/light-style.qss @@ -140,7 +140,11 @@ /* Window */ -MainWindow { +MainWindow[custom-style=true] { + background-color: transparent; +} + +MainWindow[custom-style=false] { background-color: #F3F3F3; } diff --git a/examples/mainwindow/mainwindow.cpp b/examples/mainwindow/mainwindow.cpp index 52b2880..173be17 100644 --- a/examples/mainwindow/mainwindow.cpp +++ b/examples/mainwindow/mainwindow.cpp @@ -4,11 +4,14 @@ #include <QtCore/QFile> #include <QtCore/QTime> #include <QtCore/QTimer> +#include <QtGui/QPainter> +#include <QtGui/QActionGroup> #include <QtWidgets/QApplication> #include <QtWidgets/QStyle> #include <QtWidgets/QPushButton> #include <QWKWidgets/widgetwindowagent.h> +#include <QWKStyleSupport/styleagent.h> #include <widgetframe/windowbar.h> #include <widgetframe/windowbutton.h> @@ -30,6 +33,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { installWindowAgent(); + + styleAgent = new QWK::StyleAgent(this); auto clockWidget = new ClockWidget(); clockWidget->setObjectName(QStringLiteral("clock-widget")); @@ -104,11 +109,11 @@ void MainWindow::installWindowAgent() { // 1. Setup window agent - auto agent = new QWK::WidgetWindowAgent(this); - agent->setup(this); + windowAgent = new QWK::WidgetWindowAgent(this); + windowAgent->setup(this); // 2. Construct your title bar - auto menuBar = [this, agent]() { + auto menuBar = [this]() { auto menuBar = new QMenuBar(); // Virtual menu @@ -122,7 +127,7 @@ edit->addAction(new QAction(tr("Redo(&R)"), menuBar)); // Theme action - auto darkAction = new QAction(tr("Dark Theme"), menuBar); + auto darkAction = new QAction(tr("Enable dark theme"), menuBar); darkAction->setCheckable(true); connect(darkAction, &QAction::triggered, this, [this](bool checked) { loadStyleSheet(checked ? Dark : Light); // @@ -131,10 +136,60 @@ darkAction->setChecked(currentTheme == Dark); // }); +#ifdef Q_OS_WIN + auto dwmBlurAction = new QAction(tr("Enable DWM blur"), menuBar); + dwmBlurAction->setCheckable(true); + connect(dwmBlurAction, &QAction::triggered, this, [this](bool checked){ + QWindow *w = windowHandle(); + styleAgent->setWindowAttribute(w, QStringLiteral("dwm-blur"), checked); + setProperty("custom-style", checked); + style()->polish(this); + }); + + auto acrylicAction = new QAction(tr("Enable acrylic material"), menuBar); + acrylicAction->setCheckable(true); + connect(acrylicAction, &QAction::triggered, this, [this](bool checked){ + QWindow *w = windowHandle(); + styleAgent->setWindowAttribute(w, QStringLiteral("acrylic-material"), QColor()); + setProperty("custom-style", checked); + style()->polish(this); + }); + + auto micaAction = new QAction(tr("Enable mica"), menuBar); + micaAction->setCheckable(true); + connect(micaAction, &QAction::triggered, this, [this](bool checked){ + QWindow *w = windowHandle(); + styleAgent->setWindowAttribute(w, QStringLiteral("mica"), checked); + setProperty("custom-style", checked); + style()->polish(this); + }); + + auto micaAltAction = new QAction(tr("Enable mica alt"), menuBar); + micaAltAction->setCheckable(true); + connect(micaAltAction, &QAction::triggered, this, [this](bool checked){ + QWindow *w = windowHandle(); + styleAgent->setWindowAttribute(w, QStringLiteral("mica-alt"), checked); + setProperty("custom-style", checked); + style()->polish(this); + }); + + auto winStyleGroup = new QActionGroup(menuBar); + winStyleGroup->addAction(dwmBlurAction); + winStyleGroup->addAction(acrylicAction); + winStyleGroup->addAction(micaAction); + winStyleGroup->addAction(micaAltAction); +#endif + // Real menu auto settings = new QMenu(tr("Settings(&S)"), menuBar); settings->addAction(darkAction); +#ifdef Q_OS_WIN settings->addSeparator(); + settings->addAction(dwmBlurAction); + settings->addAction(acrylicAction); + settings->addAction(micaAction); + settings->addAction(micaAltAction); +#endif menuBar->addMenu(file); menuBar->addMenu(edit); @@ -180,28 +235,28 @@ windowBar->setTitleLabel(titleLabel); windowBar->setHostWidget(this); - agent->setTitleBar(windowBar); + windowAgent->setTitleBar(windowBar); #ifndef Q_OS_MAC - agent->setSystemButton(QWK::WindowAgentBase::WindowIcon, iconButton); - agent->setSystemButton(QWK::WindowAgentBase::Minimize, minButton); - agent->setSystemButton(QWK::WindowAgentBase::Maximize, maxButton); - agent->setSystemButton(QWK::WindowAgentBase::Close, closeButton); + windowAgent->setSystemButton(QWK::WindowAgentBase::WindowIcon, iconButton); + windowAgent->setSystemButton(QWK::WindowAgentBase::Minimize, minButton); + windowAgent->setSystemButton(QWK::WindowAgentBase::Maximize, maxButton); + windowAgent->setSystemButton(QWK::WindowAgentBase::Close, closeButton); #endif - agent->setHitTestVisible(menuBar, true); + windowAgent->setHitTestVisible(menuBar, true); setMenuWidget(windowBar); // 3. Adds simulated mouse events to the title bar buttons #ifdef Q_OS_WINDOWS // Emulate Window system menu button behaviors - connect(iconButton, &QAbstractButton::clicked, agent, [iconButton, agent] { + connect(iconButton, &QAbstractButton::clicked, windowAgent, [this, iconButton] { iconButton->setProperty("double-click-close", false); // Pick a suitable time threshold - QTimer::singleShot(75, agent, [iconButton, agent]() { + QTimer::singleShot(75, windowAgent, [this, iconButton]() { if (iconButton->property("double-click-close").toBool()) return; - agent->showSystemMenu(iconButton->mapToGlobal(QPoint{0, iconButton->height()})); + windowAgent->showSystemMenu(iconButton->mapToGlobal(QPoint{0, iconButton->height()})); }); }); connect(iconButton, &QWK::WindowButton::doubleClicked, this, [iconButton, this]() { @@ -238,4 +293,4 @@ setStyleSheet(QString::fromUtf8(qss.readAll())); Q_EMIT themeChanged(); } -} \ No newline at end of file +} diff --git a/examples/mainwindow/mainwindow.h b/examples/mainwindow/mainwindow.h index 5511105..48d73cf 100644 --- a/examples/mainwindow/mainwindow.h +++ b/examples/mainwindow/mainwindow.h @@ -3,6 +3,11 @@ #include <QtWidgets/QMainWindow> +namespace QWK { + class WidgetWindowAgent; + class StyleAgent; +} + class MainWindow : public QMainWindow { Q_OBJECT public: @@ -26,6 +31,9 @@ void loadStyleSheet(Theme theme); Theme currentTheme{}; + + QWK::WidgetWindowAgent *windowAgent; + QWK::StyleAgent *styleAgent; }; #endif // MAINWINDOW_H diff --git a/share/install.cmake b/share/install.cmake index b8a6c5d..cb4aec1 100644 --- a/share/install.cmake +++ b/share/install.cmake @@ -18,10 +18,12 @@ 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") @@ -58,12 +60,14 @@ 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) diff --git a/share/qmake/QWKStyleSupport.pri.in b/share/qmake/QWKStyleSupport.pri.in new file mode 100644 index 0000000..0e7be64 --- /dev/null +++ b/share/qmake/QWKStyleSupport.pri.in @@ -0,0 +1,11 @@ +!defined(QMAKE_QWK_STYLESUPPORT_INCLUDED, var) { + QMAKE_QWK_STYLESUPPORT_INCLUDED = 1 + + include($$PWD/QWKCore.pri) + + CONFIG(debug, debug|release) { + LIBS += -l@QMAKE_QWK_STYLESUPPORT_NAME_DEBUG@ + } else { + LIBS += -l@QMAKE_QWK_STYLESUPPORT_NAME_RELEASE@ + } +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 739c9f8..a857d44 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -130,6 +130,10 @@ add_subdirectory(quick) endif() +if(QWINDOWKIT_BUILD_STYLESUPPORT) + add_subdirectory(stylesupport) +endif() + # ---------------------------------- # Documentation # ---------------------------------- diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index d76ca34..30188b9 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -13,25 +13,20 @@ windowitemdelegate.cpp kernel/nativeeventfilter_p.h kernel/nativeeventfilter.cpp - kernel/systemwindow_p.h + shared/systemwindow_p.h contexts/abstractwindowcontext_p.h contexts/abstractwindowcontext.cpp ) -set(_defines_private) set(_links_private) if(WIN32) list(APPEND _src qwindowkit_windows.h qwindowkit_windows.cpp + shared/qwkwindowsextra_p.h ) elseif(APPLE) - list(APPEND _links_private - "-framework Foundation" - "-framework Cocoa" - "-framework AppKit" - ) else() list(APPEND _src qwindowkit_linux.h @@ -54,6 +49,11 @@ contexts/cocoawindowcontext_p.h contexts/cocoawindowcontext.mm ) + list(APPEND _links_private + "-framework Foundation" + "-framework Cocoa" + "-framework AppKit" + ) else() list(APPEND _src contexts/qtwindowcontext_p.h @@ -64,12 +64,11 @@ qwk_add_library(${PROJECT_NAME} AUTOGEN SOURCES ${_src} - DEFINES_PRIVATE ${_defines_private} LINKS LINKS_PRIVATE ${_links_private} QT_LINKS Core Gui QT_INCLUDE_PRIVATE Core Gui - INCLUDE_PRIVATE kernel contexts platforms + INCLUDE_PRIVATE kernel contexts shared PREFIX QWK_CORE ) diff --git a/src/core/contexts/abstractwindowcontext.cpp b/src/core/contexts/abstractwindowcontext.cpp index d110fff..0d72e3c 100644 --- a/src/core/contexts/abstractwindowcontext.cpp +++ b/src/core/contexts/abstractwindowcontext.cpp @@ -3,10 +3,32 @@ #include <QtGui/QPen> #include <QtGui/QPainter> #include <QtGui/QScreen> +#include <memory> #include "qwkglobal_p.h" namespace QWK { + + class WinIdChangeEventFilter : public QObject { + public: + explicit WinIdChangeEventFilter(QObject *widget, AbstractWindowContext *ctx, + QObject *parent = nullptr) + : QObject(parent), ctx(ctx) { + widget->installEventFilter(this); + } + + protected: + bool eventFilter(QObject *obj, QEvent *event) override { + Q_UNUSED(obj) + if (event->type() == QEvent::WinIdChange) { + ctx->notifyWinIdChange(); + } + return false; + } + + protected: + AbstractWindowContext *ctx; + }; AbstractWindowContext::AbstractWindowContext() = default; @@ -18,32 +40,12 @@ } m_host = host; m_delegate.reset(delegate); + m_winIdChangeEventFilter = std::make_unique<WinIdChangeEventFilter>(host, this); m_windowHandle = m_delegate->hostWindow(m_host); if (m_windowHandle) { winIdChanged(); } - } - - bool AbstractWindowContext::setWindowAttribute(const QString &key, const QVariant &attribute) { - auto it = m_windowAttributes.find(key); - if (it.value() == attribute) - return true; - - auto newVar = attribute; - auto oldVar = it.value(); - bool res = false; - void *args[] = { - &const_cast<QString &>(key), - &newVar, - &oldVar, - &res, - }; - virtual_hook(WindowAttributeChangedHook, args); - if (res) { - it.value() = newVar; - } - return res; } bool AbstractWindowContext::setHitTestVisible(const QObject *obj, bool visible) { @@ -79,6 +81,15 @@ if (m_titleBar == item) { return false; } + + if (m_titleBar) { + // Since the title bar is changed, all items inside it should be dereferenced right away + for (auto &button : m_systemButtons) { + button = nullptr; + } + m_hitTestVisibleItems.clear(); + } + m_titleBar = item; return true; } diff --git a/src/core/contexts/abstractwindowcontext_p.h b/src/core/contexts/abstractwindowcontext_p.h index 753e8ee..f3e05a1 100644 --- a/src/core/contexts/abstractwindowcontext_p.h +++ b/src/core/contexts/abstractwindowcontext_p.h @@ -37,9 +37,6 @@ 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); @@ -64,7 +61,6 @@ RaiseWindowHook, ShowSystemMenuHook, DefaultColorsHook, - WindowAttributeChangedHook, DrawWindows10BorderHook, // Only works on Windows 10 SystemButtonAreaChangedHook, // Only works on Mac }; @@ -89,7 +85,7 @@ QObject *m_titleBar{}; std::array<QObject *, WindowAgentBase::NumSystemButton> m_systemButtons{}; - QVariantHash m_windowAttributes; + std::unique_ptr<QObject> m_winIdChangeEventFilter; }; inline QObject *AbstractWindowContext::host() const { @@ -102,10 +98,6 @@ inline WindowItemDelegate *AbstractWindowContext::delegate() const { 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 { diff --git a/src/core/contexts/cocoawindowcontext.mm b/src/core/contexts/cocoawindowcontext.mm index 560e2b5..96dbe65 100644 --- a/src/core/contexts/cocoawindowcontext.mm +++ b/src/core/contexts/cocoawindowcontext.mm @@ -376,24 +376,6 @@ return; } - case WindowAttributeChangedHook: { - auto args = static_cast<void **>(data); - const auto &key = *static_cast<const QString *>(args[0]); - const auto &newVar = *static_cast<const QVariant *>(args[1]); - const auto &oldVar = *static_cast<const QVariant *>(args[2]); - auto &res = *static_cast<bool *>(args[3]); - - if (key == QStringLiteral("no-system-buttons")) { - if (newVar.toBool()) { - // TODO: set off - } else { - // TODO: set on - } - res = true; - } - return; - } - default: break; } diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 3fc2ee3..4d1a6ff 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -10,8 +10,6 @@ #include <QtGui/QPalette> #include <QtGui/QStyleHints> -#include <QtCore/private/qwinregistry_p.h> -#include <QtCore/private/qsystemlibrary_p.h> #include <QtGui/private/qhighdpiscaling_p.h> #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) # include <QtGui/private/qguiapplication_p.h> @@ -23,120 +21,14 @@ # include <QtGui/qpa/qplatformwindow_p.h> #endif -#include <shellscalingapi.h> -#include <dwmapi.h> -#include <timeapi.h> - #include "qwkglobal_p.h" +#include "qwkwindowsextra_p.h" #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) Q_DECLARE_METATYPE(QMargins) #endif namespace QWK { - - enum _DWMWINDOWATTRIBUTE { - // [set] BOOL, Allows the use of host backdrop brushes for the window. - _DWMWA_USE_HOSTBACKDROPBRUSH = 17, - - // Undocumented, the same with DWMWA_USE_IMMERSIVE_DARK_MODE, but available on systems - // before Win10 20H1. - _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19, - - // [set] BOOL, Allows a window to either use the accent color, or dark, according to the - // user Color Mode preferences. - _DWMWA_USE_IMMERSIVE_DARK_MODE = 20, - - // [set] WINDOW_CORNER_PREFERENCE, Controls the policy that rounds top-level window corners - _DWMWA_WINDOW_CORNER_PREFERENCE = 33, - - // [get] UINT, width of the visible border around a thick frame window - _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37, - - // [get, set] SYSTEMBACKDROP_TYPE, Controls the system-drawn backdrop material of a window, - // including behind the non-client area. - _DWMWA_SYSTEMBACKDROP_TYPE = 38, - - // Undocumented, use this value to enable Mica material on Win11 21H2. You should use - // DWMWA_SYSTEMBACKDROP_TYPE instead on Win11 22H2 and newer. - _DWMWA_MICA_EFFECT = 1029 - }; - - // Types used with DWMWA_SYSTEMBACKDROP_TYPE - enum _DWM_SYSTEMBACKDROP_TYPE { - _DWMSBT_AUTO, // [Default] Let DWM automatically decide the system-drawn backdrop for this window. - _DWMSBT_NONE, // [Disable] Do not draw any system backdrop. - _DWMSBT_MAINWINDOW, // [Mica] Draw the backdrop material effect corresponding to a long-lived window. - _DWMSBT_TRANSIENTWINDOW, // [Acrylic] Draw the backdrop material effect corresponding to a transient window. - _DWMSBT_TABBEDWINDOW, // [Mica Alt] Draw the backdrop material effect corresponding to a window with a tabbed title bar. - }; - - enum WINDOWCOMPOSITIONATTRIB { - WCA_UNDEFINED = 0, - WCA_NCRENDERING_ENABLED = 1, - WCA_NCRENDERING_POLICY = 2, - WCA_TRANSITIONS_FORCEDISABLED = 3, - WCA_ALLOW_NCPAINT = 4, - WCA_CAPTION_BUTTON_BOUNDS = 5, - WCA_NONCLIENT_RTL_LAYOUT = 6, - WCA_FORCE_ICONIC_REPRESENTATION = 7, - WCA_EXTENDED_FRAME_BOUNDS = 8, - WCA_HAS_ICONIC_BITMAP = 9, - WCA_THEME_ATTRIBUTES = 10, - WCA_NCRENDERING_EXILED = 11, - WCA_NCADORNMENTINFO = 12, - WCA_EXCLUDED_FROM_LIVEPREVIEW = 13, - WCA_VIDEO_OVERLAY_ACTIVE = 14, - WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15, - WCA_DISALLOW_PEEK = 16, - WCA_CLOAK = 17, - WCA_CLOAKED = 18, - WCA_ACCENT_POLICY = 19, - WCA_FREEZE_REPRESENTATION = 20, - WCA_EVER_UNCLOAKED = 21, - WCA_VISUAL_OWNER = 22, - WCA_HOLOGRAPHIC = 23, - WCA_EXCLUDED_FROM_DDA = 24, - WCA_PASSIVEUPDATEMODE = 25, - WCA_USEDARKMODECOLORS = 26, - WCA_CORNER_STYLE = 27, - WCA_PART_COLOR = 28, - WCA_DISABLE_MOVESIZE_FEEDBACK = 29, - WCA_LAST = 30 - }; - - enum ACCENT_STATE { - ACCENT_DISABLED = 0, - ACCENT_ENABLE_GRADIENT = 1, - ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, - ACCENT_ENABLE_BLURBEHIND = 3, // Traditional DWM blur - ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803 - ACCENT_ENABLE_HOST_BACKDROP = 5, // RS5 1809 - ACCENT_INVALID_STATE = 6 // Using this value will remove the window background - }; - - enum ACCENT_FLAG { - ACCENT_NONE = 0, - ACCENT_ENABLE_ACRYLIC = 1, - ACCENT_ENABLE_ACRYLIC_WITH_LUMINOSITY = 482 - }; - - struct ACCENT_POLICY { - DWORD dwAccentState; - DWORD dwAccentFlags; - DWORD dwGradientColor; // #AABBGGRR - DWORD dwAnimationId; - }; - using PACCENT_POLICY = ACCENT_POLICY *; - - struct WINDOWCOMPOSITIONATTRIBDATA { - WINDOWCOMPOSITIONATTRIB Attrib; - PVOID pvData; - SIZE_T cbData; - }; - using PWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA *; - - using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA); // The thickness of an auto-hide taskbar in pixels. static constexpr const quint8 kAutoHideTaskBarThickness = 2; @@ -154,261 +46,6 @@ // Original Qt window proc function static WNDPROC g_qtWindowProc = nullptr; - - struct DynamicApis { - static const DynamicApis &instance() { - static const DynamicApis inst{}; - return inst; - } - - // template <typename T> - // struct DefaultFunc; - // - // template <typename Return, typename... Args> - // struct DefaultFunc<Return(QT_WIN_CALLBACK *)(Args...)> { - // static Return STDAPICALLTYPE func(Args...) { - // return Return{}; - // } - // }; - // - // #define DYNAMIC_API_DECLARE(NAME) decltype(&::NAME) p##NAME = - // DefaultFunc<decltype(&::NAME)>::func - -#define DYNAMIC_API_DECLARE(NAME) decltype(&::NAME) p##NAME = nullptr - - DYNAMIC_API_DECLARE(DwmFlush); - DYNAMIC_API_DECLARE(DwmIsCompositionEnabled); - DYNAMIC_API_DECLARE(DwmGetCompositionTimingInfo); - DYNAMIC_API_DECLARE(DwmGetWindowAttribute); - DYNAMIC_API_DECLARE(DwmSetWindowAttribute); - DYNAMIC_API_DECLARE(DwmExtendFrameIntoClientArea); - DYNAMIC_API_DECLARE(GetDpiForWindow); - DYNAMIC_API_DECLARE(GetSystemMetricsForDpi); - DYNAMIC_API_DECLARE(GetDpiForMonitor); - DYNAMIC_API_DECLARE(timeGetDevCaps); - DYNAMIC_API_DECLARE(timeBeginPeriod); - DYNAMIC_API_DECLARE(timeEndPeriod); - -#undef DYNAMIC_API_DECLARE - - SetWindowCompositionAttributePtr pSetWindowCompositionAttribute = nullptr; - - private: - DynamicApis() { -#define DYNAMIC_API_RESOLVE(DLL, NAME) \ - p##NAME = reinterpret_cast<decltype(p##NAME)>(DLL.resolve(#NAME)) - - QSystemLibrary user32(QStringLiteral("user32")); - DYNAMIC_API_RESOLVE(user32, GetDpiForWindow); - DYNAMIC_API_RESOLVE(user32, GetSystemMetricsForDpi); - DYNAMIC_API_RESOLVE(user32, SetWindowCompositionAttribute); - - QSystemLibrary shcore(QStringLiteral("shcore")); - DYNAMIC_API_RESOLVE(shcore, GetDpiForMonitor); - - QSystemLibrary dwmapi(QStringLiteral("dwmapi")); - DYNAMIC_API_RESOLVE(dwmapi, DwmFlush); - DYNAMIC_API_RESOLVE(dwmapi, DwmIsCompositionEnabled); - DYNAMIC_API_RESOLVE(dwmapi, DwmGetCompositionTimingInfo); - DYNAMIC_API_RESOLVE(dwmapi, DwmGetWindowAttribute); - DYNAMIC_API_RESOLVE(dwmapi, DwmSetWindowAttribute); - DYNAMIC_API_RESOLVE(dwmapi, DwmExtendFrameIntoClientArea); - - QSystemLibrary winmm(QStringLiteral("winmm")); - DYNAMIC_API_RESOLVE(winmm, timeGetDevCaps); - DYNAMIC_API_RESOLVE(winmm, timeBeginPeriod); - DYNAMIC_API_RESOLVE(winmm, timeEndPeriod); - -#undef DYNAMIC_API_RESOLVE - } - - ~DynamicApis() = default; - - Q_DISABLE_COPY_MOVE(DynamicApis) - }; - - static inline constexpr bool operator==(const POINT &lhs, const POINT &rhs) noexcept { - return ((lhs.x == rhs.x) && (lhs.y == rhs.y)); - } - - static inline constexpr bool operator!=(const POINT &lhs, const POINT &rhs) noexcept { - return !operator==(lhs, rhs); - } - - static inline constexpr bool operator==(const SIZE &lhs, const SIZE &rhs) noexcept { - return ((lhs.cx == rhs.cx) && (lhs.cy == rhs.cy)); - } - - static inline constexpr bool operator!=(const SIZE &lhs, const SIZE &rhs) noexcept { - return !operator==(lhs, rhs); - } - - static inline constexpr bool operator>(const SIZE &lhs, const SIZE &rhs) noexcept { - return ((lhs.cx * lhs.cy) > (rhs.cx * rhs.cy)); - } - - static inline constexpr bool operator>=(const SIZE &lhs, const SIZE &rhs) noexcept { - return (operator>(lhs, rhs) || operator==(lhs, rhs)); - } - - static inline constexpr bool operator<(const SIZE &lhs, const SIZE &rhs) noexcept { - return (operator!=(lhs, rhs) && !operator>(lhs, rhs)); - } - - static inline constexpr bool operator<=(const SIZE &lhs, const SIZE &rhs) noexcept { - return (operator<(lhs, rhs) || operator==(lhs, rhs)); - } - - static inline constexpr bool operator==(const RECT &lhs, const RECT &rhs) noexcept { - return ((lhs.left == rhs.left) && (lhs.top == rhs.top) && (lhs.right == rhs.right) && - (lhs.bottom == rhs.bottom)); - } - - static inline constexpr bool operator!=(const RECT &lhs, const RECT &rhs) noexcept { - return !operator==(lhs, rhs); - } - - static inline constexpr QPoint point2qpoint(const POINT &point) { - return QPoint{int(point.x), int(point.y)}; - } - - static inline constexpr POINT qpoint2point(const QPoint &point) { - return POINT{LONG(point.x()), LONG(point.y())}; - } - - static inline constexpr QSize size2qsize(const SIZE &size) { - return QSize{int(size.cx), int(size.cy)}; - } - - static inline constexpr SIZE qsize2size(const QSize &size) { - return SIZE{LONG(size.width()), LONG(size.height())}; - } - - static inline constexpr QRect rect2qrect(const RECT &rect) { - return QRect{ - QPoint{int(rect.left), int(rect.top) }, - QSize{int(RECT_WIDTH(rect)), int(RECT_HEIGHT(rect))} - }; - } - - static inline constexpr RECT qrect2rect(const QRect &qrect) { - return RECT{LONG(qrect.left()), LONG(qrect.top()), LONG(qrect.right()), - LONG(qrect.bottom())}; - } - - static inline /*constexpr*/ QString hwnd2str(const WId windowId) { - // NULL handle is allowed here. - return QLatin1String("0x") + - QString::number(windowId, 16).toUpper().rightJustified(8, u'0'); - } - - static inline /*constexpr*/ QString hwnd2str(HWND hwnd) { - // NULL handle is allowed here. - return hwnd2str(reinterpret_cast<WId>(hwnd)); - } - - static inline bool isWin8OrGreater() { - static const bool result = IsWindows8OrGreater_Real(); - return result; - } - - static inline bool isWin8Point1OrGreater() { - static const bool result = IsWindows8Point1OrGreater_Real(); - return result; - } - - static inline bool isWin10OrGreater() { - static const bool result = IsWindows10OrGreater_Real(); - return result; - } - - static inline bool isWin11OrGreater() { - static const bool result = IsWindows11OrGreater_Real(); - return result; - } - - static inline bool isWin1122H2OrGreater() { - static const bool result = IsWindows1122H2OrGreater_Real(); - return result; - } - - static inline bool isDwmCompositionEnabled() { - if (isWin8OrGreater()) { - return true; - } - const DynamicApis &apis = DynamicApis::instance(); - if (!apis.pDwmIsCompositionEnabled) { - return false; - } - BOOL enabled = FALSE; - return SUCCEEDED(apis.pDwmIsCompositionEnabled(&enabled)) && enabled; - } - - static inline bool isWindowFrameBorderColorized() { - const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)"); - if (!registry.isValid()) { - return false; - } - const auto value = registry.dwordValue(L"ColorPrevalence"); - if (!value.second) { - return false; - } - return value.first; - } - - static inline bool isDarkThemeActive() { -#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) - return QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark; -#else - const QWinRegistryKey registry( - HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)"); - if (!registry.isValid()) { - return false; - } - const auto value = registry.dwordValue(L"AppsUseLightTheme"); - if (!value.second) { - return false; - } - return !value.first; -#endif - } - - static inline bool isDarkWindowFrameEnabled(HWND hwnd) { - BOOL enabled = FALSE; - const DynamicApis &apis = DynamicApis::instance(); - if (SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd, _DWMWA_USE_IMMERSIVE_DARK_MODE, &enabled, - sizeof(enabled)))) { - return enabled; - } else if (SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd, - _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, - &enabled, sizeof(enabled)))) { - return enabled; - } else { - return false; - } - } - - static inline QColor getAccentColor() { -#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) - return QGuiApplication::palette().color(QPalette::Accent); -#else - const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)"); - if (!registry.isValid()) { - return {}; - } - const auto value = registry.dwordValue(L"AccentColor"); - if (!value.second) { - return {}; - } - // The retrieved value is in the #AABBGGRR format, we need to - // convert it to the #AARRGGBB format which Qt expects. - const QColor abgr = QColor::fromRgba(value.first); - if (!abgr.isValid()) { - return {}; - } - return QColor::fromRgb(abgr.blue(), abgr.green(), abgr.red(), abgr.alpha()); -#endif - } static inline void triggerFrameChange(HWND hwnd) { ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, @@ -990,131 +627,6 @@ #endif showSystemMenu2(hWnd, qpoint2point(nativeGlobalPos), false, m_delegate->isHostSizeFixed(m_host)); - return; - } - - case WindowAttributeChangedHook: { - auto args = static_cast<void **>(data); - const auto &key = *static_cast<const QString *>(args[0]); - const auto &newVar = *static_cast<const QVariant *>(args[1]); - const auto &oldVar = *static_cast<const QVariant *>(args[2]); - auto &res = *static_cast<bool *>(args[3]); - - if (!windowId) - return; - const auto hwnd = reinterpret_cast<HWND>(windowId); - - const DynamicApis &apis = DynamicApis::instance(); - - if (key == QStringLiteral("frame-shadow")) { - if (newVar.toBool()) { - // TODO: set off - } else { - // TODO: set on - } - } else if (key == QStringLiteral("mica")) { - if (!isWin11OrGreater()) { - return; - } - if (newVar.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); - } - } else if (key == QStringLiteral("mica-alt")) { - if (!isWin1122H2OrGreater()) { - return; - } - if (newVar.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); - } - res = true; - } else if (key == QStringLiteral("acrylic-material")) { - if (newVar.type() == QVariant::Color) { - // 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 = newVar.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); - } - res = true; - } return; } diff --git a/src/core/shared/qwkwindowsextra_p.h b/src/core/shared/qwkwindowsextra_p.h new file mode 100644 index 0000000..03adf7e --- /dev/null +++ b/src/core/shared/qwkwindowsextra_p.h @@ -0,0 +1,386 @@ +#ifndef QWKWINDOWSEXTRA_P_H +#define QWKWINDOWSEXTRA_P_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 <shellscalingapi.h> +#include <dwmapi.h> +#include <timeapi.h> + +#include <QWKCore/qwindowkit_windows.h> + +#include <QtCore/private/qsystemlibrary_p.h> +#include <QtCore/private/qwinregistry_p.h> + +#include <QtGui/QStyleHints> +#include <QtGui/QPalette> + +// Don't include this header in any header files. + +namespace QWK { + + enum _DWMWINDOWATTRIBUTE { + // [set] BOOL, Allows the use of host backdrop brushes for the window. + _DWMWA_USE_HOSTBACKDROPBRUSH = 17, + + // Undocumented, the same with DWMWA_USE_IMMERSIVE_DARK_MODE, but available on systems + // before Win10 20H1. + _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19, + + // [set] BOOL, Allows a window to either use the accent color, or dark, according to the + // user Color Mode preferences. + _DWMWA_USE_IMMERSIVE_DARK_MODE = 20, + + // [set] WINDOW_CORNER_PREFERENCE, Controls the policy that rounds top-level window corners + _DWMWA_WINDOW_CORNER_PREFERENCE = 33, + + // [get] UINT, width of the visible border around a thick frame window + _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37, + + // [get, set] SYSTEMBACKDROP_TYPE, Controls the system-drawn backdrop material of a window, + // including behind the non-client area. + _DWMWA_SYSTEMBACKDROP_TYPE = 38, + + // Undocumented, use this value to enable Mica material on Win11 21H2. You should use + // DWMWA_SYSTEMBACKDROP_TYPE instead on Win11 22H2 and newer. + _DWMWA_MICA_EFFECT = 1029 + }; + + // Types used with DWMWA_SYSTEMBACKDROP_TYPE + enum _DWM_SYSTEMBACKDROP_TYPE { + _DWMSBT_AUTO, // [Default] Let DWM automatically decide the system-drawn backdrop for this + // window. + _DWMSBT_NONE, // [Disable] Do not draw any system backdrop. + _DWMSBT_MAINWINDOW, // [Mica] Draw the backdrop material effect corresponding to a + // long-lived window. + _DWMSBT_TRANSIENTWINDOW, // [Acrylic] Draw the backdrop material effect corresponding to a + // transient window. + _DWMSBT_TABBEDWINDOW, // [Mica Alt] Draw the backdrop material effect corresponding to a + // window with a tabbed title bar. + }; + + enum WINDOWCOMPOSITIONATTRIB { + WCA_UNDEFINED = 0, + WCA_NCRENDERING_ENABLED = 1, + WCA_NCRENDERING_POLICY = 2, + WCA_TRANSITIONS_FORCEDISABLED = 3, + WCA_ALLOW_NCPAINT = 4, + WCA_CAPTION_BUTTON_BOUNDS = 5, + WCA_NONCLIENT_RTL_LAYOUT = 6, + WCA_FORCE_ICONIC_REPRESENTATION = 7, + WCA_EXTENDED_FRAME_BOUNDS = 8, + WCA_HAS_ICONIC_BITMAP = 9, + WCA_THEME_ATTRIBUTES = 10, + WCA_NCRENDERING_EXILED = 11, + WCA_NCADORNMENTINFO = 12, + WCA_EXCLUDED_FROM_LIVEPREVIEW = 13, + WCA_VIDEO_OVERLAY_ACTIVE = 14, + WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15, + WCA_DISALLOW_PEEK = 16, + WCA_CLOAK = 17, + WCA_CLOAKED = 18, + WCA_ACCENT_POLICY = 19, + WCA_FREEZE_REPRESENTATION = 20, + WCA_EVER_UNCLOAKED = 21, + WCA_VISUAL_OWNER = 22, + WCA_HOLOGRAPHIC = 23, + WCA_EXCLUDED_FROM_DDA = 24, + WCA_PASSIVEUPDATEMODE = 25, + WCA_USEDARKMODECOLORS = 26, + WCA_CORNER_STYLE = 27, + WCA_PART_COLOR = 28, + WCA_DISABLE_MOVESIZE_FEEDBACK = 29, + WCA_LAST = 30 + }; + + enum ACCENT_STATE { + ACCENT_DISABLED = 0, + ACCENT_ENABLE_GRADIENT = 1, + ACCENT_ENABLE_TRANSPARENTGRADIENT = 2, + ACCENT_ENABLE_BLURBEHIND = 3, // Traditional DWM blur + ACCENT_ENABLE_ACRYLICBLURBEHIND = 4, // RS4 1803 + ACCENT_ENABLE_HOST_BACKDROP = 5, // RS5 1809 + ACCENT_INVALID_STATE = 6 // Using this value will remove the window background + }; + + enum ACCENT_FLAG { + ACCENT_NONE = 0, + ACCENT_ENABLE_ACRYLIC = 1, + ACCENT_ENABLE_ACRYLIC_WITH_LUMINOSITY = 482 + }; + + struct ACCENT_POLICY { + DWORD dwAccentState; + DWORD dwAccentFlags; + DWORD dwGradientColor; // #AABBGGRR + DWORD dwAnimationId; + }; + using PACCENT_POLICY = ACCENT_POLICY *; + + struct WINDOWCOMPOSITIONATTRIBDATA { + WINDOWCOMPOSITIONATTRIB Attrib; + PVOID pvData; + SIZE_T cbData; + }; + using PWINDOWCOMPOSITIONATTRIBDATA = WINDOWCOMPOSITIONATTRIBDATA *; + + using SetWindowCompositionAttributePtr = BOOL(WINAPI *)(HWND, PWINDOWCOMPOSITIONATTRIBDATA); + + namespace { + + struct DynamicApis { + static const DynamicApis &instance() { + static const DynamicApis inst{}; + return inst; + } + +#define DYNAMIC_API_DECLARE(NAME) decltype(&::NAME) p##NAME = nullptr + + DYNAMIC_API_DECLARE(DwmFlush); + DYNAMIC_API_DECLARE(DwmIsCompositionEnabled); + DYNAMIC_API_DECLARE(DwmGetCompositionTimingInfo); + DYNAMIC_API_DECLARE(DwmGetWindowAttribute); + DYNAMIC_API_DECLARE(DwmSetWindowAttribute); + DYNAMIC_API_DECLARE(DwmExtendFrameIntoClientArea); + DYNAMIC_API_DECLARE(DwmEnableBlurBehindWindow); + DYNAMIC_API_DECLARE(GetDpiForWindow); + DYNAMIC_API_DECLARE(GetSystemMetricsForDpi); + DYNAMIC_API_DECLARE(GetDpiForMonitor); + DYNAMIC_API_DECLARE(timeGetDevCaps); + DYNAMIC_API_DECLARE(timeBeginPeriod); + DYNAMIC_API_DECLARE(timeEndPeriod); + +#undef DYNAMIC_API_DECLARE + + SetWindowCompositionAttributePtr pSetWindowCompositionAttribute = nullptr; + + private: + DynamicApis() { +#define DYNAMIC_API_RESOLVE(DLL, NAME) \ + p##NAME = reinterpret_cast<decltype(p##NAME)>(DLL.resolve(#NAME)) + + QSystemLibrary user32(QStringLiteral("user32")); + DYNAMIC_API_RESOLVE(user32, GetDpiForWindow); + DYNAMIC_API_RESOLVE(user32, GetSystemMetricsForDpi); + DYNAMIC_API_RESOLVE(user32, SetWindowCompositionAttribute); + + QSystemLibrary shcore(QStringLiteral("shcore")); + DYNAMIC_API_RESOLVE(shcore, GetDpiForMonitor); + + QSystemLibrary dwmapi(QStringLiteral("dwmapi")); + DYNAMIC_API_RESOLVE(dwmapi, DwmFlush); + DYNAMIC_API_RESOLVE(dwmapi, DwmIsCompositionEnabled); + DYNAMIC_API_RESOLVE(dwmapi, DwmGetCompositionTimingInfo); + DYNAMIC_API_RESOLVE(dwmapi, DwmGetWindowAttribute); + DYNAMIC_API_RESOLVE(dwmapi, DwmSetWindowAttribute); + DYNAMIC_API_RESOLVE(dwmapi, DwmExtendFrameIntoClientArea); + DYNAMIC_API_RESOLVE(dwmapi, DwmEnableBlurBehindWindow); + + QSystemLibrary winmm(QStringLiteral("winmm")); + DYNAMIC_API_RESOLVE(winmm, timeGetDevCaps); + DYNAMIC_API_RESOLVE(winmm, timeBeginPeriod); + DYNAMIC_API_RESOLVE(winmm, timeEndPeriod); + +#undef DYNAMIC_API_RESOLVE + } + + ~DynamicApis() = default; + + Q_DISABLE_COPY_MOVE(DynamicApis) + }; + + } + + static inline constexpr bool operator==(const POINT &lhs, const POINT &rhs) noexcept { + return ((lhs.x == rhs.x) && (lhs.y == rhs.y)); + } + + static inline constexpr bool operator!=(const POINT &lhs, const POINT &rhs) noexcept { + return !operator==(lhs, rhs); + } + + static inline constexpr bool operator==(const SIZE &lhs, const SIZE &rhs) noexcept { + return ((lhs.cx == rhs.cx) && (lhs.cy == rhs.cy)); + } + + static inline constexpr bool operator!=(const SIZE &lhs, const SIZE &rhs) noexcept { + return !operator==(lhs, rhs); + } + + static inline constexpr bool operator>(const SIZE &lhs, const SIZE &rhs) noexcept { + return ((lhs.cx * lhs.cy) > (rhs.cx * rhs.cy)); + } + + static inline constexpr bool operator>=(const SIZE &lhs, const SIZE &rhs) noexcept { + return (operator>(lhs, rhs) || operator==(lhs, rhs)); + } + + static inline constexpr bool operator<(const SIZE &lhs, const SIZE &rhs) noexcept { + return (operator!=(lhs, rhs) && !operator>(lhs, rhs)); + } + + static inline constexpr bool operator<=(const SIZE &lhs, const SIZE &rhs) noexcept { + return (operator<(lhs, rhs) || operator==(lhs, rhs)); + } + + static inline constexpr bool operator==(const RECT &lhs, const RECT &rhs) noexcept { + return ((lhs.left == rhs.left) && (lhs.top == rhs.top) && (lhs.right == rhs.right) && + (lhs.bottom == rhs.bottom)); + } + + static inline constexpr bool operator!=(const RECT &lhs, const RECT &rhs) noexcept { + return !operator==(lhs, rhs); + } + + static inline constexpr QPoint point2qpoint(const POINT &point) { + return QPoint{int(point.x), int(point.y)}; + } + + static inline constexpr POINT qpoint2point(const QPoint &point) { + return POINT{LONG(point.x()), LONG(point.y())}; + } + + static inline constexpr QSize size2qsize(const SIZE &size) { + return QSize{int(size.cx), int(size.cy)}; + } + + static inline constexpr SIZE qsize2size(const QSize &size) { + return SIZE{LONG(size.width()), LONG(size.height())}; + } + + static inline constexpr QRect rect2qrect(const RECT &rect) { + return QRect{ + QPoint{int(rect.left), int(rect.top) }, + QSize{int(RECT_WIDTH(rect)), int(RECT_HEIGHT(rect))} + }; + } + + static inline constexpr RECT qrect2rect(const QRect &qrect) { + return RECT{LONG(qrect.left()), LONG(qrect.top()), LONG(qrect.right()), + LONG(qrect.bottom())}; + } + + static inline /*constexpr*/ QString hwnd2str(const WId windowId) { + // NULL handle is allowed here. + return QLatin1String("0x") + + QString::number(windowId, 16).toUpper().rightJustified(8, u'0'); + } + + static inline /*constexpr*/ QString hwnd2str(HWND hwnd) { + // NULL handle is allowed here. + return hwnd2str(reinterpret_cast<WId>(hwnd)); + } + + static inline bool isWin8OrGreater() { + static const bool result = IsWindows8OrGreater_Real(); + return result; + } + + static inline bool isWin8Point1OrGreater() { + static const bool result = IsWindows8Point1OrGreater_Real(); + return result; + } + + static inline bool isWin10OrGreater() { + static const bool result = IsWindows10OrGreater_Real(); + return result; + } + + static inline bool isWin11OrGreater() { + static const bool result = IsWindows11OrGreater_Real(); + return result; + } + + static inline bool isWin1122H2OrGreater() { + static const bool result = IsWindows1122H2OrGreater_Real(); + return result; + } + + static inline bool isDwmCompositionEnabled() { + if (isWin8OrGreater()) { + return true; + } + const DynamicApis &apis = DynamicApis::instance(); + if (!apis.pDwmIsCompositionEnabled) { + return false; + } + BOOL enabled = FALSE; + return SUCCEEDED(apis.pDwmIsCompositionEnabled(&enabled)) && enabled; + } + + static inline bool isWindowFrameBorderColorized() { + const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)"); + if (!registry.isValid()) { + return false; + } + const auto value = registry.dwordValue(L"ColorPrevalence"); + if (!value.second) { + return false; + } + return value.first; + } + + static inline bool isDarkThemeActive() { +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + return QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark; +#else + const QWinRegistryKey registry( + HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)"); + if (!registry.isValid()) { + return false; + } + const auto value = registry.dwordValue(L"AppsUseLightTheme"); + if (!value.second) { + return false; + } + return !value.first; +#endif + } + + static inline bool isDarkWindowFrameEnabled(HWND hwnd) { + BOOL enabled = FALSE; + const DynamicApis &apis = DynamicApis::instance(); + if (SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd, _DWMWA_USE_IMMERSIVE_DARK_MODE, &enabled, + sizeof(enabled)))) { + return enabled; + } else if (SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd, + _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, + &enabled, sizeof(enabled)))) { + return enabled; + } else { + return false; + } + } + + static inline QColor getAccentColor() { +#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) + return QGuiApplication::palette().color(QPalette::Accent); +#else + const QWinRegistryKey registry(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\DWM)"); + if (!registry.isValid()) { + return {}; + } + const auto value = registry.dwordValue(L"AccentColor"); + if (!value.second) { + return {}; + } + // The retrieved value is in the #AABBGGRR format, we need to + // convert it to the #AARRGGBB format which Qt expects. + const QColor abgr = QColor::fromRgba(value.first); + if (!abgr.isValid()) { + return {}; + } + return QColor::fromRgb(abgr.blue(), abgr.green(), abgr.red(), abgr.alpha()); +#endif + } + +} + +#endif // QWKWINDOWSEXTRA_P_H diff --git a/src/core/kernel/systemwindow_p.h b/src/core/shared/systemwindow_p.h similarity index 100% rename from src/core/kernel/systemwindow_p.h rename to src/core/shared/systemwindow_p.h diff --git a/src/core/windowagentbase.cpp b/src/core/windowagentbase.cpp index 819dfb2..72b1229 100644 --- a/src/core/windowagentbase.cpp +++ b/src/core/windowagentbase.cpp @@ -50,16 +50,6 @@ 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); diff --git a/src/core/windowagentbase.h b/src/core/windowagentbase.h index 6e684bd..d377ba8 100644 --- a/src/core/windowagentbase.h +++ b/src/core/windowagentbase.h @@ -28,9 +28,6 @@ }; Q_ENUM(SystemButton) - QVariant windowAttribute(const QString &key) const; - bool setWindowAttribute(const QString &key, const QVariant &attribute); - public Q_SLOTS: void showSystemMenu(const QPoint &pos); // Only available on Windows now void centralize(); diff --git a/src/stylesupport/CMakeLists.txt b/src/stylesupport/CMakeLists.txt new file mode 100644 index 0000000..787fa8c --- /dev/null +++ b/src/stylesupport/CMakeLists.txt @@ -0,0 +1,49 @@ +project(QWKStyleSupport + VERSION ${QWINDOWKIT_VERSION} + LANGUAGES CXX +) + +set(_src + qwkstylesupportglobal.h + styleagent.h + styleagent_p.h + styleagent.cpp +) + +set(_links_private) + +if(WIN32) + list(APPEND _src + styleagent_win.cpp + ) +elseif(APPLE) + list(APPEND _links_private + "-framework Foundation" + "-framework Cocoa" + "-framework AppKit" + ) + list(APPEND _src + styleagent_mac.cpp + ) +else() + list(APPEND _src + styleagent_linux.cpp + ) +endif() + +qwk_add_library(${PROJECT_NAME} AUTOGEN + SOURCES ${_src} + LINKS QWKCore + LINKS_PRIVATE ${_links_private} + QT_LINKS Core Gui + QT_INCLUDE_PRIVATE Core Gui + PREFIX QWK_STYLESUPPORT +) + +set_target_properties(${PROJECT_NAME} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED TRUE +) + +set(QWINDOWKIT_ENABLED_TARGETS ${QWINDOWKIT_ENABLED_TARGETS} ${PROJECT_NAME} PARENT_SCOPE) +set(QWINDOWKIT_ENABLED_SUBDIRECTORIES ${QWINDOWKIT_ENABLED_SUBDIRECTORIES} stylesupport PARENT_SCOPE) \ No newline at end of file diff --git a/src/stylesupport/qwkstylesupportglobal.h b/src/stylesupport/qwkstylesupportglobal.h new file mode 100644 index 0000000..27f8e31 --- /dev/null +++ b/src/stylesupport/qwkstylesupportglobal.h @@ -0,0 +1,18 @@ +#ifndef QWKSTYLESUPPORTGLOBAL_H +#define QWKSTYLESUPPORTGLOBAL_H + +#include <QtCore/QtGlobal> + +#ifndef QWK_STYLESUPPORT_EXPORT +# ifdef QWK_STYLESUPPORT_STATIC +# define QWK_STYLESUPPORT_EXPORT +# else +# ifdef QWK_STYLESUPPORT_LIBRARY +# define QWK_STYLESUPPORT_EXPORT Q_DECL_EXPORT +# else +# define QWK_STYLESUPPORT_EXPORT Q_DECL_IMPORT +# endif +# endif +#endif + +#endif // QWKSTYLESUPPORTGLOBAL_H diff --git a/src/stylesupport/styleagent.cpp b/src/stylesupport/styleagent.cpp new file mode 100644 index 0000000..d24186f --- /dev/null +++ b/src/stylesupport/styleagent.cpp @@ -0,0 +1,83 @@ +#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(); + } + + void StyleAgentPrivate::_q_windowDestroyed() { + windowAttributes.remove(static_cast<QWindow *>(sender())); + } + + 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; + } + + QVariant StyleAgent::windowAttribute(QWindow *window, const QString &key) const { + Q_D(const StyleAgent); + return d->windowAttributes.value(window).value(key); + } + + bool StyleAgent::setWindowAttribute(QWindow *window, const QString &key, + const QVariant &attribute) { + Q_D(StyleAgent); + if (!window) + return false; + + auto it = d->windowAttributes.find(window); + if (it == d->windowAttributes.end()) { + if (!attribute.isValid()) + return true; + if (!d->updateWindowAttribute(window, key, attribute, {})) + return false; + connect(window, &QWindow::destroyed, d, &StyleAgentPrivate::_q_windowDestroyed); + d->windowAttributes.insert(window, QVariantHash{ + {key, attribute} + }); + } else { + auto &attributes = it.value(); + auto oldAttribute = attributes.value(key); + if (oldAttribute == attribute) + return true; + if (!d->updateWindowAttribute(window, key, attribute, oldAttribute)) + return false; + attributes.insert(key, attribute); + } + return true; + } + + StyleAgent::StyleAgent(StyleAgentPrivate &d, QObject *parent) : QObject(parent), d_ptr(&d) { + d.q_ptr = this; + + d.init(); + } + +} diff --git a/src/stylesupport/styleagent.h b/src/stylesupport/styleagent.h new file mode 100644 index 0000000..94afa33 --- /dev/null +++ b/src/stylesupport/styleagent.h @@ -0,0 +1,47 @@ +#ifndef STYLEAGENT_H +#define STYLEAGENT_H + +#include <memory> + +#include <QtCore/QObject> +#include <QtGui/QWindow> + +#include <QWKStyleSupport/qwkstylesupportglobal.h> + +namespace QWK { + + class StyleAgentPrivate; + + class QWK_STYLESUPPORT_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; + + QVariant windowAttribute(QWindow *window, const QString &key) const; + bool setWindowAttribute(QWindow *window, const QString &key, const QVariant &attribute); + + Q_SIGNALS: + void systemThemeChanged(); // Do we need wallpaper change notify? + + protected: + StyleAgent(StyleAgentPrivate &d, QObject *parent = nullptr); + + const std::unique_ptr<StyleAgentPrivate> d_ptr; + }; + +} + +#endif // STYLEAGENT_H \ No newline at end of file diff --git a/src/stylesupport/styleagent_linux.cpp b/src/stylesupport/styleagent_linux.cpp new file mode 100644 index 0000000..3c83589 --- /dev/null +++ b/src/stylesupport/styleagent_linux.cpp @@ -0,0 +1,20 @@ +#include "styleagent_p.h" + +#include <QtCore/QVariant> + +namespace QWK { + + void StyleAgentPrivate::setupSystemThemeHook() { + } + + void StyleAgentPrivate::removeSystemThemeHook() { + } + + bool StyleAgentPrivate::updateWindowAttribute(QWindow *window, const QString &key, + const QVariant &attribute, + const QVariant &oldAttribute) { + Q_UNUSED(oldAttribute) + return false; + } + +} \ No newline at end of file diff --git a/src/stylesupport/styleagent_mac.mm b/src/stylesupport/styleagent_mac.mm new file mode 100644 index 0000000..64cdf16 --- /dev/null +++ b/src/stylesupport/styleagent_mac.mm @@ -0,0 +1,29 @@ +#include "styleagent_p.h" + +#include <QtCore/QVariant> + +namespace QWK { + + void StyleAgentPrivate::setupSystemThemeHook() { + } + + void StyleAgentPrivate::removeSystemThemeHook() { + } + + bool StyleAgentPrivate::updateWindowAttribute(QWindow *window, const QString &key, + const QVariant &attribute, + const QVariant &oldAttribute) { + Q_UNUSED(oldAttribute) + + if (key == QStringLiteral("no-system-buttons")) { + if (attribute.toBool()) { + // TODO: set off + } else { + // TODO: set on + } + return true; + } + return false; + } + +} \ No newline at end of file diff --git a/src/stylesupport/styleagent_p.h b/src/stylesupport/styleagent_p.h new file mode 100644 index 0000000..739a0b1 --- /dev/null +++ b/src/stylesupport/styleagent_p.h @@ -0,0 +1,44 @@ +#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 <QWKStyleSupport/styleagent.h> +#include <QtCore/QHash> + +namespace QWK { + + class StyleAgentPrivate : public QObject { + Q_DECLARE_PUBLIC(StyleAgent) + public: + StyleAgentPrivate(); + ~StyleAgentPrivate() override; + + void init(); + + StyleAgent *q_ptr; + + StyleAgent::SystemTheme systemTheme = StyleAgent::Dark; + QHash<QWindow *, QVariantHash> windowAttributes; + + virtual void setupSystemThemeHook(); + virtual void removeSystemThemeHook(); + virtual bool updateWindowAttribute(QWindow *window, const QString &key, + const QVariant &attribute, const QVariant &oldAttribute); + + void notifyThemeChanged(StyleAgent::SystemTheme theme); + + private: + void _q_windowDestroyed(); + }; + +} + +#endif // STYLEAGENTPRIVATE_H \ No newline at end of file diff --git a/src/stylesupport/styleagent_win.cpp b/src/stylesupport/styleagent_win.cpp new file mode 100644 index 0000000..67b6532 --- /dev/null +++ b/src/stylesupport/styleagent_win.cpp @@ -0,0 +1,242 @@ +#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(); + } + } + + bool StyleAgentPrivate::updateWindowAttribute(QWindow *window, const QString &key, + const QVariant &attribute, + const QVariant &oldAttribute) { + Q_UNUSED(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; + } + +} \ No newline at end of file diff --git a/src/widgets/widgetwindowagent.cpp b/src/widgets/widgetwindowagent.cpp index a15a7ed..b510efb 100644 --- a/src/widgets/widgetwindowagent.cpp +++ b/src/widgets/widgetwindowagent.cpp @@ -9,27 +9,6 @@ namespace QWK { - class WidgetWinIdChangeEventFilter : public QObject { - public: - explicit WidgetWinIdChangeEventFilter(QWidget *widget, AbstractWindowContext *ctx) - : QObject(ctx), widget(widget), ctx(ctx) { - widget->installEventFilter(this); - } - - protected: - bool eventFilter(QObject *obj, QEvent *event) override { - Q_UNUSED(obj) - if (event->type() == QEvent::WinIdChange) { - ctx->notifyWinIdChange(); - } - return false; - } - - protected: - QWidget *widget; - AbstractWindowContext *ctx; - }; - WidgetWindowAgentPrivate::WidgetWindowAgentPrivate() = default; WidgetWindowAgentPrivate::~WidgetWindowAgentPrivate() = default; @@ -63,8 +42,6 @@ #ifdef Q_OS_WINDOWS d->setupWindows10BorderWorkaround(); #endif - std::ignore = new WidgetWinIdChangeEventFilter(w, d->context.get()); - return true; } -- Gitblit v1.9.1