Sine Striker
2023-12-03 7a54badb4a5782c551deb2919f3c489fdb4fbc02
Add snap layout handling
16个文件已修改
1个文件已添加
542 ■■■■■ 已修改文件
src/core/CMakeLists.txt 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext.cpp 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext_p.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext.cpp 380 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext_p.h 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/qwindowkit_windows.h 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/qwkcoreglobal_p.h 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/windowitemdelegate.cpp 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/windowitemdelegate.h 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/quick/quickitemdelegate.cpp 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/quick/quickitemdelegate_p.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/quick/quickwindowagent.cpp 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/quick/quickwindowagent_p.h 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetitemdelegate.cpp 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetitemdelegate_p.h 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent.cpp 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent_p.h 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/CMakeLists.txt
@@ -10,6 +10,7 @@
    corewindowagent_p.h
    corewindowagent.cpp
    windowitemdelegate.h
    windowitemdelegate.cpp
    contexts/abstractwindowcontext_p.h
    contexts/abstractwindowcontext.cpp
)
src/core/contexts/abstractwindowcontext.cpp
@@ -84,4 +84,21 @@
        return hitTestVisibleShape;
    }
    bool AbstractWindowContext::isInSystemButtons(const QPoint &pos,
                                                  CoreWindowAgent::SystemButton *button) const {
        *button = CoreWindowAgent::Unknown;
        for (int i = CoreWindowAgent::WindowIcon; i <= CoreWindowAgent::Close; ++i) {
            auto currentButton = m_systemButtons[i];
            if (!currentButton || !m_delegate->isVisible(currentButton) ||
                !m_delegate->isEnabled(currentButton)) {
                continue;
            }
            if (m_delegate->mapGeometryToScene(currentButton).contains(pos)) {
                *button = CoreWindowAgent::WindowIcon;
                return true;
            }
        }
        return false;
    }
}
src/core/contexts/abstractwindowcontext_p.h
@@ -40,6 +40,7 @@
        void showSystemMenu(const QPoint &pos);
        QRegion hitTestShape() const;
        bool isInSystemButtons(const QPoint &pos, CoreWindowAgent::SystemButton *button) const;
    protected:
        QWindow *m_windowHandle;
src/core/contexts/win32windowcontext.cpp
@@ -1,8 +1,12 @@
#include "win32windowcontext_p.h"
#include <optional>
#include <QtCore/QHash>
#include <QtCore/QAbstractNativeEventFilter>
#include <QtCore/QCoreApplication>
#include <QtGui/private/qhighdpiscaling_p.h>
#include "qwkcoreglobal_p.h"
@@ -30,6 +34,76 @@
    };
    static WindowsNativeEventFilter *g_nativeFilter = nullptr;
    static inline QPoint fromNativeLocalPosition(const QWindow *window, const QPoint &point) {
#if 1
        return QHighDpi::fromNativeLocalPosition(point, window);
#else
        return QPointF(QPointF(point) / window->devicePixelRatio()).toPoint();
#endif
    }
    static inline Win32WindowContext::WindowPart getHitWindowPart(int hitTestResult) {
        switch (hitTestResult) {
            case HTCLIENT:
                return Win32WindowContext::ClientArea;
            case HTCAPTION:
                return Win32WindowContext::TitleBar;
            case HTSYSMENU:
            case HTHELP:
            case HTREDUCE:
            case HTZOOM:
            case HTCLOSE:
                return Win32WindowContext::ChromeButton;
            case HTLEFT:
            case HTRIGHT:
            case HTTOP:
            case HTTOPLEFT:
            case HTTOPRIGHT:
            case HTBOTTOM:
            case HTBOTTOMLEFT:
            case HTBOTTOMRIGHT:
                return Win32WindowContext::ResizeBorder;
            case HTBORDER:
                return Win32WindowContext::FixedBorder;
            default:
                break;
        }
        return Win32WindowContext::Outside;
    }
    static bool isValidWindow(WId windowId, bool checkVisible, bool checkTopLevel) {
        const auto hwnd = reinterpret_cast<HWND>(windowId);
        if (::IsWindow(hwnd) == FALSE) {
            return false;
        }
        const LONG_PTR styles = ::GetWindowLongPtrW(hwnd, GWL_STYLE);
        if ((styles == 0) || (styles & WS_DISABLED)) {
            return false;
        }
        const LONG_PTR exStyles = ::GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
        if ((exStyles == 0) || (exStyles & WS_EX_TOOLWINDOW)) {
            return false;
        }
        RECT rect = {0, 0, 0, 0};
        if (::GetWindowRect(hwnd, &rect) == FALSE) {
            return false;
        }
        if ((rect.left >= rect.right) || (rect.top >= rect.bottom)) {
            return false;
        }
        if (checkVisible) {
            if (::IsWindowVisible(hwnd) == FALSE) {
                return false;
            }
        }
        if (checkTopLevel) {
            if (::GetAncestor(hwnd, GA_ROOT) != hwnd) {
                return false;
            }
        }
        return true;
    }
    // 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
