README.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/contexts/cocoawindowcontext.mm | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/contexts/cocoawindowcontext_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/contexts/qtwindowcontext.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/core/windowitemdelegate_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/quick/quickitemdelegate.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/quick/quickitemdelegate_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/widgets/widgetitemdelegate.cpp | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
src/widgets/widgetitemdelegate_p.h | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
README.md
@@ -14,7 +14,7 @@ | Component | Requirement | Detailed | |:---------:|:-----------:|:------------------------------------:| | Qt | \>=5.15.2 | Core, Gui, Widgets, Quick | | Qt | \>=5.15 | Core, Gui, Widgets, Quick | | Compiler | \>=C++17 | MSVC 2019, GCC, Clang | | CMake | \>=3.17 | >=3.20 is recommended | src/core/contexts/cocoawindowcontext.mm
@@ -104,9 +104,10 @@ nswindow.hasShadow = YES; nswindow.showsToolbarButton = NO; nswindow.movableByWindowBackground = NO; //nswindow.movable = NO; // This line causes the window in the wrong position when become fullscreen. // For some unknown reason, we don't need the following hack in Qt versions below or // equal to 6.2.4. // nswindow.movable = NO; // This line causes the window in the wrong position when // become fullscreen. // For some unknown reason, we don't need the following hack in Qt versions below or // equal to 6.2.4. #if (QT_VERSION > QT_VERSION_CHECK(6, 2, 4)) [nswindow standardWindowButton:NSWindowCloseButton].hidden = (visible ? NO : YES); [nswindow standardWindowButton:NSWindowMiniaturizeButton].hidden = (visible ? NO : YES); @@ -238,9 +239,145 @@ return it.value(); } CocoaWindowContext::CocoaWindowContext() : AbstractWindowContext() {} class CocoaWindowEventFilter : public QObject { public: explicit CocoaWindowEventFilter(AbstractWindowContext *context, QObject *parent = nullptr); ~CocoaWindowEventFilter() override; CocoaWindowContext::~CocoaWindowContext() = default; enum WindowStatus { Idle, WaitingRelease, PreparingMove, Moving, }; protected: bool eventFilter(QObject *object, QEvent *event) override; private: AbstractWindowContext *m_context; bool m_cursorShapeChanged; WindowStatus m_windowStatus; }; CocoaWindowEventFilter::CocoaWindowEventFilter(AbstractWindowContext *context, QObject *parent) : QObject(parent), m_context(context), m_cursorShapeChanged(false), m_windowStatus(Idle) { m_context->window()->installEventFilter(this); } CocoaWindowEventFilter::~CocoaWindowEventFilter() = default; bool CocoaWindowEventFilter::eventFilter(QObject *object, QEvent *event) { auto type = event->type(); if (type < QEvent::MouseButtonPress || type > QEvent::MouseMove) { return false; } QObject *host = m_context->host(); QWindow *window = m_context->window(); WindowItemDelegate *delegate = m_context->delegate(); auto me = static_cast<const QMouseEvent *>(event); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QPoint scenePos = mouseEvent->scenePosition().toPoint(); QPoint globalPos = mouseEvent->globalPosition().toPoint(); #else QPoint scenePos = me->windowPos().toPoint(); QPoint globalPos = me->screenPos().toPoint(); #endif bool inTitleBar = m_context->isInTitleBarDraggableArea(scenePos); switch (type) { case QEvent::MouseButtonPress: { switch (me->button()) { case Qt::LeftButton: { if (inTitleBar) { // If we call startSystemMove() now but release the mouse without actual // movement, there will be no MouseReleaseEvent, so we defer it when the // mouse is actually moving for the first time m_windowStatus = PreparingMove; event->accept(); return true; } m_windowStatus = WaitingRelease; break; } case Qt::RightButton: { m_context->showSystemMenu(globalPos); break; } default: break; } break; } case QEvent::MouseButtonRelease: { switch (m_windowStatus) { case PreparingMove: case Moving: { m_windowStatus = Idle; event->accept(); return true; } case WaitingRelease: { m_windowStatus = Idle; break; } default: { if (inTitleBar) { event->accept(); return true; } break; } } break; } case QEvent::MouseMove: { switch (m_windowStatus) { case Moving: { return true; } case PreparingMove: { m_windowStatus = Moving; window->startSystemMove(); event->accept(); return true; } default: break; } break; } case QEvent::MouseButtonDblClick: { if (me->button() == Qt::LeftButton && inTitleBar && !delegate->isHostSizeFixed(host)) { Qt::WindowStates windowState = delegate->getWindowState(host); if (!(windowState & Qt::WindowFullScreen)) { if (windowState & Qt::WindowMaximized) { delegate->setWindowState(host, windowState & ~Qt::WindowMaximized); } else { delegate->setWindowState(host, windowState | Qt::WindowMaximized); } event->accept(); return true; } } break; } default: break; } return false; } CocoaWindowContext::CocoaWindowContext() : AbstractWindowContext() { } CocoaWindowContext::~CocoaWindowContext() { // TODO: deref something? } QString CocoaWindowContext::key() const { return QStringLiteral("cocoa"); @@ -250,10 +387,12 @@ } bool CocoaWindowContext::setupHost() { windowId = m_windowHandle->winId(); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) m_windowHandle->setProperty("_q_mac_wantsLayer", 1); #endif ensureWindowProxy(m_windowHandle->winId())->setSystemTitleBarVisible(false); ensureWindowProxy(windowId)->setSystemTitleBarVisible(false); std::ignore = new CocoaWindowEventFilter(this, this); return true; } src/core/contexts/cocoawindowcontext_p.h
@@ -1,7 +1,6 @@ #ifndef COCOAWINDOWCONTEXT_P_H #define COCOAWINDOWCONTEXT_P_H #include <QWKCore/private/abstractwindowcontext_p.h> namespace QWK { src/core/contexts/qtwindowcontext.cpp
@@ -1,5 +1,7 @@ #include "qtwindowcontext_p.h" #include <QtCore/QDebug> namespace QWK { static constexpr const quint8 kDefaultResizeBorderThickness = 8; @@ -40,8 +42,7 @@ #endif } static inline Qt::Edges calculateWindowEdges(const QWindow *window, const QPoint &pos) { static inline Qt::Edges calculateWindowEdges(const QWindow *window, const QPoint &pos) { #ifdef Q_OS_MACOS Q_UNUSED(window); Q_UNUSED(pos); @@ -73,94 +74,146 @@ #endif } class WindowEventFilter : public QObject { class QtWindowEventFilter : public QObject { public: explicit WindowEventFilter(AbstractWindowContext *context, QObject *parent = nullptr); ~WindowEventFilter() override; explicit QtWindowEventFilter(AbstractWindowContext *context, QObject *parent = nullptr); ~QtWindowEventFilter() override; enum WindowStatus { Idle, WaitingRelease, PreparingMove, Moving, Resizing, }; protected: bool eventFilter(QObject *object, QEvent *event) override; private: AbstractWindowContext *m_context; bool m_leftButtonPressed; bool m_cursorShapeChanged; WindowStatus m_windowStatus; }; WindowEventFilter::WindowEventFilter(AbstractWindowContext *context, QObject *parent) : QObject(parent), m_context(context), m_leftButtonPressed(false), m_cursorShapeChanged(false) {} QtWindowEventFilter::QtWindowEventFilter(AbstractWindowContext *context, QObject *parent) : QObject(parent), m_context(context), m_cursorShapeChanged(false), m_windowStatus(Idle) { m_context->window()->installEventFilter(this); } WindowEventFilter::~WindowEventFilter() = default; QtWindowEventFilter::~QtWindowEventFilter() = default; bool WindowEventFilter::eventFilter(QObject *object, QEvent *event) { const auto type = event->type(); bool QtWindowEventFilter::eventFilter(QObject *object, QEvent *event) { auto type = event->type(); if (type < QEvent::MouseButtonPress || type > QEvent::MouseMove) { return false; } QObject *host = m_context->host(); QWindow *window = m_context->window(); WindowItemDelegate *delegate = m_context->delegate(); const bool fixedSize = delegate->isHostSizeFixed(host); const auto mouseEvent = static_cast<const QMouseEvent *>(event); bool fixedSize = delegate->isHostSizeFixed(host); auto me = static_cast<const QMouseEvent *>(event); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const QPoint scenePos = mouseEvent->scenePosition().toPoint(); const QPoint globalPos = mouseEvent->globalPosition().toPoint(); QPoint scenePos = mouseEvent->scenePosition().toPoint(); QPoint globalPos = mouseEvent->globalPosition().toPoint(); #else const QPoint scenePos = mouseEvent->windowPos().toPoint(); const QPoint globalPos = mouseEvent->screenPos().toPoint(); QPoint scenePos = me->windowPos().toPoint(); QPoint globalPos = me->screenPos().toPoint(); #endif const bool inTitleBar = m_context->isInTitleBarDraggableArea(scenePos); bool inTitleBar = m_context->isInTitleBarDraggableArea(scenePos); switch (type) { case QEvent::MouseButtonPress: { if (mouseEvent->button() == Qt::LeftButton) { m_leftButtonPressed = true; if (!fixedSize) { const Qt::Edges edges = calculateWindowEdges(window, scenePos); if (edges != Qt::Edges{}) { window->startSystemResize(edges); switch (me->button()) { case Qt::LeftButton: { if (!fixedSize) { Qt::Edges edges = calculateWindowEdges(window, scenePos); if (edges != Qt::Edges()) { m_windowStatus = Resizing; window->startSystemResize(edges); event->accept(); return true; } } if (inTitleBar) { // If we call startSystemMove() now but release the mouse without actual // movement, there will be no MouseReleaseEvent, so we defer it when the // mouse is actually moving for the first time m_windowStatus = PreparingMove; event->accept(); return true; } m_windowStatus = WaitingRelease; break; } if (inTitleBar) { event->accept(); return true; case Qt::RightButton: { m_context->showSystemMenu(globalPos); break; } default: break; } break; } case QEvent::MouseButtonRelease: { if (mouseEvent->button() == Qt::LeftButton) { m_leftButtonPressed = false; if (inTitleBar) { switch (m_windowStatus) { case PreparingMove: case Moving: case Resizing: { m_windowStatus = Idle; event->accept(); return true; } } break; } case QEvent::MouseMove: { if (!fixedSize) { const Qt::CursorShape shape = calculateCursorShape(window, scenePos); if (shape == Qt::ArrowCursor) { if (m_cursorShapeChanged) { delegate->restoreCursorShape(host); m_cursorShapeChanged = false; case WaitingRelease: { m_windowStatus = Idle; break; } default: { if (inTitleBar) { event->accept(); return true; } } else { delegate->setCursorShape(host, shape); m_cursorShapeChanged = true; break; } } if (m_leftButtonPressed && inTitleBar) { window->startSystemMove(); event->accept(); return true; break; } case QEvent::MouseMove: { switch (m_windowStatus) { case Moving: { return true; } case PreparingMove: { m_windowStatus = Moving; window->startSystemMove(); event->accept(); return true; } case Idle: { if (!fixedSize) { const Qt::CursorShape shape = calculateCursorShape(window, scenePos); if (shape == Qt::ArrowCursor) { if (m_cursorShapeChanged) { delegate->restoreCursorShape(host); m_cursorShapeChanged = false; } } else { delegate->setCursorShape(host, shape); m_cursorShapeChanged = true; } } break; } default: break; } break; } case QEvent::MouseButtonDblClick: { if (mouseEvent->button() == Qt::LeftButton && inTitleBar && !fixedSize) { const Qt::WindowStates windowState = delegate->getWindowState(host); if (me->button() == Qt::LeftButton && inTitleBar && !fixedSize) { Qt::WindowStates windowState = delegate->getWindowState(host); if (!(windowState & Qt::WindowFullScreen)) { if (windowState & Qt::WindowMaximized) { delegate->setWindowState(host, windowState & ~Qt::WindowMaximized); @@ -173,10 +226,9 @@ } break; } default: { Q_UNREACHABLE(); return false; } default: break; } return false; } @@ -191,11 +243,18 @@ } void QtWindowContext::virtual_hook(int id, void *data) { switch (id) { case ShowSystemMenuHook: return; default: break; } AbstractWindowContext::virtual_hook(id, data); } bool QtWindowContext::setupHost() { m_delegate->setWindowFlags(m_host, Qt::FramelessWindowHint); m_windowHandle->installEventFilter(new WindowEventFilter(this, m_windowHandle)); std::ignore = new QtWindowEventFilter(this, this); return true; } src/core/windowitemdelegate_p.h
@@ -30,10 +30,10 @@ // Callbacks virtual void resetQtGrabbedControl(QObject *host) const; virtual void setWindowState(QObject *host, const Qt::WindowStates &state) const = 0; virtual void setCursorShape(QObject *host, const Qt::CursorShape shape) const = 0; virtual void setWindowState(QObject *host, Qt::WindowStates state) const = 0; virtual void setCursorShape(QObject *host, Qt::CursorShape shape) const = 0; virtual void restoreCursorShape(QObject *host) const = 0; virtual void setWindowFlags(QObject *host, const Qt::WindowFlags &flags) const = 0; virtual void setWindowFlags(QObject *host, Qt::WindowFlags flags) const = 0; private: Q_DISABLE_COPY(WindowItemDelegate) src/quick/quickitemdelegate.cpp
@@ -46,7 +46,7 @@ return static_cast<const QQuickWindow *>(host)->windowStates(); } void QuickItemDelegate::setWindowState(QObject *host, const Qt::WindowStates &state) const { void QuickItemDelegate::setWindowState(QObject *host, Qt::WindowStates state) const { static_cast<QQuickWindow *>(host)->setWindowStates(state); } @@ -62,7 +62,7 @@ return static_cast<const QQuickWindow *>(host)->flags(); } void QuickItemDelegate::setWindowFlags(QObject *host, const Qt::WindowFlags &flags) const { void QuickItemDelegate::setWindowFlags(QObject *host, Qt::WindowFlags flags) const { static_cast<QQuickWindow *>(host)->setFlags(flags); } src/quick/quickitemdelegate_p.h
@@ -26,10 +26,10 @@ Qt::WindowStates getWindowState(const QObject *host) const override; Qt::WindowFlags getWindowFlags(const QObject *host) const override; void setWindowState(QObject *host, const Qt::WindowStates &state) const override; void setCursorShape(QObject *host, const Qt::CursorShape shape) const override; void setWindowState(QObject *host, Qt::WindowStates state) const override; void setCursorShape(QObject *host, Qt::CursorShape shape) const override; void restoreCursorShape(QObject *host) const override; void setWindowFlags(QObject *host, const Qt::WindowFlags &flags) const override; void setWindowFlags(QObject *host, Qt::WindowFlags flags) const override; }; } src/widgets/widgetitemdelegate.cpp
@@ -78,11 +78,11 @@ return static_cast<const QWidget *>(host)->windowState(); } void WidgetItemDelegate::setWindowState(QObject *host, const Qt::WindowStates &state) const { void WidgetItemDelegate::setWindowState(QObject *host, Qt::WindowStates state) const { static_cast<QWidget *>(host)->setWindowState(state); } void WidgetItemDelegate::setCursorShape(QObject *host, const Qt::CursorShape shape) const { void WidgetItemDelegate::setCursorShape(QObject *host, Qt::CursorShape shape) const { static_cast<QWidget *>(host)->setCursor(QCursor(shape)); } @@ -94,7 +94,7 @@ return static_cast<const QWidget *>(host)->windowFlags(); } void WidgetItemDelegate::setWindowFlags(QObject *host, const Qt::WindowFlags &flags) const { void WidgetItemDelegate::setWindowFlags(QObject *host, Qt::WindowFlags flags) const { static_cast<QWidget *>(host)->setWindowFlags(flags); } src/widgets/widgetitemdelegate_p.h
@@ -27,10 +27,10 @@ Qt::WindowFlags getWindowFlags(const QObject *host) const override; void resetQtGrabbedControl(QObject *host) const override; void setWindowState(QObject *host, const Qt::WindowStates &state) const override; void setCursorShape(QObject *host, const Qt::CursorShape shape) const override; void setWindowState(QObject *host, Qt::WindowStates state) const override; void setCursorShape(QObject *host, Qt::CursorShape shape) const override; void restoreCursorShape(QObject *host) const override; void setWindowFlags(QObject *host, const Qt::WindowFlags &flags) const override; void setWindowFlags(QObject *host, Qt::WindowFlags flags) const override; }; }