src/core/contexts/win32windowcontext.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/contexts/win32windowcontext_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/corewindowagent.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/corewindowagent_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/quick/quickwindowagent.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/widgets/widgetwindowagent.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
src/core/contexts/win32windowcontext.cpp
@@ -17,24 +17,6 @@ static WNDPROC g_qtWindowProc = nullptr; // Original Qt window proc function static bool g_lastMessageHandled = false; static LRESULT g_lastMessageResult = false; class WindowsNativeEventFilter : public QAbstractNativeEventFilter { public: bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override { if (g_lastMessageHandled) { *result = static_cast<QT_NATIVE_EVENT_RESULT_TYPE>(g_lastMessageResult); return true; } return false; } }; static WindowsNativeEventFilter *g_nativeFilter = nullptr; static inline QPoint fromNativeLocalPosition(const QWindow *window, const QPoint &point) { #if 1 return QHighDpi::fromNativeLocalPosition(point, window); @@ -105,6 +87,44 @@ return true; } // https://github.com/qt/qtbase/blob/e26a87f1ecc40bc8c6aa5b889fce67410a57a702/src/plugins/platforms/windows/qwindowscontext.cpp#L1556 // In QWindowsContext::windowsProc(), the messages will be passed to all global native event // filters, but because we have already filtered the messages in the hook WndProc function for // convenience, Qt does not know we may have already process the messages and thus will call // 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 { public: bool nativeEventFilter(const QByteArray &eventType, void *message, QT_NATIVE_EVENT_RESULT_TYPE *result) override { if (lastMessageHandled) { *result = static_cast<QT_NATIVE_EVENT_RESULT_TYPE>(lastMessageResult); return true; } return false; } static bool lastMessageHandled; static LRESULT lastMessageResult; static WindowsNativeEventFilter *instance; static inline void install() { instance = new WindowsNativeEventFilter(); qApp->installNativeEventFilter(instance); } static inline void uninstall() { qApp->removeNativeEventFilter(instance); delete instance; instance = nullptr; } }; bool WindowsNativeEventFilter::lastMessageHandled = false; LRESULT WindowsNativeEventFilter::lastMessageResult = 0; WindowsNativeEventFilter *WindowsNativeEventFilter::instance = 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 // unfiltered messages to the event dispatcher. To activate the Snap Layout feature on Windows @@ -171,7 +191,9 @@ } // Try hooked procedure and save result g_lastMessageHandled = ctx->windowProc(hWnd, message, wParam, lParam, &g_lastMessageResult); auto &handled = WindowsNativeEventFilter::lastMessageHandled; auto &result = WindowsNativeEventFilter::lastMessageResult; handled = ctx->windowProc(hWnd, message, wParam, lParam, &result); // TODO: Determine whether to show system menu // ... @@ -190,11 +212,9 @@ if (auto hWnd = reinterpret_cast<HWND>(windowId); hWnd) { g_wndProcHash->remove(hWnd); // Remove event filter if the last window is destroyed // Remove event filter if the all windows has been destroyed if (g_wndProcHash->empty()) { qApp->removeNativeEventFilter(g_nativeFilter); delete g_nativeFilter; g_nativeFilter = nullptr; WindowsNativeEventFilter::uninstall(); } } } @@ -214,9 +234,8 @@ ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(QWKHookedWndProc)); // Install global native event filter if (!g_nativeFilter) { g_nativeFilter = new WindowsNativeEventFilter(); qApp->installNativeEventFilter(g_nativeFilter); if (!WindowsNativeEventFilter::instance) { WindowsNativeEventFilter::install(); } // Cache window ID @@ -411,7 +430,7 @@ } bool Win32WindowContext::snapLayoutHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result) { LPARAM lParam, LRESULT *result) { switch (message) { case WM_MOUSELEAVE: { if (!isTaggedMessage(wParam)) { @@ -429,6 +448,9 @@ GET_Y_LPARAM(dwScreenPos))); auto dummy = CoreWindowAgent::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() // again, resulting in the client area not responding to any mouse event. mouseLeaveBlocked = true; *result = FALSE; return true; @@ -439,7 +461,10 @@ } case WM_MOUSEMOVE: { if ((lastHitTestResult != WindowPart::ChromeButton) && mouseLeaveBlocked) { // At appropriate time, we will call TrackMouseEvent() for Qt. Simultaneously, // we unset `mouseLeaveBlocked` mark and pretend as if Qt has received // WM_MOUSELEAVE. if (lastHitTestResult != WindowPart::ChromeButton && mouseLeaveBlocked) { mouseLeaveBlocked = false; std::ignore = requestForMouseLeaveMessage(hWnd, false); } @@ -479,7 +504,7 @@ // comes, so we reset it when we receive a WM_NCMOUSEMOVE. // If the mouse is entering the client area, there must be a WM_NCHITTEST // setting it to `Client` before the WM_NCMOUSELEAVE comes; If the mouse is // setting it to `Client` before the WM_NCMOUSELEAVE comes; if the mouse is // leaving the window, current window part remains as `Outside`. lastHitTestResult = WindowPart::Outside; } src/core/contexts/win32windowcontext_p.h
@@ -25,17 +25,22 @@ bool setup() override; bool windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); // In order to perfectly apply Windows 11 Snap Layout into the Qt window, we need to // intercept and simulate most of the mouse events, so that the processing logic // is quite complex. Simultaneously, in order to make the handling code of other // Windows messages clearer, we have separated them into this function. bool snapLayoutHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); protected: WId windowId = 0; // Store the last hit test result, it's helpful to handle WM_MOUSEMOVE and WM_NCMOUSELEAVE. // The last hit test result, helpful to handle WM_MOUSEMOVE and WM_NCMOUSELEAVE. WindowPart lastHitTestResult = WindowPart::Outside; // True if we blocked a WM_MOUSELEAVE when mouse moves on chrome button, false when a // WM_MOUSELEAVE comes or we manually call TrackMouseEvent(). // Whether the last mouse leave message is blocked, mainly for handling the unexpected // WM_MOUSELEAVE. bool mouseLeaveBlocked = false; }; src/core/corewindowagent.cpp
@@ -13,7 +13,7 @@ namespace QWK { CoreWindowAgentPrivate::CoreWindowAgentPrivate() : q_ptr(nullptr), eventHandler(nullptr) { CoreWindowAgentPrivate::CoreWindowAgentPrivate() : q_ptr(nullptr), context(nullptr) { } CoreWindowAgentPrivate::~CoreWindowAgentPrivate() = default; @@ -38,7 +38,7 @@ delete handler; return false; } eventHandler.reset(handler); context.reset(handler); return true; } @@ -46,12 +46,12 @@ void CoreWindowAgent::showSystemMenu(const QPoint &pos) { Q_D(CoreWindowAgent); d->eventHandler->showSystemMenu(pos); d->context->showSystemMenu(pos); } void CoreWindowAgent::startSystemMove(const QPoint &pos) { Q_D(CoreWindowAgent); auto win = d->eventHandler->window(); auto win = d->context->window(); if (!win) { return; } @@ -62,7 +62,7 @@ void CoreWindowAgent::startSystemResize(Qt::Edges edges, const QPoint &pos) { Q_D(CoreWindowAgent); auto win = d->eventHandler->window(); auto win = d->context->window(); if (!win) { return; } src/core/corewindowagent_p.h
@@ -18,7 +18,7 @@ bool setup(QWindow *window, WindowItemDelegate *delegate); std::unique_ptr<AbstractWindowContext> eventHandler; std::unique_ptr<AbstractWindowContext> context; Q_DISABLE_COPY_MOVE(CoreWindowAgentPrivate) }; src/quick/quickwindowagent.cpp
@@ -43,27 +43,27 @@ bool QuickWindowAgent::isHitTestVisible(QQuickItem *item) const { Q_D(const QuickWindowAgent); return d->eventHandler->isHitTestVisible(item); return d->context->isHitTestVisible(item); } void QuickWindowAgent::setHitTestVisible(QQuickItem *item, bool visible) { Q_D(QuickWindowAgent); d->eventHandler->setHitTestVisible(item, visible); d->context->setHitTestVisible(item, visible); } void QuickWindowAgent::setHitTestVisible(const QRect &rect, bool visible) { Q_D(QuickWindowAgent); d->eventHandler->setHitTestVisible(rect, visible); d->context->setHitTestVisible(rect, visible); } QQuickItem *QuickWindowAgent::systemButton(SystemButton button) const { Q_D(const QuickWindowAgent); return static_cast<QQuickItem *>(d->eventHandler->systemButton(button)); return static_cast<QQuickItem *>(d->context->systemButton(button)); } void QuickWindowAgent::setSystemButton(SystemButton button, QQuickItem *item) { Q_D(QuickWindowAgent); if (!d->eventHandler->setSystemButton(button, item)) { if (!d->context->setSystemButton(button, item)) { return; } Q_EMIT systemButtonChanged(button, item); @@ -71,12 +71,12 @@ QQuickItem *QuickWindowAgent::titleBar() const { Q_D(const QuickWindowAgent); return static_cast<QQuickItem *>(d->eventHandler->titleBar()); return static_cast<QQuickItem *>(d->context->titleBar()); } void QuickWindowAgent::setTitleBar(QQuickItem *item) { Q_D(QuickWindowAgent); if (!d->eventHandler->setTitleBar(item)) { if (!d->context->setTitleBar(item)) { return; } Q_EMIT titleBarWidgetChanged(item); src/widgets/widgetwindowagent.cpp
@@ -42,27 +42,27 @@ bool WidgetWindowAgent::isHitTestVisible(QWidget *w) const { Q_D(const WidgetWindowAgent); return d->eventHandler->isHitTestVisible(w); return d->context->isHitTestVisible(w); } void WidgetWindowAgent::setHitTestVisible(QWidget *w, bool visible) { Q_D(WidgetWindowAgent); d->eventHandler->setHitTestVisible(w, visible); d->context->setHitTestVisible(w, visible); } void WidgetWindowAgent::setHitTestVisible(const QRect &rect, bool visible) { Q_D(WidgetWindowAgent); d->eventHandler->setHitTestVisible(rect, visible); d->context->setHitTestVisible(rect, visible); } QWidget *WidgetWindowAgent::systemButton(CoreWindowAgent::SystemButton button) const { Q_D(const WidgetWindowAgent); return static_cast<QWidget *>(d->eventHandler->systemButton(button)); return static_cast<QWidget *>(d->context->systemButton(button)); } void WidgetWindowAgent::setSystemButton(CoreWindowAgent::SystemButton button, QWidget *w) { Q_D(WidgetWindowAgent); if (!d->eventHandler->setSystemButton(button, w)) { if (!d->context->setSystemButton(button, w)) { return; } Q_EMIT systemButtonChanged(button, w); @@ -70,12 +70,12 @@ QWidget *WidgetWindowAgent::titleBar() const { Q_D(const WidgetWindowAgent); return static_cast<QWidget *>(d->eventHandler->titleBar()); return static_cast<QWidget *>(d->context->titleBar()); } void WidgetWindowAgent::setTitleBar(QWidget *w) { Q_D(WidgetWindowAgent); if (!d->eventHandler->setTitleBar(w)) { if (!d->context->setTitleBar(w)) { return; } Q_EMIT titleBarWidgetChanged(w);