@@ -108,7 +182,7 @@
    }
    Win32WindowContext::Win32WindowContext(QWindow *window, WindowItemDelegate *delegate)
        : AbstractWindowContext(window, delegate), windowId(0) {
        : AbstractWindowContext(window, delegate) {
    }
    Win32WindowContext::~Win32WindowContext() {
@@ -158,7 +232,31 @@
                                        LRESULT *result) {
        *result = FALSE;
        Q_UNUSED(windowId)
        // We should skip these messages otherwise we will get crashes.
        // NOTE: WM_QUIT won't be posted to the WindowProc function.
        switch (message) {
            case WM_CLOSE:
            case WM_DESTROY:
            case WM_NCDESTROY:
            // Undocumented messages:
            case WM_UAHDESTROYWINDOW:
            case WM_UNREGISTER_WINDOW_SERVICES:
                return false;
            default:
                break;
        }
        if (!isValidWindow(windowId, false, true)) {
            return false;
        }
        // Test snap layout
        if (snapLayoutHandler(hWnd, message, wParam, lParam, result)) {
            return true;
        }
        // TODO: Uncomment and do something
        // bool frameBorderVisible = Utils::isWindowFrameBorderVisible();
        // TODO: Implement
        // ...
@@ -166,4 +264,282 @@
        return false; // Not handled
    }
    static constexpr const auto kMessageTag = WPARAM(0x97CCEA99);
    static inline constexpr bool isTaggedMessage(WPARAM wParam) {
        return (wParam == kMessageTag);
    }
    static quint64 getKeyState() {
        quint64 result = 0;
        const auto &get = [](const int virtualKey) -> bool {
            return (::GetAsyncKeyState(virtualKey) < 0);
        };
        const bool buttonSwapped = (::GetSystemMetrics(SM_SWAPBUTTON) != FALSE);
        if (get(VK_LBUTTON)) {
            result |= (buttonSwapped ? MK_RBUTTON : MK_LBUTTON);
        }
        if (get(VK_RBUTTON)) {
            result |= (buttonSwapped ? MK_LBUTTON : MK_RBUTTON);
        }
        if (get(VK_SHIFT)) {
            result |= MK_SHIFT;
        }
        if (get(VK_CONTROL)) {
            result |= MK_CONTROL;
        }
        if (get(VK_MBUTTON)) {
            result |= MK_MBUTTON;
        }
        if (get(VK_XBUTTON1)) {
            result |= MK_XBUTTON1;
        }
        if (get(VK_XBUTTON2)) {
            result |= MK_XBUTTON2;
        }
        return result;
    }
    static void emulateClientAreaMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
                                         const std::optional<int> &overrideMessage = std::nullopt) {
        const int myMsg = overrideMessage.value_or(message);
        const auto wParamNew = [myMsg, wParam]() -> WPARAM {
            if (myMsg == WM_NCMOUSELEAVE) {
                // wParam is always ignored in mouse leave messages, but here we
                // give them a special tag to be able to distinguish which messages
                // are sent by ourselves.
                return kMessageTag;
            }
            const quint64 keyState = getKeyState();
            if ((myMsg >= WM_NCXBUTTONDOWN) && (myMsg <= WM_NCXBUTTONDBLCLK)) {
                const auto xButtonMask = GET_XBUTTON_WPARAM(wParam);
                return MAKEWPARAM(keyState, xButtonMask);
            }
            return keyState;
        }();
        const auto lParamNew = [myMsg, lParam, hWnd]() -> LPARAM {
            if (myMsg == WM_NCMOUSELEAVE) {
                // lParam is always ignored in mouse leave messages.
                return 0;
            }
            const auto screenPos = POINT{GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
            POINT clientPos = screenPos;
            if (::ScreenToClient(hWnd, &clientPos) == FALSE) {
                return 0;
            }
            return MAKELPARAM(clientPos.x, clientPos.y);
        }();
#if 0
#  define SEND_MESSAGE ::SendMessageW
#else
#  define SEND_MESSAGE ::PostMessageW
#endif
        switch (myMsg) {
            case WM_NCHITTEST: // Treat hit test messages as mouse move events.
            case WM_NCMOUSEMOVE:
                SEND_MESSAGE(hWnd, WM_MOUSEMOVE, wParamNew, lParamNew);
                break;
            case WM_NCLBUTTONDOWN:
                SEND_MESSAGE(hWnd, WM_LBUTTONDOWN, wParamNew, lParamNew);
                break;
            case WM_NCLBUTTONUP:
                SEND_MESSAGE(hWnd, WM_LBUTTONUP, wParamNew, lParamNew);
                break;
            case WM_NCLBUTTONDBLCLK:
                SEND_MESSAGE(hWnd, WM_LBUTTONDBLCLK, wParamNew, lParamNew);
                break;
            case WM_NCRBUTTONDOWN:
                SEND_MESSAGE(hWnd, WM_RBUTTONDOWN, wParamNew, lParamNew);
                break;
            case WM_NCRBUTTONUP:
                SEND_MESSAGE(hWnd, WM_RBUTTONUP, wParamNew, lParamNew);
                break;
            case WM_NCRBUTTONDBLCLK:
                SEND_MESSAGE(hWnd, WM_RBUTTONDBLCLK, wParamNew, lParamNew);
                break;
            case WM_NCMBUTTONDOWN:
                SEND_MESSAGE(hWnd, WM_MBUTTONDOWN, wParamNew, lParamNew);
                break;
            case WM_NCMBUTTONUP:
                SEND_MESSAGE(hWnd, WM_MBUTTONUP, wParamNew, lParamNew);
                break;
            case WM_NCMBUTTONDBLCLK:
                SEND_MESSAGE(hWnd, WM_MBUTTONDBLCLK, wParamNew, lParamNew);
                break;
            case WM_NCXBUTTONDOWN:
                SEND_MESSAGE(hWnd, WM_XBUTTONDOWN, wParamNew, lParamNew);
                break;
            case WM_NCXBUTTONUP:
                SEND_MESSAGE(hWnd, WM_XBUTTONUP, wParamNew, lParamNew);
                break;
            case WM_NCXBUTTONDBLCLK:
                SEND_MESSAGE(hWnd, WM_XBUTTONDBLCLK, wParamNew, lParamNew);
                break;
#if 0 // ### TODO: How to handle touch events?
        case WM_NCPOINTERUPDATE:
        case WM_NCPOINTERDOWN:
        case WM_NCPOINTERUP:
            break;
#endif
            case WM_NCMOUSEHOVER:
                SEND_MESSAGE(hWnd, WM_MOUSEHOVER, wParamNew, lParamNew);
                break;
            case WM_NCMOUSELEAVE:
                SEND_MESSAGE(hWnd, WM_MOUSELEAVE, wParamNew, lParamNew);
                break;
            default:
                break;
        }
#undef SEND_MESSAGE
    }
    static bool requestForMouseLeaveMessage(HWND hWnd, bool nonClient) {
        TRACKMOUSEEVENT tme;
        SecureZeroMemory(&tme, sizeof(tme));
        tme.cbSize = sizeof(tme);
        tme.dwFlags = TME_LEAVE;
        if (nonClient) {
            tme.dwFlags |= TME_NONCLIENT;
        }
        tme.hwndTrack = hWnd;
        tme.dwHoverTime = HOVER_DEFAULT;
        if (::TrackMouseEvent(&tme) == FALSE) {
            return false;
        }
        return true;
    }
    bool Win32WindowContext::snapLayoutHandler(HWND hWnd, UINT message, WPARAM wParam,
                                                     LPARAM lParam, LRESULT *result) {
        switch (message) {
            case WM_MOUSELEAVE: {
                if (!isTaggedMessage(wParam)) {
                    // Qt will call TrackMouseEvent() to get the WM_MOUSELEAVE message when it
                    // receives WM_MOUSEMOVE messages, and since we are converting every
                    // WM_NCMOUSEMOVE message to WM_MOUSEMOVE message and send it back to the window
                    // to be able to hover our controls, we also get lots of WM_MOUSELEAVE messages
                    // at the same time because of the reason above, and these superfluous mouse
                    // leave events cause Qt to think the mouse has left the control, and thus we
                    // actually lost the hover state. So we filter out these superfluous mouse leave
                    // events here to avoid this issue.
                    DWORD dwScreenPos = ::GetMessagePos();
                    QPoint qtScenePos =
                        fromNativeLocalPosition(m_windowHandle, QPoint(GET_X_LPARAM(dwScreenPos),
                                                                       GET_Y_LPARAM(dwScreenPos)));
                    auto dummy = CoreWindowAgent::Unknown;
                    if (isInSystemButtons(qtScenePos, &dummy)) {
                        mouseLeaveBlocked = true;
                        *result = FALSE;
                        return true;
                    }
                }
                mouseLeaveBlocked = false;
                break;
            }
            case WM_MOUSEMOVE: {
                if ((lastHitTestResult != WindowPart::ChromeButton) && mouseLeaveBlocked) {
                    mouseLeaveBlocked = false;
                    std::ignore = requestForMouseLeaveMessage(hWnd, false);
                }
                break;
            }
            case WM_NCMOUSEMOVE:
            case WM_NCLBUTTONDOWN:
            case WM_NCLBUTTONUP:
            case WM_NCLBUTTONDBLCLK:
            case WM_NCRBUTTONDOWN:
            case WM_NCRBUTTONUP:
            case WM_NCRBUTTONDBLCLK:
            case WM_NCMBUTTONDOWN:
            case WM_NCMBUTTONUP:
            case WM_NCMBUTTONDBLCLK:
            case WM_NCXBUTTONDOWN:
            case WM_NCXBUTTONUP:
            case WM_NCXBUTTONDBLCLK:
#if 0 // ### TODO: How to handle touch events?
    case WM_NCPOINTERUPDATE:
    case WM_NCPOINTERDOWN:
    case WM_NCPOINTERUP:
#endif
            case WM_NCMOUSEHOVER: {
                const WindowPart currentWindowPart = lastHitTestResult;
                if (message == WM_NCMOUSEMOVE) {
                    if (currentWindowPart != WindowPart::ChromeButton) {
                        std::ignore = m_delegate->resetQtGrabbedControl();
                        if (mouseLeaveBlocked) {
                            emulateClientAreaMessage(hWnd, message, wParam, lParam,
                                                     WM_NCMOUSELEAVE);
                        }
                    }
                    // We need to make sure we get the right hit-test result when a WM_NCMOUSELEAVE
                    // 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
                    // leaving the window, current window part remains as `Outside`.
                    lastHitTestResult = WindowPart::Outside;
                }
                if (currentWindowPart == WindowPart::ChromeButton) {
                    emulateClientAreaMessage(hWnd, message, wParam, lParam);
                    if (message == WM_NCMOUSEMOVE) {
                        // ### FIXME FIXME FIXME
                        // ### FIXME: Calling DefWindowProc() here is really dangerous, investigate
                        // how to avoid doing this.
                        // ### FIXME FIXME FIXME
                        *result = ::DefWindowProcW(hWnd, WM_NCMOUSEMOVE, wParam, lParam);
                    } else {
                        // According to MSDN, we should return non-zero for X button messages to
                        // indicate we have handled these messages (due to historical reasons), for
                        // all other messages we should return zero instead.
                        *result =
                            (((message >= WM_NCXBUTTONDOWN) && (message <= WM_NCXBUTTONDBLCLK))
                                 ? TRUE
                                 : FALSE);
                    }
                    return true;
                }
                break;
            }
            case WM_NCMOUSELEAVE: {
                const WindowPart currentWindowPart = lastHitTestResult;
                if (currentWindowPart == WindowPart::ChromeButton) {
                    // If we press on the chrome button and move mouse, Windows will take the
                    // pressing area as HTCLIENT which maybe because of our former retransmission of
                    // WM_NCLBUTTONDOWN, as a result, a WM_NCMOUSELEAVE will come immediately and a
                    // lot of WM_MOUSEMOVE will come if we move the mouse, we should track the mouse
                    // in advance.
                    if (mouseLeaveBlocked) {
                        mouseLeaveBlocked = false;
                        std::ignore = requestForMouseLeaveMessage(hWnd, false);
                    }
                } else {
                    if (mouseLeaveBlocked) {
                        // The mouse is moving from the chrome button to other non-client area, we
                        // should emulate a WM_MOUSELEAVE message to reset the button state.
                        emulateClientAreaMessage(hWnd, message, wParam, lParam, WM_NCMOUSELEAVE);
                    }
                    if (currentWindowPart == WindowPart::Outside) {
                        // Notice: we're not going to clear window part cache when the mouse leaves
                        // window from client area, which means we will get previous window part as
                        // HTCLIENT if the mouse leaves window from client area and enters window
                        // from non-client area, but it has no bad effect.
                        std::ignore = m_delegate->resetQtGrabbedControl();
                    }
                }
                break;
            }
            default:
                break;
        }
        return false;
    }
}
src/core/contexts/win32windowcontext_p.h
@@ -12,13 +12,31 @@
        Win32WindowContext(QWindow *window, WindowItemDelegate *delegate);
        ~Win32WindowContext() override;
        enum WindowPart {
            Outside,
            ClientArea,
            ChromeButton,
            ResizeBorder,
            FixedBorder,
            TitleBar,
        };
    public:
        bool setup() override;
        bool windowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT *result);
        bool snapLayoutHandler(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam,
                               LRESULT *result);
    protected:
        WId windowId;
        WId windowId = 0;
        // Store the last hit test result, it's 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().
        bool mouseLeaveBlocked = false;
    };
}
src/core/qwindowkit_windows.h
@@ -21,4 +21,13 @@
#  define GET_Y_LPARAM(lp) (static_cast<int>(static_cast<short>(HIWORD(lp))))
#endif
// Maybe undocumented Windows messages
#ifndef WM_UAHDESTROYWINDOW
#  define WM_UAHDESTROYWINDOW (0x0090)
#endif
#ifndef WM_UNREGISTER_WINDOW_SERVICES
#  define WM_UNREGISTER_WINDOW_SERVICES (0x0272)
#endif
#endif // QWINDOWKIT_WINDOWS_H
src/core/qwkcoreglobal_p.h
@@ -24,13 +24,4 @@
#  define QWK_FATAL qCFatal(qWindowKitLog)
#endif
// MOC can't handle C++ attributes before 5.15.
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
#  define Q_NODISCARD    [[nodiscard]]
#  define Q_MAYBE_UNUSED [[maybe_unused]]
#else
#  define Q_NODISCARD
#  define Q_MAYBE_UNUSED
#endif
#endif // QWKCOREGLOBAL_P_H
src/core/windowitemdelegate.cpp
New file
@@ -0,0 +1,13 @@
#include "windowitemdelegate.h"
namespace QWK {
    WindowItemDelegate::WindowItemDelegate() = default;
    WindowItemDelegate::~WindowItemDelegate() = default;
    bool WindowItemDelegate::resetQtGrabbedControl() const {
        return true;
    }
}
src/core/windowitemdelegate.h
@@ -2,22 +2,28 @@
#define WINDOWITEMDELEGATE_H
#include <QtCore/QObject>
#include <QtCore/QPoint>
#include <QtGui/QWindow>
#include <QWKCore/qwkcoreglobal.h>
#include <QWKCore/corewindowagent.h>
namespace QWK {
    class WindowItemDelegate {
    class QWK_CORE_EXPORT WindowItemDelegate {
    public:
        WindowItemDelegate() = default;
        virtual ~WindowItemDelegate() = default;
        WindowItemDelegate();
        virtual ~WindowItemDelegate();
    public:
        virtual QWindow *window(QObject *obj) const = 0;
        // Property query
        virtual bool isEnabled(QObject *obj) const = 0;
        virtual bool isVisible(QObject *obj) const = 0;
        virtual QRect mapGeometryToScene(const QObject *obj) const = 0;
        // Callbacks
        virtual bool resetQtGrabbedControl() const;
    private:
        Q_DISABLE_COPY_MOVE(WindowItemDelegate)
src/quick/quickitemdelegate.cpp
@@ -11,15 +11,26 @@
    QuickItemDelegate::~QuickItemDelegate() = default;
    QWindow *QuickItemDelegate::window(QObject *obj) const {
        return qobject_cast<QQuickItem *>(obj)->window();
        return static_cast<QQuickItem *>(obj)->window();
    }
    bool QuickItemDelegate::isEnabled(QObject *obj) const {
        return qobject_cast<QQuickItem *>(obj)->isEnabled();
        return static_cast<QQuickItem *>(obj)->isEnabled();
    }
    bool QuickItemDelegate::isVisible(QObject *obj) const {
        return qobject_cast<QQuickItem *>(obj)->isVisible();
        return static_cast<QQuickItem *>(obj)->isVisible();
    }
    QRect QuickItemDelegate::mapGeometryToScene(const QObject *obj) const {
        auto item = static_cast<const QQuickItem *>(obj);
        const QPointF originPoint = item->mapToScene(QPointF(0.0, 0.0));
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
        const QSizeF size = item->size();
#else
        const QSizeF size = {item->width(), item->height()};
#endif
        return QRectF(originPoint, size).toRect();
    }
}
src/quick/quickitemdelegate_p.h
@@ -19,6 +19,7 @@
        bool isEnabled(QObject *obj) const override;
        bool isVisible(QObject *obj) const override;
        QRect mapGeometryToScene(const QObject *obj) const override;
    };
}
src/quick/quickwindowagent.cpp
@@ -30,14 +30,14 @@
        }
        Q_D(QuickWindowAgent);
        if (d->host) {
        if (d->hostWindow) {
            return false;
        }
        if (!d->setup(window, new QuickItemDelegate())) {
            return true;
        }
        d->host = window;
        d->hostWindow = window;
        return true;
    }
