From 5a0e5a9261c251dba54afb8cc0b6b32ae9d95510 Mon Sep 17 00:00:00 2001 From: Zhao Yuhang <2546789017@qq.com> Date: 周二, 12 12月 2023 19:50:34 +0800 Subject: [PATCH] add theme win32 message --- src/core/contexts/win32windowcontext.cpp | 283 ++++++++++++++++++++++++++++++++----------------------- 1 files changed, 164 insertions(+), 119 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index ea68c0d..372d0c5 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -27,6 +27,9 @@ #include <timeapi.h> #include "nativeeventfilter.h" +#include "qwkglobal_p.h" + +#include "win10borderhandler_p.h" #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) Q_DECLARE_METATYPE(QMargins) @@ -34,37 +37,42 @@ namespace QWK { - using _DWMWINDOWATTRIBUTE = enum _DWMWINDOWATTRIBUTE { - _DWMWA_USE_HOSTBACKDROPBRUSH = - 17, // [set] BOOL, Allows the use of host backdrop brushes for the window. - _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = - 19, // Undocumented, the same with DWMWA_USE_IMMERSIVE_DARK_MODE, but available on - // systems before Win10 20H1. - _DWMWA_USE_IMMERSIVE_DARK_MODE = - 20, // [set] BOOL, Allows a window to either use the accent color, or dark, according to - // the user Color Mode preferences. - _DWMWA_WINDOW_CORNER_PREFERENCE = 33, // [set] WINDOW_CORNER_PREFERENCE, Controls the policy - // that rounds top-level window corners - _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = - 37, // [get] UINT, width of the visible border around a thick frame window - _DWMWA_SYSTEMBACKDROP_TYPE = - 38, // [get, set] SYSTEMBACKDROP_TYPE, Controls the system-drawn backdrop material of a - // window, including behind the non-client area. - _DWMWA_MICA_EFFECT = - 1029 // Undocumented, use this value to enable Mica material on Win11 21H2. You should - // use DWMWA_SYSTEMBACKDROP_TYPE instead on Win11 22H2 and newer. + 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 }; // The thickness of an auto-hide taskbar in pixels. - static constexpr const auto kAutoHideTaskBarThickness = quint8{2}; + static constexpr const quint8 kAutoHideTaskBarThickness = 2; - static inline constexpr const auto kFrameBorderActiveColorLight = - QColor{110, 110, 110}; // #6E6E6E - static inline constexpr const auto kFrameBorderActiveColorDark = QColor{51, 51, 51}; // #333333 - static inline constexpr const auto kFrameBorderInactiveColorLight = - QColor{167, 167, 167}; // #A7A7A7 - static inline constexpr const auto kFrameBorderInactiveColorDark = - QColor{61, 61, 62}; // #3D3D3E + QWK_USED static constexpr const struct { + const uint32_t activeLight = MAKE_RGBA_COLOR(110, 110, 110, 255); // #6E6E6E + const uint32_t activeDark = MAKE_RGBA_COLOR(51, 51, 51, 255); // #333333 + const uint32_t inactiveLight = MAKE_RGBA_COLOR(167, 167, 167, 255); // #A7A7A7 + const uint32_t inactiveDark = MAKE_RGBA_COLOR(61, 61, 62, 255); // #3D3D3E + } kWindowsColorSet; // hWnd -> context using WndProcHash = QHash<HWND, Win32WindowContext *>; @@ -351,44 +359,37 @@ } } - static inline quint32 getWindowFrameBorderThickness(HWND hwnd) { - UINT result{0}; + static inline quint32 getSystemMetricsForDpi(int index, quint32 dpi) { const DynamicApis &apis = DynamicApis::instance(); - if (SUCCEEDED(apis.pDwmGetWindowAttribute(hwnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, - &result, sizeof(result)))) { - return result; - } else { - const quint32 dpi = getDpiForWindow(hwnd); - result = quint32(std::round(qreal(1) * qreal(dpi) / qreal(USER_DEFAULT_SCREEN_DPI))); + if (apis.pGetSystemMetricsForDpi) { + return ::GetSystemMetricsForDpi(index, dpi); + } + return ::GetSystemMetrics(index); + } + + static inline quint32 getWindowFrameBorderThickness(HWND hwnd) { + const DynamicApis &apis = DynamicApis::instance(); + if (UINT result = 0; SUCCEEDED(apis.pDwmGetWindowAttribute( + hwnd, _DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, &result, sizeof(result)))) { return result; } + return getSystemMetricsForDpi(SM_CXBORDER, getDpiForWindow(hwnd)); } static inline quint32 getResizeBorderThickness(HWND hwnd) { - const DynamicApis &apis = DynamicApis::instance(); - if (apis.pGetSystemMetricsForDpi) { - const quint32 dpi = getDpiForWindow(hwnd); - return apis.pGetSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + - apis.pGetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); - } else { - return ::GetSystemMetrics(SM_CXSIZEFRAME) + ::GetSystemMetrics(SM_CXPADDEDBORDER); - } + const quint32 dpi = getDpiForWindow(hwnd); + return getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + + getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); } static inline quint32 getTitleBarHeight(HWND hwnd) { - const auto captionHeight = [hwnd]() -> int { - const DynamicApis &apis = DynamicApis::instance(); - if (apis.pGetSystemMetricsForDpi) { - const quint32 dpi = getDpiForWindow(hwnd); - return apis.pGetSystemMetricsForDpi(SM_CYCAPTION, dpi); - } else { - return ::GetSystemMetrics(SM_CYCAPTION); - } - }(); - return captionHeight + getResizeBorderThickness(hwnd); + const quint32 dpi = getDpiForWindow(hwnd); + return getSystemMetricsForDpi(SM_CYCAPTION, dpi) + + getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + + getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); } - static inline void updateInternalWindowFrameMargins(HWND hwnd, QWindow *window) { + static void updateInternalWindowFrameMargins(HWND hwnd, QWindow *window) { const auto margins = [hwnd]() -> QMargins { const auto titleBarHeight = int(getTitleBarHeight(hwnd)); if (isWin10OrGreater()) { @@ -454,7 +455,7 @@ #endif } - static inline void syncPaintEventWithDwm() { + static void syncPaintEventWithDwm() { // No need to sync with DWM if DWM composition is disabled. if (!isDwmCompositionEnabled()) { return; @@ -499,9 +500,9 @@ apis.ptimeEndPeriod(ms_granularity); } - static inline void showSystemMenu2(HWND hWnd, const POINT &pos, const bool selectFirstEntry, - const bool fixedSize) { - const HMENU hMenu = ::GetSystemMenu(hWnd, FALSE); + static void showSystemMenu2(HWND hWnd, const POINT &pos, const bool selectFirstEntry, + const bool fixedSize) { + HMENU hMenu = ::GetSystemMenu(hWnd, FALSE); if (!hMenu) { // The corresponding window doesn't have a system menu, most likely due to the // lack of the "WS_SYSMENU" window style. This situation should not be treated @@ -794,6 +795,7 @@ moveToDesktopCenter(hwnd); return; } + case ShowSystemMenuHook: { const auto &pos = *static_cast<const QPoint *>(data); auto hWnd = reinterpret_cast<HWND>(m_windowHandle->winId()); @@ -801,72 +803,33 @@ m_delegate->isHostSizeFixed(m_host)); return; } - case NeedsDrawBordersHook: { - auto &result = *static_cast<bool *>(data); - result = isWin10OrGreater() && !isWin11OrGreater(); + + case DefaultColorsHook: { + auto &map = *static_cast<QMap<QString, QColor> *>(data); + map.clear(); + map.insert(QStringLiteral("activeLight"), kWindowsColorSet.activeLight); + map.insert(QStringLiteral("activeDark"), kWindowsColorSet.activeDark); + map.insert(QStringLiteral("inactiveLight"), kWindowsColorSet.inactiveLight); + map.insert(QStringLiteral("inactiveDark"), kWindowsColorSet.inactiveDark); return; } - case DrawBordersHook: { - auto args = static_cast<void **>(data); - auto &painter = *static_cast<QPainter *>(args[0]); - const auto &rect = *static_cast<const QRect *>(args[1]); - const auto ®ion = *static_cast<const QRegion *>(args[2]); - const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId()); - QPen pen; - const auto borderThickness = int(QHighDpi::fromNativePixels( - getWindowFrameBorderThickness(hwnd), m_windowHandle)); - pen.setWidth(borderThickness * 2); - const bool active = m_host->isWidgetType() - ? m_host->property("isActiveWindow").toBool() - : m_host->property("active").toBool(); - const bool dark = isDarkThemeActive() && isDarkWindowFrameEnabled(hwnd); - if (active) { - if (isWindowFrameBorderColorized()) { - pen.setColor(getAccentColor()); - } else { - if (dark) { - pen.setColor(kFrameBorderActiveColorDark); - } else { - pen.setColor(kFrameBorderActiveColorLight); - } - } - } else { - if (dark) { - pen.setColor(kFrameBorderInactiveColorDark); - } else { - pen.setColor(kFrameBorderInactiveColorLight); - } - } - painter.save(); - painter.setRenderHint( - QPainter::Antialiasing); // ### TODO: do we need to enable or disable it? - painter.setPen(pen); - painter.drawLine(QLine{ - QPoint{0, 0}, - QPoint{rect.width(), 0} - }); - painter.restore(); - return; - } - case QueryBorderThicknessHook: { - auto args = static_cast<void **>(data); - const bool requireNative = *static_cast<const bool *>(args[0]); - quint32 &thickness = *static_cast<quint32 *>(args[1]); - const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId()); - const auto nativeThickness = getWindowFrameBorderThickness(hwnd); - if (requireNative) { - thickness = nativeThickness; - } else { - thickness = QHighDpi::fromNativePixels(nativeThickness, m_windowHandle); - } - return; - } + default: { // unreachable break; } } AbstractWindowContext::virtual_hook(id, data); + } + + bool Win32WindowContext::needWin10BorderHandler() const { + return isWin10OrGreater() && !isWin11OrGreater(); + } + + void Win32WindowContext::setWin10BorderHandler(Win10BorderHandler *handler) { + win10BorderHandler.reset(handler); + handler->setBorderThickness( + int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId)))); } bool Win32WindowContext::setupHost() { @@ -929,14 +892,19 @@ return true; } + // Whether to show system menu if (systemMenuHandler(hWnd, message, wParam, lParam, result)) { + return true; + } + + if (themeStuffHandler(hWnd, message, wParam, lParam, result)) { return true; } return false; // Not handled } - static constexpr const struct { + QWK_USED static constexpr const struct { const WPARAM wParam = 0xF1C9ADD4; const LPARAM lParam = 0xAFB6F4C6; } kMessageTag; @@ -1080,7 +1048,7 @@ LPARAM lParam, LRESULT *result) { switch (message) { case WM_MOUSELEAVE: { - if (wParam == kMessageTag.wParam) { + if (wParam != kMessageTag.wParam) { // Qt will call TrackMouseEvent() to get the WM_MOUSELEAVE message when it // receives WM_MOUSEMOVE messages, and since we are converting every // WM_NCMOUSEMOVE message to WM_MOUSEMOVE message and send it back to the window @@ -1343,7 +1311,7 @@ // this is also the normal behavior of a native Win32 window (but only when the // window is not maximized/fullscreen/minimized, of course). if (isWindowNoState(hWnd)) { - static constexpr const auto kBorderSize = quint8{2}; + static constexpr const quint8 kBorderSize = 2; bool isTop = (nativeLocalPos.y <= kBorderSize); bool isLeft = nativeLocalPos.x <= kBorderSize; bool isRight = (nativeLocalPos.x >= (clientWidth - kBorderSize)); @@ -1919,4 +1887,81 @@ return false; } + bool Win32WindowContext::themeStuffHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, + LRESULT *resul) + { + switch (message) { + case WM_DPICHANGED: { + const auto dpiX = UINT(LOWORD(wParam)); + const auto dpiY = UINT(HIWORD(wParam)); + break; + } + case WM_THEMECHANGED: + case WM_SYSCOLORCHANGE: { + break; + } + case WM_DWMCOLORIZATIONCOLORCHANGED: { + const QColor color = QColor::fromRgba(wParam); + const auto blendWithOpacity = *reinterpret_cast<LPBOOL>(lParam); + break; + } + case WM_SETTINGCHANGE: { + if (!wParam && lParam && std::wcscmp(reinterpret_cast<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0) { + const QColor color = getAccentColor(); + } + break; + } + case WM_SIZE: { + const bool max = wParam == SIZE_MAXIMIZED; + const bool min = wParam == SIZE_MINIMIZED; + const bool full = isFullScreen(hWnd); + break; + } + case WM_ACTIVATE: { + const auto state = LOWORD(wParam); + const bool active = state == WA_ACTIVE || state == WA_CLICKACTIVE; + break; + } + default: + break; + } + return false; + } + + void Win10BorderHandler::paintBorder(QPainter &painter, const QRect &rect, + const QRegion ®ion) { + Q_UNUSED(rect) + Q_UNUSED(region) + + QPen pen; + pen.setWidth(m_borderThickness * 2); + + const bool dark = isDarkThemeActive() && + isDarkWindowFrameEnabled(reinterpret_cast<HWND>(m_window->winId())); + if (isActive()) { + if (isWindowFrameBorderColorized()) { + pen.setColor(getAccentColor()); + } else { + static QColor frameBorderActiveColorLight(kWindowsColorSet.activeLight); + static QColor frameBorderActiveColorDark(kWindowsColorSet.activeDark); + pen.setColor(dark ? frameBorderActiveColorDark : frameBorderActiveColorLight); + } + } else { + static QColor frameBorderInactiveColorLight(kWindowsColorSet.inactiveLight); + static QColor frameBorderInactiveColorDark(kWindowsColorSet.inactiveDark); + pen.setColor(dark ? frameBorderInactiveColorDark : frameBorderInactiveColorLight); + } + painter.save(); + + // ### TODO: do we need to enable or disable it? + painter.setRenderHint(QPainter::Antialiasing); + + painter.setPen(pen); + painter.drawLine(QLine{ + QPoint{0, 0}, + QPoint{m_window->width(), 0} + }); + painter.restore(); + } + } -- Gitblit v1.9.1