From d61db440e1c757a656ec2ab91f0c2f39ccd5d5f3 Mon Sep 17 00:00:00 2001 From: Yuhang Zhao <zhaoyuhang@rankyee.com> Date: ćšć, 28 12æ 2023 15:19:56 +0800 Subject: [PATCH] minor tweaks --- src/core/contexts/win32windowcontext.cpp | 262 ++++++++++++++++++++++++++++------------------------ 1 files changed, 141 insertions(+), 121 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index d78ce81..a41d110 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -5,10 +5,10 @@ #include <QtCore/QHash> #include <QtCore/QScopeGuard> #include <QtCore/QTimer> +#include <QtCore/QDateTime> #include <QtGui/QGuiApplication> #include <QtGui/QPainter> #include <QtGui/QPalette> -#include <QtGui/QStyleHints> #include <QtGui/private/qhighdpiscaling_p.h> #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) @@ -25,10 +25,6 @@ #include "qwkglobal_p.h" #include "qwkwindowsextra_p.h" - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) -Q_DECLARE_METATYPE(QMargins) -#endif namespace QWK { @@ -50,13 +46,13 @@ static WNDPROC g_qtWindowProc = nullptr; static inline bool -#if !QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER) +#if !QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) constexpr #endif isSystemBorderEnabled() { return -#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER) +#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) isWin10OrGreater() #else false @@ -68,55 +64,6 @@ ::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED); - } - - static inline quint32 getDpiForWindow(HWND hwnd) { - const DynamicApis &apis = DynamicApis::instance(); - if (apis.pGetDpiForWindow) { // Win10 - return apis.pGetDpiForWindow(hwnd); - } else if (apis.pGetDpiForMonitor) { // Win8.1 - HMONITOR monitor = ::MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); - UINT dpiX{0}; - UINT dpiY{0}; - apis.pGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); - return dpiX; - } else { // Win2K - HDC hdc = ::GetDC(nullptr); - const int dpiX = ::GetDeviceCaps(hdc, LOGPIXELSX); - // const int dpiY = ::GetDeviceCaps(hdc, LOGPIXELSY); - ::ReleaseDC(nullptr, hdc); - return quint32(dpiX); - } - } - - static inline quint32 getSystemMetricsForDpi(int index, quint32 dpi) { - const DynamicApis &apis = DynamicApis::instance(); - 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 quint32 dpi = getDpiForWindow(hwnd); - return getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + - getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); - } - - static inline quint32 getTitleBarHeight(HWND hwnd) { - const quint32 dpi = getDpiForWindow(hwnd); - return getSystemMetricsForDpi(SM_CYCAPTION, dpi) + - getSystemMetricsForDpi(SM_CXSIZEFRAME, dpi) + - getSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi); } static void setInternalWindowFrameMargins(QWindow *window, const QMargins &margins) { @@ -454,8 +401,8 @@ return false; } - static WindowsNativeEventFilter *instance; - static Win32WindowContext *lastMessageContext; + static inline WindowsNativeEventFilter *instance = nullptr; + static inline Win32WindowContext *lastMessageContext = nullptr; static inline void install() { if (instance) { @@ -472,9 +419,6 @@ instance = nullptr; } }; - - WindowsNativeEventFilter *WindowsNativeEventFilter::instance = nullptr; - Win32WindowContext *WindowsNativeEventFilter::lastMessageContext = nullptr; // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowscontext.cpp#L1025 // We can see from the source code that Qt will filter out some messages first and then send the @@ -626,6 +570,7 @@ case RaiseWindowHook: { if (!windowId) return; + m_delegate->setWindowVisible(m_host, true); const auto hwnd = reinterpret_cast<HWND>(windowId); bringWindowToFront(hwnd); return; @@ -658,6 +603,7 @@ } case DrawWindows10BorderHook: { +#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) if (!windowId) return; @@ -668,7 +614,7 @@ const auto hwnd = reinterpret_cast<HWND>(windowId); QPen pen; - pen.setWidth(getWindowFrameBorderThickness(hwnd) * 2); + pen.setWidth(int(getWindowFrameBorderThickness(hwnd)) * 2); const bool dark = isDarkThemeActive() && isDarkWindowFrameEnabled(hwnd); if (m_delegate->isWindowActive(m_host)) { @@ -688,7 +634,7 @@ } painter.save(); - // We needs anti-aliasing to give us better result. + // We need antialiasing to give us better result. painter.setRenderHint(QPainter::Antialiasing); painter.setPen(pen); @@ -697,6 +643,33 @@ QPoint{m_windowHandle->width(), 0} }); painter.restore(); +#endif + return; + } + + case DrawWindows10BorderHook2: { +#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) + if (!m_windowHandle) + return; + + // https://github.com/microsoft/terminal/blob/71a6f26e6ece656084e87de1a528c4a8072eeabd/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L1025 + // https://docs.microsoft.com/en-us/windows/win32/dwm/customframe#extending-the-client-frame + // Draw a black rectangle to make Windows native top border show + + auto hWnd = reinterpret_cast<HWND>(windowId); + HDC hdc = ::GetDC(hWnd); + RECT windowRect{}; + ::GetClientRect(hWnd, &windowRect); + RECT rcTopBorder = { + 0, + 0, + RECT_WIDTH(windowRect), + int(getWindowFrameBorderThickness(hWnd)), + }; + ::FillRect(hdc, &rcTopBorder, + reinterpret_cast<HBRUSH>(::GetStockObject(BLACK_BRUSH))); + ::ReleaseDC(hWnd, hdc); +#endif return; } @@ -706,13 +679,41 @@ AbstractWindowContext::virtual_hook(id, data); } - bool Win32WindowContext::needBorderPainter() const { - Q_UNUSED(this) - return isSystemBorderEnabled() && !isWin11OrGreater(); - } + QVariant Win32WindowContext::windowAttribute(const QString &key) const { + if (key == QStringLiteral("title-bar-rect")) { + if (!m_windowHandle) + return {}; - int Win32WindowContext::borderThickness() const { - return getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId)); + RECT frame{}; + auto hwnd = reinterpret_cast<HWND>(windowId); + // According to MSDN, WS_OVERLAPPED is not allowed for AdjustWindowRect. + auto style = static_cast<DWORD>(::GetWindowLongPtrW(hwnd, GWL_STYLE) & ~WS_OVERLAPPED); + auto exStyle = static_cast<DWORD>(::GetWindowLongPtrW(hwnd, GWL_EXSTYLE)); + const DynamicApis &apis = DynamicApis::instance(); + if (apis.pAdjustWindowRectExForDpi) { + apis.pAdjustWindowRectExForDpi(&frame, style, FALSE, exStyle, + getDpiForWindow(hwnd)); + } else { + ::AdjustWindowRectEx(&frame, style, FALSE, exStyle); + } + frame.left = std::abs(frame.left); + frame.top = std::abs(frame.top); + frame.right = std::abs(frame.right); + frame.bottom = std::abs(frame.bottom); + return QVariant::fromValue(rect2qrect(frame)); + } + + if (key == QStringLiteral("win10-border-needed")) { + return isSystemBorderEnabled() && !isWin11OrGreater(); + } + + if (key == QStringLiteral("border-thickness")) { + return m_windowHandle + ? int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId))) + : 0; + } + + return AbstractWindowContext::windowAttribute(key); } void Win32WindowContext::winIdChanged() { @@ -731,18 +732,24 @@ auto hWnd = reinterpret_cast<HWND>(winId); if (!isSystemBorderEnabled()) { - static constexpr const MARGINS margins = {1, 1, 1, 1}; - DynamicApis::instance().pDwmExtendFrameIntoClientArea(hWnd, &margins); + static auto margins = QVariant::fromValue(QMargins(1, 1, 1, 1)); + + // If we remove the system border, the window will lose its shadow. If dwm is enabled, + // then you need to set at least 1px margins, otherwise the following operation will + // fail with no effect. + setWindowAttribute(QStringLiteral("extra-margins"), margins); } + // We should disable WS_SYSMENU, otherwise the system button icons will be visible if mica + // is enabled and the title bar is transparent { auto style = ::GetWindowLongPtrW(hWnd, GWL_STYLE); -#if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDER) - ::SetWindowLongPtrW(hWnd, GWL_STYLE, style & (~WS_SYSMENU)); -#else - ::SetWindowLongPtrW(hWnd, GWL_STYLE, - (style | WS_THICKFRAME | WS_CAPTION) & (~WS_SYSMENU)); -#endif + if (isSystemBorderEnabled()) { + ::SetWindowLongPtrW(hWnd, GWL_STYLE, style & (~WS_SYSMENU)); + } else { + ::SetWindowLongPtrW(hWnd, GWL_STYLE, + (style | WS_THICKFRAME | WS_CAPTION) & (~WS_SYSMENU)); + } } // Add managed window @@ -797,7 +804,7 @@ msg.wParam = wParam; msg.lParam = lParam; QT_NATIVE_EVENT_RESULT_TYPE res = 0; - if (dispatch(QByteArrayLiteral("windows_generic_MSG"), &msg, &res)) { + if (nativeDispatch(QByteArrayLiteral("windows_generic_MSG"), &msg, &res)) { *result = LRESULT(res); return true; } @@ -807,11 +814,45 @@ bool Win32WindowContext::windowAttributeChanged(const QString &key, const QVariant &attribute, const QVariant &oldAttribute) { + Q_UNUSED(oldAttribute) + const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId()); const DynamicApis &apis = DynamicApis::instance(); - static constexpr const MARGINS extendMargins = {-1, -1, -1, -1}; - static const auto defaultMargins = - isSystemBorderEnabled() ? MARGINS{0, 0, 0, 0} : MARGINS{1, 1, 1, 1}; + static constexpr const MARGINS extendedMargins = {-1, -1, -1, -1}; + const auto &restoreMargins = [this, &apis, hwnd]() { + auto margins = qmargins2margins( + m_windowAttributes.value(QStringLiteral("extra-margins")).value<QMargins>()); + apis.pDwmExtendFrameIntoClientArea(hwnd, &margins); + }; + + if (key == QStringLiteral("extra-margins")) { + auto margins = qmargins2margins(attribute.value<QMargins>()); + return apis.pDwmExtendFrameIntoClientArea(hwnd, &margins) == S_OK; + } + + if (key == QStringLiteral("dark-mode")) { + if (!isWin101809OrGreater()) { + return false; + } + + BOOL enable = attribute.toBool(); + if (isWin101903OrGreater()) { + apis.pSetPreferredAppMode(enable ? PAM_AUTO : PAM_DEFAULT); + } else { + apis.pAllowDarkModeForApp(enable); + } + for (const auto attr : { + _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, + _DWMWA_USE_IMMERSIVE_DARK_MODE, + }) { + apis.pDwmSetWindowAttribute(hwnd, attr, &enable, sizeof(enable)); + } + + apis.pFlushMenuThemes(); + return true; + } + + // For Win11 or later if (key == QStringLiteral("mica")) { if (!isWin11OrGreater()) { return false; @@ -819,7 +860,7 @@ if (attribute.toBool()) { // We need to extend the window frame into the whole client area to be able // to see the blurred window background. - apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins); + apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins); if (isWin1122H2OrGreater()) { // Use official DWM API to enable Mica, available since Windows 11 22H2 // (10.0.22621). @@ -841,17 +882,19 @@ const BOOL enable = FALSE; apis.pDwmSetWindowAttribute(hwnd, _DWMWA_MICA_EFFECT, &enable, sizeof(enable)); } - apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins); + restoreMargins(); } return true; - } else if (key == QStringLiteral("mica-alt")) { + } + + 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. - apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins); + apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins); // Use official DWM API to enable Mica Alt, available since Windows 11 22H2 // (10.0.22621). const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TABBEDWINDOW; @@ -861,17 +904,19 @@ const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_AUTO; apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, sizeof(backdropType)); - apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins); + restoreMargins(); } return true; - } else if (key == QStringLiteral("acrylic-material")) { + } + + if (key == QStringLiteral("acrylic-material")) { 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. - apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins); + apis.pDwmExtendFrameIntoClientArea(hwnd, &extendedMargins); const _DWM_SYSTEMBACKDROP_TYPE backdropType = _DWMSBT_TRANSIENTWINDOW; apis.pDwmSetWindowAttribute(hwnd, _DWMWA_SYSTEMBACKDROP_TYPE, &backdropType, @@ -906,16 +951,15 @@ // wcad.cbData = sizeof(policy); // apis.pSetWindowCompositionAttribute(hwnd, &wcad); - apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins); + restoreMargins(); } return true; - } else if (key == QStringLiteral("dwm-blur")) { - // TODO: Optimize - // Currently not available!!! + } + + 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. - apis.pDwmExtendFrameIntoClientArea(hwnd, &extendMargins); + // We can't extend the window frame for this effect. + restoreMargins(); if (isWin8OrGreater()) { ACCENT_POLICY policy{}; policy.dwAccentState = ACCENT_ENABLE_BLURBEHIND; @@ -947,31 +991,7 @@ bb.dwFlags = DWM_BB_ENABLE; apis.pDwmEnableBlurBehindWindow(hwnd, &bb); } - apis.pDwmExtendFrameIntoClientArea(hwnd, &defaultMargins); } - return true; - } else if (key == QStringLiteral("dark-mode")) { - if (!isWin101809OrGreater()) { - return false; - } - - BOOL enable = attribute.toBool(); - - if (isWin101903OrGreater()) { - apis.pSetPreferredAppMode(enable ? PAM_AUTO : PAM_DEFAULT); - } else { - apis.pAllowDarkModeForApp(enable); - } - - for (const auto attr : { - _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, - _DWMWA_USE_IMMERSIVE_DARK_MODE, - }) { - apis.pDwmSetWindowAttribute(hwnd, attr, &enable, sizeof(enable)); - } - - apis.pFlushMenuThemes(); - return true; } return false; @@ -1309,12 +1329,12 @@ LPARAM lParam, LRESULT *result) { switch (message) { case WM_SHOWWINDOW: { - if (!centered) { + if (!initialCentered) { // If wParam is TRUE, the window is being shown. // If lParam is zero, the message was sent because of a call to the ShowWindow // function. if (wParam && !lParam) { - centered = true; + initialCentered = true; moveWindowToDesktopCenter(hWnd); } } -- Gitblit v1.9.1