@@ -58,7 +58,7 @@
    QQuickItem *QuickWindowAgent::systemButton(SystemButton button) const {
        Q_D(const QuickWindowAgent);
        return qobject_cast<QQuickItem *>(d->eventHandler->systemButton(button));
        return static_cast<QQuickItem *>(d->eventHandler->systemButton(button));
    }
    void QuickWindowAgent::setSystemButton(SystemButton button, QQuickItem *item) {
@@ -71,7 +71,7 @@
    QQuickItem *QuickWindowAgent::titleBar() const {
        Q_D(const QuickWindowAgent);
        return qobject_cast<QQuickItem *>(d->eventHandler->titleBar());
        return static_cast<QQuickItem *>(d->eventHandler->titleBar());
    }
    void QuickWindowAgent::setTitleBar(QQuickItem *item) {
src/quick/quickwindowagent_p.h
@@ -15,7 +15,7 @@
        void init();
        // Host
        QQuickWindow *host{};
        QQuickWindow *hostWindow{};
    };
}
src/widgets/widgetitemdelegate.cpp
@@ -1,25 +1,48 @@
#include "widgetitemdelegate_p.h"
#include <QtGui/QMouseEvent>
#include <QtWidgets/QWidget>
#include <QtWidgets/QApplication>
extern Q_WIDGETS_EXPORT QWidget *qt_button_down;
namespace QWK {
    
