From c3c6647e7888b7dbe9d9d22fb77bf08104a3653c Mon Sep 17 00:00:00 2001 From: Sine Striker <trueful@163.com> Date: 周一, 11 12月 2023 02:14:32 +0800 Subject: [PATCH] refactor --- src/core/contexts/win32windowcontext.cpp | 218 ++++++++++++++++++++++------------------------------- 1 files changed, 91 insertions(+), 127 deletions(-) diff --git a/src/core/contexts/win32windowcontext.cpp b/src/core/contexts/win32windowcontext.cpp index 72f20e0..9cf1ecc 100644 --- a/src/core/contexts/win32windowcontext.cpp +++ b/src/core/contexts/win32windowcontext.cpp @@ -1,10 +1,8 @@ #include "win32windowcontext_p.h" -#include "qwkcoreglobal_p.h" #include <optional> #include <QtCore/QHash> -#include <QtCore/QAbstractNativeEventFilter> #include <QtCore/QScopeGuard> #include <QtGui/QGuiApplication> @@ -25,7 +23,11 @@ #include <timeapi.h> #include <versionhelpers.h> +#include "nativeeventfilter.h" + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) Q_DECLARE_METATYPE(QMargins) +#endif namespace QWK { @@ -60,46 +62,44 @@ // } // }; // -// #define DWM_API_DECLARE(NAME) decltype(&::NAME) p##NAME = DefaultFunc<decltype(&::NAME)>::func -#define DWM_API_DECLARE(NAME) decltype(&::NAME) p##NAME = nullptr +// #define DYNAMIC_API_DECLARE(NAME) decltype(&::NAME) p##NAME = +// DefaultFunc<decltype(&::NAME)>::func +#define DYNAMIC_API_DECLARE(NAME) decltype(&::NAME) p##NAME = nullptr - DWM_API_DECLARE(DwmFlush); - DWM_API_DECLARE(DwmIsCompositionEnabled); - DWM_API_DECLARE(DwmGetCompositionTimingInfo); - DWM_API_DECLARE(GetDpiForWindow); - DWM_API_DECLARE(GetSystemMetricsForDpi); - DWM_API_DECLARE(GetDpiForMonitor); - DWM_API_DECLARE(timeGetDevCaps); - DWM_API_DECLARE(timeBeginPeriod); - DWM_API_DECLARE(timeEndPeriod); + DYNAMIC_API_DECLARE(DwmFlush); + DYNAMIC_API_DECLARE(DwmIsCompositionEnabled); + DYNAMIC_API_DECLARE(DwmGetCompositionTimingInfo); + 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 DWM_API_DECLARE +#undef DYNAMIC_API_DECLARE DynamicApis() { +#define DYNAMIC_API_RESOLVE(DLL, NAME) \ + p##NAME = reinterpret_cast<decltype(p##NAME)>(DLL.resolve(#NAME)) + QSystemLibrary user32(QStringLiteral("user32")); - pGetDpiForWindow = - reinterpret_cast<decltype(pGetDpiForWindow)>(user32.resolve("GetDpiForWindow")); - pGetSystemMetricsForDpi = reinterpret_cast<decltype(pGetSystemMetricsForDpi)>( - user32.resolve("GetSystemMetricsForDpi")); + DYNAMIC_API_RESOLVE(user32, GetDpiForWindow); + DYNAMIC_API_RESOLVE(user32, GetSystemMetricsForDpi); QSystemLibrary shcore(QStringLiteral("shcore")); - pGetDpiForMonitor = - reinterpret_cast<decltype(pGetDpiForMonitor)>(shcore.resolve("GetDpiForMonitor")); + DYNAMIC_API_RESOLVE(shcore, GetDpiForMonitor); QSystemLibrary dwmapi(QStringLiteral("dwmapi")); - pDwmFlush = reinterpret_cast<decltype(pDwmFlush)>(dwmapi.resolve("DwmFlush")); - pDwmIsCompositionEnabled = reinterpret_cast<decltype(pDwmIsCompositionEnabled)>( - dwmapi.resolve("DwmIsCompositionEnabled")); - pDwmGetCompositionTimingInfo = reinterpret_cast<decltype(pDwmGetCompositionTimingInfo)>( - dwmapi.resolve("DwmGetCompositionTimingInfo")); + DYNAMIC_API_RESOLVE(dwmapi, DwmFlush); + DYNAMIC_API_RESOLVE(dwmapi, DwmIsCompositionEnabled); + DYNAMIC_API_RESOLVE(dwmapi, DwmGetCompositionTimingInfo); QSystemLibrary winmm(QStringLiteral("winmm")); - ptimeGetDevCaps = - reinterpret_cast<decltype(ptimeGetDevCaps)>(winmm.resolve("timeGetDevCaps")); - ptimeBeginPeriod = - reinterpret_cast<decltype(ptimeBeginPeriod)>(winmm.resolve("timeBeginPeriod")); - ptimeEndPeriod = - reinterpret_cast<decltype(ptimeEndPeriod)>(winmm.resolve("timeEndPeriod")); + DYNAMIC_API_RESOLVE(winmm, timeGetDevCaps); + DYNAMIC_API_RESOLVE(winmm, timeBeginPeriod); + DYNAMIC_API_RESOLVE(winmm, timeEndPeriod); + +#undef DYNAMIC_API_RESOLVE } ~DynamicApis() = default; @@ -209,7 +209,8 @@ } static inline bool isWin11OrGreater() { - static const bool result = ::IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN10), LOBYTE(_WIN32_WINNT_WIN10), 22000); + static const bool result = ::IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN10), + LOBYTE(_WIN32_WINNT_WIN10), 22000); return result; } @@ -385,8 +386,8 @@ apis.ptimeEndPeriod(ms_granularity); } - static inline void showSystemMenu2(HWND hWnd, const POINT &pos, const bool selectFirstEntry) - { + static inline void showSystemMenu2(HWND hWnd, const POINT &pos, const bool selectFirstEntry, + const bool fixedSize) { const HMENU hMenu = ::GetSystemMenu(hWnd, FALSE); if (!hMenu) { // The corresponding window doesn't have a system menu, most likely due to the @@ -395,97 +396,53 @@ return; } - // Tweak the menu items according to the current window status and user settings. - const bool disableClose = /*data->callbacks->getProperty(kSysMenuDisableCloseVar, false).toBool()*/false; - const bool disableRestore = /*data->callbacks->getProperty(kSysMenuDisableRestoreVar, false).toBool()*/false; - const bool disableMinimize = /*data->callbacks->getProperty(kSysMenuDisableMinimizeVar, false).toBool()*/false; - const bool disableMaximize = /*data->callbacks->getProperty(kSysMenuDisableMaximizeVar, false).toBool()*/false; - const bool disableSize = /*data->callbacks->getProperty(kSysMenuDisableSizeVar, false).toBool()*/false; - const bool disableMove = /*data->callbacks->getProperty(kSysMenuDisableMoveVar, false).toBool()*/false; - const bool removeClose = /*data->callbacks->getProperty(kSysMenuRemoveCloseVar, false).toBool()*/false; - const bool removeSeparator = /*data->callbacks->getProperty(kSysMenuRemoveSeparatorVar, false).toBool()*/false; - const bool removeRestore = /*data->callbacks->getProperty(kSysMenuRemoveRestoreVar, false).toBool()*/false; - const bool removeMinimize = /*data->callbacks->getProperty(kSysMenuRemoveMinimizeVar, false).toBool()*/false; - const bool removeMaximize = /*data->callbacks->getProperty(kSysMenuRemoveMaximizeVar, false).toBool()*/false; - const bool removeSize = /*data->callbacks->getProperty(kSysMenuRemoveSizeVar, false).toBool()*/false; - const bool removeMove = /*data->callbacks->getProperty(kSysMenuRemoveMoveVar, false).toBool()*/false; const bool maxOrFull = IsMaximized(hWnd) || isFullScreen(hWnd); - const bool fixedSize = /*data->callbacks->isWindowFixedSize()*/false; - if (removeClose) { - ::DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); - } else { - ::EnableMenuItem(hMenu, SC_CLOSE, (MF_BYCOMMAND | (disableClose ? MFS_DISABLED : MFS_ENABLED))); - } - if (removeSeparator) { - // Looks like we must use 0 for the second parameter here, otherwise we can't remove the separator. - ::DeleteMenu(hMenu, 0, MFT_SEPARATOR); - } - if (removeMaximize) { - ::DeleteMenu(hMenu, SC_MAXIMIZE, MF_BYCOMMAND); - } else { - ::EnableMenuItem(hMenu, SC_MAXIMIZE, (MF_BYCOMMAND | ((maxOrFull || fixedSize || disableMaximize) ? MFS_DISABLED : MFS_ENABLED))); - } - if (removeRestore) { - ::DeleteMenu(hMenu, SC_RESTORE, MF_BYCOMMAND); - } else { - ::EnableMenuItem(hMenu, SC_RESTORE, (MF_BYCOMMAND | ((maxOrFull && !fixedSize && !disableRestore) ? MFS_ENABLED : MFS_DISABLED))); - // The first menu item should be selected by default if the menu is brought - // up by keyboard. I don't know how to pre-select a menu item but it seems - // highlight can do the job. However, there's an annoying issue if we do - // this manually: the highlighted menu item is really only highlighted, - // not selected, so even if the mouse cursor hovers on other menu items - // or the user navigates to other menu items through keyboard, the original - // highlight bar will not move accordingly, the OS will generate another - // highlight bar to indicate the current selected menu item, which will make - // the menu look kind of weird. Currently I don't know how to fix this issue. - ::HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | (selectFirstEntry ? MFS_HILITE : MFS_UNHILITE))); - } - if (removeMinimize) { - ::DeleteMenu(hMenu, SC_MINIMIZE, MF_BYCOMMAND); - } else { - ::EnableMenuItem(hMenu, SC_MINIMIZE, (MF_BYCOMMAND | (disableMinimize ? MFS_DISABLED : MFS_ENABLED))); - } - if (removeSize) { - ::DeleteMenu(hMenu, SC_SIZE, MF_BYCOMMAND); - } else { - ::EnableMenuItem(hMenu, SC_SIZE, (MF_BYCOMMAND | ((maxOrFull || fixedSize || disableSize || disableMinimize || disableMaximize) ? MFS_DISABLED : MFS_ENABLED))); - } - if (removeMove) { - ::DeleteMenu(hMenu, SC_MOVE, MF_BYCOMMAND); - } else { - ::EnableMenuItem(hMenu, SC_MOVE, (MF_BYCOMMAND | ((maxOrFull || disableMove) ? MFS_DISABLED : MFS_ENABLED))); - } + ::EnableMenuItem(hMenu, SC_CLOSE, (MF_BYCOMMAND | MFS_ENABLED)); + ::EnableMenuItem(hMenu, SC_MAXIMIZE, + (MF_BYCOMMAND | ((maxOrFull || fixedSize) ? MFS_DISABLED : MFS_ENABLED))); + ::EnableMenuItem(hMenu, SC_RESTORE, + (MF_BYCOMMAND | ((maxOrFull && !fixedSize) ? MFS_ENABLED : MFS_DISABLED))); + // The first menu item should be selected by default if the menu is brought + // up by keyboard. I don't know how to pre-select a menu item but it seems + // highlight can do the job. However, there's an annoying issue if we do + // this manually: the highlighted menu item is really only highlighted, + // not selected, so even if the mouse cursor hovers on other menu items + // or the user navigates to other menu items through keyboard, the original + // highlight bar will not move accordingly, the OS will generate another + // highlight bar to indicate the current selected menu item, which will make + // the menu look kind of weird. Currently I don't know how to fix this issue. + ::HiliteMenuItem(hWnd, hMenu, SC_RESTORE, + (MF_BYCOMMAND | (selectFirstEntry ? MFS_HILITE : MFS_UNHILITE))); + ::EnableMenuItem(hMenu, SC_MINIMIZE, (MF_BYCOMMAND | MFS_ENABLED)); + ::EnableMenuItem(hMenu, SC_SIZE, + (MF_BYCOMMAND | ((maxOrFull || fixedSize) ? MFS_DISABLED : MFS_ENABLED))); + ::EnableMenuItem(hMenu, SC_MOVE, (MF_BYCOMMAND | (maxOrFull ? MFS_DISABLED : MFS_ENABLED))); // The default menu item will appear in bold font. There can only be one default // menu item per menu at most. Set the item ID to "UINT_MAX" (or simply "-1") // can clear the default item for the given menu. - std::optional<UINT> defaultItemId = std::nullopt; + UINT defaultItemId = UINT_MAX; if (isWin11OrGreater()) { if (maxOrFull) { - if (!removeRestore) { - defaultItemId = SC_RESTORE; - } + defaultItemId = SC_RESTORE; } else { - if (!removeMaximize) { - defaultItemId = SC_MAXIMIZE; - } + defaultItemId = SC_MAXIMIZE; } } - if (!(defaultItemId.has_value() || removeClose)) { + if (defaultItemId == UINT_MAX) { defaultItemId = SC_CLOSE; } - ::SetMenuDefaultItem(hMenu, defaultItemId.value_or(UINT_MAX), FALSE); - - //::DrawMenuBar(hWnd); + ::SetMenuDefaultItem(hMenu, defaultItemId, FALSE); // Popup the system menu at the required position. - const auto result = ::TrackPopupMenu(hMenu, (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), pos.x, pos.y, 0, hWnd, nullptr); + const auto result = ::TrackPopupMenu( + hMenu, + (TPM_RETURNCMD | (QGuiApplication::isRightToLeft() ? TPM_RIGHTALIGN : TPM_LEFTALIGN)), + pos.x, pos.y, 0, hWnd, nullptr); - if (!removeRestore) { - // Unhighlight the first menu item after the popup menu is closed, otherwise it will keep - // highlighting until we unhighlight it manually. - ::HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | MFS_UNHILITE)); - } + // Unhighlight the first menu item after the popup menu is closed, otherwise it will keep + // highlighting until we unhighlight it manually. + ::HiliteMenuItem(hWnd, hMenu, SC_RESTORE, (MF_BYCOMMAND | MFS_UNHILITE)); if (!result) { // The user canceled the menu, no need to continue. @@ -564,7 +521,7 @@ // DefWindowProc(). Consequently, we have to add a global native filter that forwards the result // of the hook function, telling Qt whether we have filtered the events before. Since Qt only // handles Windows window messages in the main thread, it is safe to do so. - class WindowsNativeEventFilter : public QAbstractNativeEventFilter { + class WindowsNativeEventFilter : public NativeEventFilter { public: bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override { @@ -599,14 +556,12 @@ return; } instance = new WindowsNativeEventFilter(); - installNativeEventFilter(instance); } static inline void uninstall() { if (!instance) { return; } - removeNativeEventFilter(instance); delete instance; instance = nullptr; } @@ -712,6 +667,12 @@ WindowsNativeEventFilter::uninstall(); } } + } + + void Win32WindowContext::showSystemMenu(const QPoint &pos) { + auto winId = m_windowHandle->winId(); + auto hWnd = reinterpret_cast<HWND>(winId); + showSystemMenu2(hWnd, {pos.x(), pos.y()}, false, m_delegate->isHostSizeFixed(m_host)); } bool Win32WindowContext::setupHost() { @@ -937,9 +898,9 @@ DWORD dwScreenPos = ::GetMessagePos(); POINT screenPoint{GET_X_LPARAM(dwScreenPos), GET_Y_LPARAM(dwScreenPos)}; ::ScreenToClient(hWnd, &screenPoint); - QPoint qtScenePos = QHighDpi::fromNativeLocalPosition( - QPoint{screenPoint.x, screenPoint.y}, m_windowHandle); - auto dummy = CoreWindowAgent::Unknown; + QPoint qtScenePos = QHighDpi::fromNativeLocalPosition(point2qpoint(screenPoint), + m_windowHandle); + auto dummy = WindowAgentBase::Unknown; if (isInSystemButtons(qtScenePos, &dummy)) { // We must record whether the last WM_MOUSELEAVE was filtered, because if // Qt does not receive this message it will not call TrackMouseEvent() @@ -1170,14 +1131,14 @@ auto clientWidth = RECT_WIDTH(clientRect); auto clientHeight = RECT_HEIGHT(clientRect); - QPoint qtScenePos = QHighDpi::fromNativeLocalPosition( - QPoint(nativeLocalPos.x, nativeLocalPos.y), m_windowHandle); + QPoint qtScenePos = + QHighDpi::fromNativeLocalPosition(point2qpoint(nativeLocalPos), m_windowHandle); bool isFixedSize = m_delegate->isHostSizeFixed(m_host); bool isTitleBar = isInTitleBarDraggableArea(qtScenePos); bool dontOverrideCursor = false; // ### TODO - CoreWindowAgent::SystemButton sysButtonType = CoreWindowAgent::Unknown; + WindowAgentBase::SystemButton sysButtonType = WindowAgentBase::Unknown; if (!isFixedSize && isInSystemButtons(qtScenePos, &sysButtonType)) { // Firstly, we set the hit test result to a default value to be able to detect // whether we have changed it or not afterwards. @@ -1221,19 +1182,19 @@ // exact role of our button. The Snap Layout feature introduced in Windows // 11 won't work without this. switch (sysButtonType) { - case CoreWindowAgent::WindowIcon: + case WindowAgentBase::WindowIcon: *result = HTSYSMENU; break; - case CoreWindowAgent::Help: + case WindowAgentBase::Help: *result = HTHELP; break; - case CoreWindowAgent::Minimize: + case WindowAgentBase::Minimize: *result = HTREDUCE; break; - case CoreWindowAgent::Maximize: + case WindowAgentBase::Maximize: *result = HTZOOM; break; - case CoreWindowAgent::Close: + case WindowAgentBase::Close: *result = HTCLOSE; break; default: @@ -1683,7 +1644,8 @@ }(); RECT windowPos{}; ::GetWindowRect(hWnd, &windowPos); - return {static_cast<LONG>(windowPos.left + horizontalOffset), static_cast<LONG>(windowPos.top + verticalOffset)}; + return {static_cast<LONG>(windowPos.left + horizontalOffset), + static_cast<LONG>(windowPos.top + verticalOffset)}; }; bool shouldShowSystemMenu = false; bool broughtByKeyboard = false; @@ -1691,7 +1653,8 @@ switch (message) { case WM_RBUTTONUP: { const POINT nativeLocalPos = getNativePosFromMouse(); - const QPoint qtScenePos = QHighDpi::fromNativeLocalPosition(QPoint(nativeLocalPos.x, nativeLocalPos.y), m_windowHandle); + const QPoint qtScenePos = + QHighDpi::fromNativeLocalPosition(point2qpoint(nativeLocalPos), m_windowHandle); if (isInTitleBarDraggableArea(qtScenePos)) { shouldShowSystemMenu = true; nativeGlobalPos = nativeLocalPos; @@ -1730,7 +1693,8 @@ break; } if (shouldShowSystemMenu) { - showSystemMenu2(hWnd, nativeGlobalPos, broughtByKeyboard); + showSystemMenu2(hWnd, nativeGlobalPos, broughtByKeyboard, + m_delegate->isHostSizeFixed(m_host)); // QPA's internal code will handle system menu events separately, and its // behavior is not what we would want to see because it doesn't know our // window doesn't have any window frame now, so return early here to avoid -- Gitblit v1.9.1