Add win32 winIdChange workaround
| | |
| | | |
| | | AbstractWindowContext::~AbstractWindowContext() = default; |
| | | |
| | | bool AbstractWindowContext::setup(QObject *host, WindowItemDelegate *delegate) { |
| | | if (!host || !delegate) { |
| | | return false; |
| | | void AbstractWindowContext::setup(QObject *host, WindowItemDelegate *delegate) { |
| | | if (m_host || !host || !delegate) { |
| | | return; |
| | | } |
| | | |
| | | auto windowHandle = delegate->hostWindow(host); |
| | | if (!windowHandle) { |
| | | return false; |
| | | } |
| | | |
| | | m_host = host; |
| | | m_delegate.reset(delegate); |
| | | m_windowHandle = windowHandle; |
| | | |
| | | if (!setupHost()) { |
| | | m_host = nullptr; |
| | | m_delegate.reset(); |
| | | m_windowHandle = nullptr; |
| | | return false; |
| | | m_windowHandle = m_delegate->hostWindow(m_host); |
| | | if (m_windowHandle) { |
| | | m_windowHandleGuard = m_windowHandle; |
| | | winIdChanged(nullptr, false); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | bool AbstractWindowContext::setHitTestVisible(const QObject *obj, bool visible) { |
| | |
| | | virtual_hook(ShowSystemMenuHook, &const_cast<QPoint &>(pos)); |
| | | } |
| | | |
| | | void AbstractWindowContext::notifyWinIdChange() { |
| | | auto oldWindow = m_windowHandle; |
| | | m_windowHandle = m_delegate->window(m_host); |
| | | winIdChanged(oldWindow, oldWindow && m_windowHandleGuard.isNull()); |
| | | } |
| | | |
| | | } |
| | |
| | | #include <memory> |
| | | |
| | | #include <QtCore/QSet> |
| | | #include <QtCore/QPointer> |
| | | #include <QtGui/QRegion> |
| | | #include <QtGui/QWindow> |
| | | |
| | |
| | | ~AbstractWindowContext() override; |
| | | |
| | | public: |
| | | bool setup(QObject *host, WindowItemDelegate *delegate); |
| | | void setup(QObject *host, WindowItemDelegate *delegate); |
| | | |
| | | inline QObject *host() const; |
| | | inline QWindow *window() const; |
| | |
| | | virtual void virtual_hook(int id, void *data); |
| | | |
| | | void showSystemMenu(const QPoint &pos); |
| | | void notifyWinIdChange(); |
| | | |
| | | protected: |
| | | virtual bool setupHost() = 0; |
| | | virtual void winIdChanged(QWindow *oldWindow, bool destroyed) = 0; |
| | | |
| | | protected: |
| | | QObject *m_host{}; |
| | | std::unique_ptr<WindowItemDelegate> m_delegate; |
| | | QWindow *m_windowHandle{}; |
| | | QPointer<QWindow> m_windowHandleGuard; |
| | | |
| | | QSet<const QObject *> m_hitTestVisibleItems; |
| | | #ifdef Q_OS_MAC |
| | |
| | | |
| | | CocoaWindowEventFilter::~CocoaWindowEventFilter() = default; |
| | | |
| | | bool CocoaWindowEventFilter::eventFilter(QObject *object, QEvent *event) { |
| | | bool CocoaWindowEventFilter::eventFilter(QObject *obj, QEvent *event) { |
| | | Q_UNUSED(obj) |
| | | auto type = event->type(); |
| | | if (type < QEvent::MouseButtonPress || type > QEvent::MouseMove) { |
| | | return false; |
| | |
| | | AbstractWindowContext::virtual_hook(id, data); |
| | | } |
| | | |
| | | bool CocoaWindowContext::setupHost() { |
| | | bool CocoaWindowContext::winIdChanged(QWindow *oldWindow) { |
| | | windowId = m_windowHandle->winId(); |
| | | ensureWindowProxy(windowId)->setSystemTitleBarVisible(false); |
| | | std::ignore = new CocoaWindowEventFilter(this, this); |
| | |
| | | void virtual_hook(int id, void *data) override; |
| | | |
| | | protected: |
| | | bool setupHost() override; |
| | | void winIdChanged(QWindow *oldWindow) override; |
| | | |
| | | protected: |
| | | WId windowId = 0; |
| | |
| | | |
| | | QtWindowEventFilter::~QtWindowEventFilter() = default; |
| | | |
| | | bool QtWindowEventFilter::eventFilter(QObject *object, QEvent *event) { |
| | | bool QtWindowEventFilter::eventFilter(QObject *obj, QEvent *event) { |
| | | Q_UNUSED(obj) |
| | | auto type = event->type(); |
| | | if (type < QEvent::MouseButtonPress || type > QEvent::MouseMove) { |
| | | return false; |
| | |
| | | AbstractWindowContext::virtual_hook(id, data); |
| | | } |
| | | |
| | | bool QtWindowContext::setupHost() { |
| | | bool QtWindowContext::winIdChanged() { |
| | | m_delegate->setWindowFlags(m_host, Qt::FramelessWindowHint); |
| | | std::ignore = new QtWindowEventFilter(this, this); |
| | | return true; |
| | |
| | | void virtual_hook(int id, void *data) override; |
| | | |
| | | protected: |
| | | bool setupHost() override; |
| | | bool winIdChanged() override; |
| | | }; |
| | | |
| | | } |
| | |
| | | return ::CallWindowProcW(g_qtWindowProc, hWnd, message, wParam, lParam); |
| | | } |
| | | |
| | | Win32WindowContext::Win32WindowContext() : AbstractWindowContext() { |
| | | static inline void addManagedWindow(HWND hWnd, Win32WindowContext *ctx) { |
| | | // Store original window proc |
| | | if (!g_qtWindowProc) { |
| | | g_qtWindowProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtrW(hWnd, GWLP_WNDPROC)); |
| | | } |
| | | |
| | | Win32WindowContext::~Win32WindowContext() { |
| | | // Hook window proc |
| | | ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(QWKHookedWndProc)); |
| | | |
| | | // Install global native event filter |
| | | WindowsNativeEventFilter::install(); |
| | | |
| | | // Save window handle mapping |
| | | g_wndProcHash->insert(hWnd, ctx); |
| | | } |
| | | |
| | | static inline void removeManagedWindow(HWND hWnd, bool restore) { |
| | | // Remove window handle mapping |
| | | if (auto hWnd = reinterpret_cast<HWND>(windowId); hWnd) { |
| | | g_wndProcHash->remove(hWnd); |
| | | if (!g_wndProcHash->remove(hWnd)) |
| | | return; |
| | | |
| | | // Remove event filter if the all windows has been destroyed |
| | | if (g_wndProcHash->empty()) { |
| | | WindowsNativeEventFilter::uninstall(); |
| | | } |
| | | |
| | | // Restore window proc |
| | | if (restore) { |
| | | ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(g_qtWindowProc)); |
| | | } |
| | | } |
| | | |
| | | Win32WindowContext::Win32WindowContext() : AbstractWindowContext() { |
| | | } |
| | | |
| | | Win32WindowContext::~Win32WindowContext() { |
| | | if (windowId) { |
| | | removeManagedWindow(reinterpret_cast<HWND>(windowId), false); |
| | | } |
| | | } |
| | | |
| | |
| | | return getWindowFrameBorderThickness(reinterpret_cast<HWND>(windowId)); |
| | | } |
| | | |
| | | bool Win32WindowContext::setupHost() { |
| | | void Win32WindowContext::winIdChanged(QWindow *oldWindow, bool destroyed) { |
| | | if (oldWindow) { |
| | | removeManagedWindow(reinterpret_cast<HWND>(windowId), !destroyed); |
| | | } |
| | | |
| | | if (!m_windowHandle) { |
| | | return; |
| | | } |
| | | |
| | | // Install window hook |
| | | auto winId = m_windowHandle->winId(); |
| | | auto hWnd = reinterpret_cast<HWND>(winId); |
| | | |
| | | #if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) |
| | | for (const auto attr : |
| | | {_DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, _DWMWA_USE_IMMERSIVE_DARK_MODE}) { |
| | | for (const auto attr : { |
| | | _DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, |
| | | _DWMWA_USE_IMMERSIVE_DARK_MODE, |
| | | }) { |
| | | const BOOL enable = TRUE; |
| | | DynamicApis::instance().pDwmSetWindowAttribute(hWnd, attr, &enable, sizeof(enable)); |
| | | } |
| | |
| | | // Inform Qt we want and have set custom margins |
| | | updateInternalWindowFrameMargins(hWnd, m_windowHandle); |
| | | |
| | | // Store original window proc |
| | | if (!g_qtWindowProc) { |
| | | g_qtWindowProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtrW(hWnd, GWLP_WNDPROC)); |
| | | } |
| | | // Add managed window |
| | | addManagedWindow(hWnd, this); |
| | | |
| | | // Hook window proc |
| | | ::SetWindowLongPtrW(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(QWKHookedWndProc)); |
| | | |
| | | // Install global native event filter |
| | | WindowsNativeEventFilter::install(); |
| | | |
| | | // Cache window ID |
| | | // Cache win id |
| | | windowId = winId; |
| | | |
| | | // Save window handle mapping |
| | | g_wndProcHash->insert(hWnd, this); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | bool Win32WindowContext::windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, |
| | |
| | | int borderThickness() const; |
| | | |
| | | protected: |
| | | bool setupHost() override; |
| | | void winIdChanged(QWindow *oldWindow, bool destroyed) override; |
| | | |
| | | public: |
| | | bool windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result); |
| | |
| | | #endif |
| | | } |
| | | |
| | | bool WindowAgentBasePrivate::setup(QObject *host, WindowItemDelegate *delegate) { |
| | | std::unique_ptr<AbstractWindowContext> ctx(createContext()); |
| | | if (!ctx->setup(host, delegate)) { |
| | | return false; |
| | | } |
| | | context = std::move(ctx); |
| | | return true; |
| | | void WindowAgentBasePrivate::setup(QObject *host, WindowItemDelegate *delegate) { |
| | | auto ctx = createContext(); |
| | | ctx->setup(host, delegate); |
| | | context.reset(ctx); |
| | | } |
| | | |
| | | WindowAgentBase::~WindowAgentBase() = default; |
| | |
| | | |
| | | virtual AbstractWindowContext *createContext() const; |
| | | |
| | | bool setup(QObject *host, WindowItemDelegate *delegate); |
| | | void setup(QObject *host, WindowItemDelegate *delegate); |
| | | |
| | | std::unique_ptr<AbstractWindowContext> context; |
| | | |
| | |
| | | return false; |
| | | } |
| | | |
| | | if (!d->setup(window, new QuickItemDelegate())) { |
| | | return false; |
| | | } |
| | | d->setup(window, new QuickItemDelegate()); |
| | | d->hostWindow = window; |
| | | |
| | | #ifdef Q_OS_WINDOWS |
| | |
| | | |
| | | namespace QWK { |
| | | |
| | | class WidgetWinIdChangeEventFilter : public QObject { |
| | | public: |
| | | explicit WidgetWinIdChangeEventFilter(QWidget *widget, AbstractWindowContext *ctx) |
| | | : QObject(ctx), widget(widget), 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: |
| | | QWidget *widget; |
| | | AbstractWindowContext *ctx; |
| | | }; |
| | | |
| | | WidgetWindowAgentPrivate::WidgetWindowAgentPrivate() = default; |
| | | |
| | | WidgetWindowAgentPrivate::~WidgetWindowAgentPrivate() = default; |
| | |
| | | w->setAttribute(Qt::WA_DontCreateNativeAncestors); |
| | | w->setAttribute(Qt::WA_NativeWindow); |
| | | |
| | | if (!d->setup(w, new WidgetItemDelegate())) { |
| | | return false; |
| | | } |
| | | d->setup(w, new WidgetItemDelegate()); |
| | | d->hostWidget = w; |
| | | |
| | | #ifdef Q_OS_WINDOWS |
| | | d->setupWindows10BorderWorkaround(); |
| | | #endif |
| | | std::ignore = new WidgetWinIdChangeEventFilter(w, d->context.get()); |
| | | |
| | | return true; |
| | | } |
| | | |
| | |
| | | |
| | | protected: |
| | | bool eventFilter(QObject *obj, QEvent *event) override { |
| | | Q_UNUSED(obj) |
| | | switch (event->type()) { |
| | | case QEvent::Move: |
| | | case QEvent::Resize: { |
| | |
| | | default: |
| | | break; |
| | | } |
| | | return QObject::eventFilter(obj, event); |
| | | return false; |
| | | } |
| | | |
| | | protected: |
| | |
| | | } |
| | | |
| | | bool eventFilter(QObject *obj, QEvent *event) override { |
| | | Q_UNUSED(obj) |
| | | switch (event->type()) { |
| | | case QEvent::Paint: { |
| | | if (widget->windowState() & (Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen)) |