    WidgetItemDelegate::WidgetItemDelegate() {
    }
    WidgetItemDelegate::WidgetItemDelegate() = default;
    WidgetItemDelegate::~WidgetItemDelegate() {
    }
    WidgetItemDelegate::~WidgetItemDelegate() = default;
    QWindow *WidgetItemDelegate::window(QObject *obj) const {
        return qobject_cast<QWidget *>(obj)->windowHandle();
        return static_cast<QWidget *>(obj)->windowHandle();
    }
    bool WidgetItemDelegate::isEnabled(QObject *obj) const {
        return qobject_cast<QWidget *>(obj)->isEnabled();
        return static_cast<QWidget *>(obj)->isEnabled();
    }
    bool WidgetItemDelegate::isVisible(QObject *obj) const {
        return qobject_cast<QWidget *>(obj)->isVisible();
        return static_cast<QWidget *>(obj)->isVisible();
    }
    QRect WidgetItemDelegate::mapGeometryToScene(const QObject *obj) const {
        auto widget = static_cast<const QWidget *>(obj);
        const QPoint originPoint = widget->mapTo(widget->window(), QPoint(0, 0));
        const QSize size = widget->size();
        return {originPoint, size};
    }
    bool WidgetItemDelegate::resetQtGrabbedControl() const {
        if (qt_button_down) {
            static constexpr const auto invalidPos = QPoint{-99999, -99999};
            const auto event =
                new QMouseEvent(QEvent::MouseButtonRelease, invalidPos, invalidPos, invalidPos,
                                Qt::LeftButton, QGuiApplication::mouseButtons() ^ Qt::LeftButton,
                                QGuiApplication::keyboardModifiers());
            QApplication::postEvent(qt_button_down, event);
            qt_button_down = nullptr;
            return true;
        }
        return false;
    }
}
src/widgets/widgetitemdelegate_p.h
@@ -19,6 +19,9 @@
        bool isEnabled(QObject *obj) const override;
        bool isVisible(QObject *obj) const override;
        QRect mapGeometryToScene(const QObject *obj) const override;
        bool resetQtGrabbedControl() const override;
    };
}
src/widgets/widgetwindowagent.cpp
@@ -28,7 +28,7 @@
        }
        Q_D(WidgetWindowAgent);
        if (d->host) {
        if (d->hostWidget) {
            return false;
        }
@@ -36,7 +36,7 @@
        if (!d->setup(w->windowHandle(), new WidgetItemDelegate())) {
            return false;
        }
        d->host = w;
        d->hostWidget = w;
        return true;
    }
@@ -57,7 +57,7 @@
    QWidget *WidgetWindowAgent::systemButton(CoreWindowAgent::SystemButton button) const {
        Q_D(const WidgetWindowAgent);
        return qobject_cast<QWidget *>(d->eventHandler->systemButton(button));
        return static_cast<QWidget *>(d->eventHandler->systemButton(button));
    }
    void WidgetWindowAgent::setSystemButton(CoreWindowAgent::SystemButton button, QWidget *w) {
@@ -70,7 +70,7 @@
    QWidget *WidgetWindowAgent::titleBar() const {
        Q_D(const WidgetWindowAgent);
        return qobject_cast<QWidget *>(d->eventHandler->titleBar());
        return static_cast<QWidget *>(d->eventHandler->titleBar());
    }
    void WidgetWindowAgent::setTitleBar(QWidget *w) {
src/widgets/widgetwindowagent_p.h
@@ -15,7 +15,7 @@
        void init();
        // Host
        QWidget *host{};
        QWidget *hostWidget{};
    };
}