| | |
| | | #include "qtwindowcontext_p.h" |
| | | |
| | | #include <QtCore/QDebug> |
| | | |
| | | #include "qwkglobal_p.h" |
| | | #include "systemwindow_p.h" |
| | | |
| | | namespace QWK { |
| | | |
| | | static constexpr const int kDefaultResizeBorderThickness = 8; |
| | | static constexpr const quint8 kDefaultResizeBorderThickness = 8; |
| | | |
| | | static Qt::CursorShape calculateCursorShape(const QWindow *window, const QPoint &pos) { |
| | | #ifdef Q_OS_MACOS |
| | |
| | | #endif |
| | | } |
| | | |
| | | QtWindowContext::QtWindowContext(QObject *host, WindowItemDelegate *delegate) |
| | | : AbstractWindowContext(host, delegate) { |
| | | static inline Qt::Edges calculateWindowEdges(const QWindow *window, const QPoint &pos) { |
| | | #ifdef Q_OS_MACOS |
| | | Q_UNUSED(window); |
| | | Q_UNUSED(pos); |
| | | return {}; |
| | | #else |
| | | Q_ASSERT(window); |
| | | if (!window) { |
| | | return {}; |
| | | } |
| | | if (window->visibility() != QWindow::Windowed) { |
| | | return {}; |
| | | } |
| | | Qt::Edges edges = {}; |
| | | const int x = pos.x(); |
| | | const int y = pos.y(); |
| | | if (x < kDefaultResizeBorderThickness) { |
| | | edges |= Qt::LeftEdge; |
| | | } |
| | | if (x >= (window->width() - kDefaultResizeBorderThickness)) { |
| | | edges |= Qt::RightEdge; |
| | | } |
| | | if (y < kDefaultResizeBorderThickness) { |
| | | edges |= Qt::TopEdge; |
| | | } |
| | | if (y >= (window->height() - kDefaultResizeBorderThickness)) { |
| | | edges |= Qt::BottomEdge; |
| | | } |
| | | return edges; |
| | | #endif |
| | | } |
| | | |
| | | QtWindowContext::~QtWindowContext() { |
| | | class QtWindowEventFilter : public QObject { |
| | | public: |
| | | 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_cursorShapeChanged; |
| | | WindowStatus m_windowStatus; |
| | | }; |
| | | |
| | | QtWindowEventFilter::QtWindowEventFilter(AbstractWindowContext *context, QObject *parent) |
| | | : QObject(parent), m_context(context), m_cursorShapeChanged(false), m_windowStatus(Idle) { |
| | | m_context->window()->installEventFilter(this); |
| | | } |
| | | |
| | | bool QtWindowContext::setup() { |
| | | if (!m_windowHandle) { |
| | | QtWindowEventFilter::~QtWindowEventFilter() = default; |
| | | |
| | | bool QtWindowEventFilter::eventFilter(QObject *obj, QEvent *event) { |
| | | Q_UNUSED(obj) |
| | | auto type = event->type(); |
| | | if (type < QEvent::MouseButtonPress || type > QEvent::MouseMove) { |
| | | return false; |
| | | } |
| | | auto host = m_context->host(); |
| | | auto window = m_context->window(); |
| | | auto delegate = m_context->delegate(); |
| | | auto me = static_cast<const QMouseEvent *>(event); |
| | | bool fixedSize = delegate->isHostSizeFixed(host); |
| | | |
| | | QPoint scenePos = getMouseEventScenePos(me); |
| | | QPoint globalPos = getMouseEventGlobalPos(me); |
| | | |
| | | bool inTitleBar = m_context->isInTitleBarDraggableArea(scenePos); |
| | | switch (type) { |
| | | case QEvent::MouseButtonPress: { |
| | | switch (me->button()) { |
| | | case Qt::LeftButton: { |
| | | if (!fixedSize) { |
| | | Qt::Edges edges = calculateWindowEdges(window, scenePos); |
| | | if (edges != Qt::Edges()) { |
| | | m_windowStatus = Resizing; |
| | | startSystemResize(window, 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; |
| | | } |
| | | break; |
| | | } |
| | | case Qt::RightButton: { |
| | | m_context->showSystemMenu(globalPos); |
| | | break; |
| | | } |
| | | default: |
| | | break; |
| | | } |
| | | m_windowStatus = WaitingRelease; |
| | | break; |
| | | } |
| | | |
| | | case QEvent::MouseButtonRelease: { |
| | | switch (m_windowStatus) { |
| | | case PreparingMove: |
| | | case Moving: |
| | | case Resizing: { |
| | | 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; |
| | | startSystemMove(window); |
| | | 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 (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); |
| | | } else { |
| | | delegate->setWindowState(host, windowState | Qt::WindowMaximized); |
| | | } |
| | | event->accept(); |
| | | return true; |
| | | } |
| | | } |
| | | break; |
| | | } |
| | | |
| | | default: |
| | | break; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | bool QtWindowContext::eventFilter(QObject *obj, QEvent *event) { |
| | | return AbstractWindowContext::eventFilter(obj, event); |
| | | QtWindowContext::QtWindowContext() : AbstractWindowContext() { |
| | | } |
| | | |
| | | } |
| | | QtWindowContext::~QtWindowContext() = default; |
| | | |
| | | QString QtWindowContext::key() const { |
| | | return QStringLiteral("qt"); |
| | | } |
| | | |
| | | void QtWindowContext::virtual_hook(int id, void *data) { |
| | | AbstractWindowContext::virtual_hook(id, data); |
| | | } |
| | | |
| | | void QtWindowContext::winIdChanged(QWindow *oldWindow, bool isDestroyed) { |
| | | Q_UNUSED(isDestroyed) |
| | | |
| | | // If the original window id is valid, remove all resources related |
| | | if (oldWindow) { |
| | | qtWindowEventFilter.reset(); |
| | | } |
| | | |
| | | if (!m_windowHandle) { |
| | | m_delegate->setWindowFlags(m_host, m_delegate->getWindowFlags(m_host) & |
| | | ~Qt::FramelessWindowHint); |
| | | return; |
| | | } |
| | | |
| | | // Allocate new resources |
| | | m_delegate->setWindowFlags(m_host, |
| | | m_delegate->getWindowFlags(m_host) | Qt::FramelessWindowHint); |
| | | qtWindowEventFilter = std::make_unique<QtWindowEventFilter>(this); |
| | | } |
| | | |
| | | } |