From 9e513416dc78bbc662b9745478cabaca8891777f Mon Sep 17 00:00:00 2001 From: Sine Striker <trueful@163.com> Date: ćšć, 21 12æ 2023 19:20:19 +0800 Subject: [PATCH] Add stylesupport --- src/core/contexts/abstractwindowcontext.cpp | 44 src/core/contexts/cocoawindowcontext.mm | 18 src/core/contexts/abstractwindowcontext_p.h | 10 src/core/windowagentbase.h | 3 src/stylesupport/styleagent_win.cpp | 130 +++++ src/core/shared/qwkwindowsextra_p.h | 381 +++++++++++++++ src/stylesupport/styleagent_mac.mm | 24 + src/core/shared/systemwindow_p.h | 0 CMakeLists.txt | 1 src/core/windowagentbase.cpp | 10 src/core/contexts/win32windowcontext.cpp | 490 -------------------- src/stylesupport/styleagent.cpp | 67 ++ src/stylesupport/styleagent_p.h | 30 + src/widgets/widgetwindowagent.cpp | 23 src/stylesupport/styleagent_linux.cpp | 16 src/stylesupport/qwkstylesupportglobal.h | 18 share/qmake/QWKStyleSupport.pri.in | 11 src/core/CMakeLists.txt | 17 src/CMakeLists.txt | 4 src/stylesupport/CMakeLists.txt | 49 ++ src/stylesupport/styleagent.h | 37 + share/install.cmake | 4 22 files changed, 805 insertions(+), 582 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/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..a568bfd 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) { 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 953186c..5575218 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..c4b5002 --- /dev/null +++ b/src/core/shared/qwkwindowsextra_p.h @@ -0,0 +1,381 @@ +#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> + +// 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(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 + } + +} + +#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..d8bc9b0 --- /dev/null +++ b/src/stylesupport/styleagent.cpp @@ -0,0 +1,67 @@ +#include "styleagent.h" +#include "styleagent_p.h" + +#include <QtCore/QVariant> + +namespace QWK { + + StyleAgentPrivate::StyleAgentPrivate() { + } + + StyleAgentPrivate::~StyleAgentPrivate() { + } + + void StyleAgentPrivate::init() { + setupSystemThemeHook(); + } + + void StyleAgentPrivate::_q_windowDestroyed() { + windowAttributes.remove(static_cast<QWindow *>(sender())); + } + + StyleAgent::StyleAgent(QObject *parent) : StyleAgent(*new StyleAgentPrivate(), parent) { + } + + StyleAgent::~StyleAgent() { + } + + 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..8474820 --- /dev/null +++ b/src/stylesupport/styleagent.h @@ -0,0 +1,37 @@ +#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; + + public: + QVariant windowAttribute(QWindow *window, const QString &key) const; + bool setWindowAttribute(QWindow *window, const QString &key, const QVariant &attribute); + + Q_SIGNALS: + void systemThemeChanged(); + + 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..b5a6064 --- /dev/null +++ b/src/stylesupport/styleagent_linux.cpp @@ -0,0 +1,16 @@ +#include "styleagent_p.h" + +#include <QtCore/QVariant> + +namespace QWK { + + void StyleAgentPrivate::setupSystemThemeHook() { + } + + bool StyleAgentPrivate::updateWindowAttribute(QWindow *window, const QString &key, + const QVariant &attribute, + const QVariant &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..a4dbe86 --- /dev/null +++ b/src/stylesupport/styleagent_mac.mm @@ -0,0 +1,24 @@ +#include "styleagent_p.h" + +#include <QtCore/QVariant> + +namespace QWK { + + void StyleAgentPrivate::setupSystemThemeHook() { + } + + bool StyleAgentPrivate::updateWindowAttribute(QWindow *window, 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; + } + +} \ 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..b07bc4e --- /dev/null +++ b/src/stylesupport/styleagent_p.h @@ -0,0 +1,30 @@ +#ifndef STYLEAGENTPRIVATE_H +#define STYLEAGENTPRIVATE_H + +#include <QWKStyleSupport/styleagent.h> + +namespace QWK { + + class StyleAgentPrivate : public QObject { + Q_DECLARE_PUBLIC(StyleAgent) + public: + StyleAgentPrivate(); + ~StyleAgentPrivate() override; + + void init(); + + StyleAgent *q_ptr; + + QHash<QWindow *, QVariantHash> windowAttributes; + + virtual void setupSystemThemeHook(); + virtual bool updateWindowAttribute(QWindow *window, const QString &key, + const QVariant &attribute, const QVariant &oldAttribute); + + 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..7a910d4 --- /dev/null +++ b/src/stylesupport/styleagent_win.cpp @@ -0,0 +1,130 @@ +#include "styleagent_p.h" + +#include <QtCore/QVariant> + +#include <QWKCore/private/qwkwindowsextra_p.h> + +namespace QWK { + + void StyleAgentPrivate::setupSystemThemeHook() { + } + + bool StyleAgentPrivate::updateWindowAttribute(QWindow *window, 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("frame-shadow")) { + if (attribute.toBool()) { + // TODO: set off + } else { + // TODO: set on + } + } else 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 (attribute.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 = 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; + } + 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