src/core/CMakeLists.txt
@@ -15,6 +15,8 @@ kernel/nativeeventfilter.cpp kernel/sharedeventfilter_p.h kernel/sharedeventfilter.cpp kernel/winidchangeeventfilter_p.h kernel/winidchangeeventfilter.cpp shared/systemwindow_p.h contexts/abstractwindowcontext_p.h contexts/abstractwindowcontext.cpp src/core/contexts/abstractwindowcontext.cpp
@@ -7,34 +7,12 @@ #include <QtGui/QPen> #include <QtGui/QPainter> #include <QtGui/QScreen> #include <memory> #include "qwkglobal_p.h" namespace QWK { namespace { 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; }; class WindowEventFilter : public QObject { public: @@ -77,16 +55,10 @@ } m_host = host; m_delegate.reset(delegate); m_winIdChangeEventFilter = std::make_unique<WinIdChangeEventFilter>(host, this); m_winIdChangeEventFilter.reset(delegate->createWinIdEventFilter(host, this)); m_windowEventFilter = std::make_unique<WindowEventFilter>(m_windowHandle, this); m_windowHandle = m_delegate->hostWindow(m_host); if (m_windowHandle) { winIdChanged(); notifyWinIdChange(); } } bool AbstractWindowContext::setHitTestVisible(const QObject *obj, bool visible) { Q_ASSERT(obj); @@ -259,15 +231,21 @@ } void AbstractWindowContext::notifyWinIdChange() { auto windowEventFilter = static_cast<WindowEventFilter *>(m_windowEventFilter.get()); windowEventFilter->setWindow(nullptr); auto oldWinId = m_windowId; m_windowId = m_winIdChangeEventFilter->winId(); // In Qt6, after QWidget::close() is called, the related QWindow's all surfaces and the // platform window will be removed, and the WinId will be set to 0. After that, when the // QWidget is shown again, the whole things will be recreated again. // As a result, we must update our WindowContext each time the WinId changes. m_windowHandle = m_delegate->hostWindow(m_host); winIdChanged(); auto windowEventFilter = static_cast<WindowEventFilter *>(m_windowEventFilter.get()); windowEventFilter->setWindow(nullptr); if (oldWinId != m_windowId) { winIdChanged(m_windowId, oldWinId); } if (m_windowHandle) { windowEventFilter->setWindow(m_windowHandle); src/core/contexts/abstractwindowcontext_p.h
@@ -26,6 +26,7 @@ #include <QWKCore/private/nativeeventfilter_p.h> #include <QWKCore/private/sharedeventfilter_p.h> #include <QWKCore/private/windowitemdelegate_p.h> #include <QWKCore/private/winidchangeeventfilter_p.h> namespace QWK { @@ -81,7 +82,7 @@ virtual bool setWindowAttribute(const QString &key, const QVariant &attribute); protected: virtual void winIdChanged() = 0; virtual void winIdChanged(WId winId, WId oldWinId) = 0; virtual bool windowAttributeChanged(const QString &key, const QVariant &attribute, const QVariant &oldAttribute); @@ -89,6 +90,7 @@ QObject *m_host{}; std::unique_ptr<WindowItemDelegate> m_delegate; QWindow *m_windowHandle{}; WId m_windowId{}; QSet<const QObject *> m_hitTestVisibleItems; #ifdef Q_OS_MAC @@ -101,7 +103,7 @@ QVariantHash m_windowAttributes; std::unique_ptr<QObject> m_windowEventFilter; std::unique_ptr<QObject> m_winIdChangeEventFilter; std::unique_ptr<WinIdChangeEventFilter> m_winIdChangeEventFilter; }; inline QObject *AbstractWindowContext::host() const { src/core/contexts/cocoawindowcontext.mm
@@ -664,7 +664,7 @@ } CocoaWindowContext::~CocoaWindowContext() { releaseWindowProxy(windowId); releaseWindowProxy(m_windowId); } QString CocoaWindowContext::key() const { @@ -674,7 +674,7 @@ void CocoaWindowContext::virtual_hook(int id, void *data) { switch (id) { case SystemButtonAreaChangedHook: { ensureWindowProxy(windowId)->setScreenRectCallback(m_systemButtonAreaCallback); ensureWindowProxy(m_windowId)->setScreenRectCallback(m_systemButtonAreaCallback); return; } @@ -686,18 +686,17 @@ QVariant CocoaWindowContext::windowAttribute(const QString &key) const { if (key == QStringLiteral("title-bar-height")) { if (!m_windowHandle) return 0; return ensureWindowProxy(windowId)->titleBarHeight(); if (!m_windowId) return {}; return ensureWindowProxy(m_windowId)->titleBarHeight(); } return AbstractWindowContext::windowAttribute(key); } void CocoaWindowContext::winIdChanged() { void CocoaWindowContext::winIdChanged(WId winId, WId oldWinId) { // If the original window id is valid, remove all resources related if (windowId) { releaseWindowProxy(windowId); windowId = 0; if (oldWinId) { releaseWindowProxy(oldWinId); } if (!m_windowHandle) { @@ -705,8 +704,7 @@ } // Allocate new resources windowId = m_windowHandle->winId(); ensureWindowProxy(windowId)->setSystemTitleBarVisible(false); ensureWindowProxy(winId)->setSystemTitleBarVisible(false); } bool CocoaWindowContext::windowAttributeChanged(const QString &key, const QVariant &attribute, @@ -716,7 +714,7 @@ if (key == QStringLiteral("no-system-buttons")) { if (attribute.type() != QVariant::Bool) return false; ensureWindowProxy(windowId)->setSystemButtonVisible(!attribute.toBool()); ensureWindowProxy(m_windowId)->setSystemButtonVisible(!attribute.toBool()); return true; } @@ -743,7 +741,7 @@ } else { return false; } return ensureWindowProxy(windowId)->setBlurEffect(mode); return ensureWindowProxy(m_windowId)->setBlurEffect(mode); } return false; src/core/contexts/cocoawindowcontext_p.h
@@ -30,13 +30,11 @@ QVariant windowAttribute(const QString &key) const override; protected: void winIdChanged() override; void winIdChanged(WId winId, WId oldWinId) override; bool windowAttributeChanged(const QString &key, const QVariant &attribute, const QVariant &oldAttribute) override; protected: WId windowId = 0; std::unique_ptr<SharedEventFilter> cocoaWindowEventFilter; }; src/core/contexts/qtwindowcontext.cpp
@@ -253,7 +253,7 @@ AbstractWindowContext::virtual_hook(id, data); } void QtWindowContext::winIdChanged() { void QtWindowContext::winIdChanged(WId winId, WId oldWinId) { if (!m_windowHandle) { m_delegate->setWindowFlags(m_host, m_delegate->getWindowFlags(m_host) & ~Qt::FramelessWindowHint); src/core/contexts/qtwindowcontext_p.h
@@ -28,7 +28,7 @@ void virtual_hook(int id, void *data) override; protected: void winIdChanged() override; void winIdChanged(WId winId, WId oldWinId) override; protected: std::unique_ptr<SharedEventFilter> qtWindowEventFilter; src/core/contexts/win32windowcontext.cpp
@@ -321,7 +321,7 @@ case HTBORDER: return Win32WindowContext::FixedBorder; default: // unreachable Q_UNREACHABLE(); break; } return Win32WindowContext::Outside; @@ -540,8 +540,8 @@ } Win32WindowContext::~Win32WindowContext() { if (windowId) { removeManagedWindow(reinterpret_cast<HWND>(windowId)); if (m_windowId) { removeManagedWindow(reinterpret_cast<HWND>(m_windowId)); } } @@ -552,19 +552,19 @@ void Win32WindowContext::virtual_hook(int id, void *data) { switch (id) { case RaiseWindowHook: { if (!windowId) if (!m_windowId) return; m_delegate->setWindowVisible(m_host, true); const auto hwnd = reinterpret_cast<HWND>(windowId); const auto hwnd = reinterpret_cast<HWND>(m_windowId); bringWindowToFront(hwnd); return; } case ShowSystemMenuHook: { if (!windowId) if (!m_windowId) return; const auto &pos = *static_cast<const QPoint *>(data); auto hWnd = reinterpret_cast<HWND>(windowId); auto hWnd = reinterpret_cast<HWND>(m_windowId); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const QPoint nativeGlobalPos = QHighDpi::toNativeGlobalPosition(pos, m_windowHandle); @@ -588,14 +588,14 @@ #if QWINDOWKIT_CONFIG(ENABLE_WINDOWS_SYSTEM_BORDERS) case DrawWindows10BorderHook: { if (!windowId) if (!m_windowId) return; 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>(windowId); const auto hwnd = reinterpret_cast<HWND>(m_windowId); QPen pen; pen.setWidth(int(getWindowFrameBorderThickness(hwnd)) * 2); @@ -638,7 +638,7 @@ // 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); auto hWnd = reinterpret_cast<HWND>(m_windowId); HDC hdc = ::GetDC(hWnd); RECT windowRect{}; ::GetClientRect(hWnd, &windowRect); @@ -667,7 +667,7 @@ return {}; RECT frame{}; auto hwnd = reinterpret_cast<HWND>(windowId); auto hwnd = reinterpret_cast<HWND>(m_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)); @@ -687,28 +687,25 @@ if (key == QStringLiteral("border-thickness")) { return m_windowHandle ? int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId))) ? int(getWindowFrameBorderThickness(reinterpret_cast<HWND>(m_windowId))) : 0; } return AbstractWindowContext::windowAttribute(key); } void Win32WindowContext::winIdChanged() { void Win32WindowContext::winIdChanged(WId winId, WId oldWinId) { // If the original window id is valid, remove all resources related if (windowId) { removeManagedWindow(reinterpret_cast<HWND>(windowId)); windowId = 0; if (oldWinId) { removeManagedWindow(reinterpret_cast<HWND>(oldWinId)); } if (!m_windowHandle) { if (!m_windowHandle || ! winId) { return; } // Install window hook auto winId = m_windowHandle->winId(); auto hWnd = reinterpret_cast<HWND>(winId); if (!isSystemBorderEnabled()) { static auto margins = QVariant::fromValue(QMargins(1, 1, 1, 1)); @@ -732,14 +729,15 @@ // Add managed window addManagedWindow(m_windowHandle, hWnd, this); // Cache win id windowId = winId; } bool Win32WindowContext::windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result) { *result = FALSE; if (message == WM_DESTROY) { qDebug() << "WM_DESTROY"; } // We should skip these messages otherwise we will get crashes. // NOTE: WM_QUIT won't be posted to the WindowProc function. @@ -757,6 +755,20 @@ if (!isValidWindow(hWnd, false, true)) { return false; } switch (message) { case WM_NCMOUSELEAVE: qDebug() << "WM_NCMOUSELEAVE" << mouseLeaveBlocked; break; case WM_MOUSEHOVER: qDebug() << "WM_MOUSEHOVER" << mouseLeaveBlocked; break; case WM_NCMOUSEHOVER: qDebug() << "WM_NCMOUSEHOVER" << mouseLeaveBlocked; break; default: break; } // Test snap layout @@ -794,7 +806,7 @@ const QVariant &oldAttribute) { Q_UNUSED(oldAttribute) const auto hwnd = reinterpret_cast<HWND>(m_windowHandle->winId()); const auto hwnd = reinterpret_cast<HWND>(m_windowId); const DynamicApis &apis = DynamicApis::instance(); static constexpr const MARGINS extendedMargins = {-1, -1, -1, -1}; const auto &restoreMargins = [this, &apis, hwnd]() { @@ -1144,6 +1156,8 @@ } } mouseLeaveBlocked = false; qDebug() << "WM_MOUSELEAVE"; break; } @@ -1152,6 +1166,7 @@ // we unset `mouseLeaveBlocked` mark and pretend as if Qt has received // WM_MOUSELEAVE. if (lastHitTestResult != WindowPart::ChromeButton && mouseLeaveBlocked) { qDebug() << lastHitTestResult << "Track"; mouseLeaveBlocked = false; requestForMouseLeaveMessage(hWnd, false); } @@ -1240,7 +1255,7 @@ // 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 // leaving the window, current window part remains as `Outside`. lastHitTestResult = WindowPart::Outside; // lastHitTestResult = WindowPart::Outside; } if (currentWindowPart == WindowPart::ChromeButton) { src/core/contexts/win32windowcontext_p.h
@@ -33,6 +33,7 @@ FixedBorder, TitleBar, }; Q_ENUM(WindowPart) QString key() const override; void virtual_hook(int id, void *data) override; @@ -40,7 +41,7 @@ QVariant windowAttribute(const QString &key) const override; protected: void winIdChanged() override; void winIdChanged(WId winId, WId oldWinId) override; bool windowAttributeChanged(const QString &key, const QVariant &attribute, const QVariant &oldAttribute) override; @@ -64,8 +65,6 @@ LRESULT *result); protected: WId windowId = 0; // The last hit test result, helpful to handle WM_MOUSEMOVE and WM_NCMOUSELEAVE. WindowPart lastHitTestResult = WindowPart::Outside; src/core/kernel/winidchangeeventfilter.cpp
New file @@ -0,0 +1,37 @@ // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) // SPDX-License-Identifier: Apache-2.0 #include "winidchangeeventfilter_p.h" #include <qpa/qplatformwindow.h> #include "abstractwindowcontext_p.h" namespace QWK { WId WindowWinIdChangeEventFilter::winId() const { auto win = static_cast<QWindow *>(host); if (isAboutToBeDestroyed) return 0; if (auto platformWindow = win->handle()) return platformWindow->winId(); return 0; } bool WindowWinIdChangeEventFilter::eventFilter(QObject *obj, QEvent *event) { Q_UNUSED(obj) if (event->type() == QEvent::PlatformSurface) { auto e = static_cast<QPlatformSurfaceEvent *>(event); if (e->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) { isAboutToBeDestroyed = true; context->notifyWinIdChange(); isAboutToBeDestroyed = false; } else { context->notifyWinIdChange(); } } return false; } } src/core/kernel/winidchangeeventfilter_p.h
New file @@ -0,0 +1,56 @@ // Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware) // Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao) // SPDX-License-Identifier: Apache-2.0 #ifndef WINIDCHANGEEVENTFILTER_P_H #define WINIDCHANGEEVENTFILTER_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 <QtGui/QWindow> #include <QWKCore/qwkglobal.h> namespace QWK { class AbstractWindowContext; class WinIdChangeEventFilter : public QObject { public: WinIdChangeEventFilter(QObject *host, AbstractWindowContext *context) : host(host), context(context) { } virtual WId winId() const = 0; protected: QObject *host; AbstractWindowContext *context; }; class QWK_CORE_EXPORT WindowWinIdChangeEventFilter : public WinIdChangeEventFilter { public: WindowWinIdChangeEventFilter(QObject *host, AbstractWindowContext *context) : WinIdChangeEventFilter(host, context), win(static_cast<QWindow *>(host)), isAboutToBeDestroyed(false) { } WId winId() const override; protected: QWindow *win; bool isAboutToBeDestroyed; bool eventFilter(QObject *obj, QEvent *event) override; }; } #endif // WINIDCHANGEEVENTFILTER_P_H src/core/windowitemdelegate.cpp
@@ -14,4 +14,10 @@ Q_UNUSED(host); } WinIdChangeEventFilter * WindowItemDelegate::createWinIdEventFilter(QObject *host, AbstractWindowContext *context) const { return new WindowWinIdChangeEventFilter(host, context); } } src/core/windowitemdelegate_p.h
@@ -18,6 +18,7 @@ #include <QtCore/QPoint> #include <QtGui/QWindow> #include <QWKCore/private/winidchangeeventfilter_p.h> #include <QWKCore/qwkglobal.h> namespace QWK { @@ -52,6 +53,10 @@ virtual void setGeometry(QObject *host, const QRect &rect) = 0; virtual void bringWindowToTop(QObject *host) const = 0; // Factories virtual WinIdChangeEventFilter * createWinIdEventFilter(QObject *host, AbstractWindowContext *context) const; private: Q_DISABLE_COPY(WindowItemDelegate) }; src/widgets/widgetitemdelegate.cpp
@@ -5,12 +5,37 @@ #include "widgetitemdelegate_p.h" #include <QtGui/QMouseEvent> #include <QtWidgets/QWidget> #include <QtWidgets/QApplication> #include <QtWidgets/QWidget> #include <QWKCore/private/abstractwindowcontext_p.h> extern Q_DECL_IMPORT QWidget *qt_button_down; namespace QWK { class WidgetWinIdChangeEventFilter : public WinIdChangeEventFilter { public: explicit WidgetWinIdChangeEventFilter(QObject *host, AbstractWindowContext *ctx) : WinIdChangeEventFilter(host, ctx), widget(static_cast<QWidget *>(host)) { widget->installEventFilter(this); } WId winId() const override { return widget->effectiveWinId(); } protected: bool eventFilter(QObject *obj, QEvent *event) override { Q_UNUSED(obj) if (event->type() == QEvent::WinIdChange) { context->notifyWinIdChange(); } return false; } QWidget *widget; }; WidgetItemDelegate::WidgetItemDelegate() = default; @@ -112,4 +137,10 @@ static_cast<QWidget *>(host)->raise(); } WinIdChangeEventFilter * WidgetItemDelegate::createWinIdEventFilter(QObject *host, AbstractWindowContext *context) const { return new WidgetWinIdChangeEventFilter(host, context); } } src/widgets/widgetitemdelegate_p.h
@@ -48,6 +48,9 @@ void setWindowVisible(QObject *host, bool visible) const override; void setGeometry(QObject *host, const QRect &rect) override; void bringWindowToTop(QObject *host) const override; WinIdChangeEventFilter * createWinIdEventFilter(QObject *host, AbstractWindowContext *context) const override; }; } src/widgets/widgetwindowagent.cpp
@@ -56,7 +56,7 @@ } w->setAttribute(Qt::WA_DontCreateNativeAncestors); w->setAttribute(Qt::WA_NativeWindow); w->setAttribute(Qt::WA_NativeWindow); // Create new window id d->setup(w, new WidgetItemDelegate()); d->hostWidget = w;