From 96f49ded2f1e7e2094f4a685ba17de068b59f2b0 Mon Sep 17 00:00:00 2001 From: Altair Wei Date: Mon, 20 Sep 2021 14:47:49 +0800 Subject: [PATCH] fix move and resize determination --- examples/minimal/flwindow.cpp | 2 +- framelesshelper.cpp | 82 ++++++++++++++++++++++++++++------- framelesshelper.h | 3 ++ utilities.h | 3 ++ utilities_linux.cpp | 77 +++++++++++++++++++++++++++++++- 5 files changed, 149 insertions(+), 18 deletions(-) diff --git a/examples/minimal/flwindow.cpp b/examples/minimal/flwindow.cpp index 39fbcc9..d361418 100644 --- a/examples/minimal/flwindow.cpp +++ b/examples/minimal/flwindow.cpp @@ -21,7 +21,7 @@ FLWindow::~FLWindow() void FLWindow::initFramelessWindow() { FramelessHelper* helper = new FramelessHelper(windowHandle()); - helper->setResizeBorderThickness(8); + helper->setResizeBorderThickness(4); helper->setTitleBarHeight(40); helper->setResizable(true); helper->install(); diff --git a/framelesshelper.cpp b/framelesshelper.cpp index 92a77a8..c144c20 100644 --- a/framelesshelper.cpp +++ b/framelesshelper.cpp @@ -37,6 +37,8 @@ FRAMELESSHELPER_BEGIN_NAMESPACE FramelessHelper::FramelessHelper(QWindow *window) : QObject(window) , m_window(window) + , m_hoveredFrameSection(Qt::NoSection) + , m_clickedFrameSection(Qt::NoSection) { Q_ASSERT(window != nullptr && window->isTopLevel()); } @@ -89,6 +91,14 @@ QRect FramelessHelper::titleBarRect() return QRect(0, 0, windowSize().width(), titleBarHeight()); } + +QRegion FramelessHelper::titleBarRegion() +{ + // TODO: consider HitTestVisibleObject + QRegion region(titleBarRect()); + return region; +} + QRect FramelessHelper::clientRect() { QRect rect(0, 0, windowSize().width(), windowSize().height()); @@ -101,6 +111,7 @@ QRect FramelessHelper::clientRect() QRegion FramelessHelper::nonClientRegion() { + // TODO: consider HitTestVisibleObject QRegion region(QRect(QPoint(0, 0), windowSize())); region -= clientRect(); return region; @@ -108,7 +119,7 @@ QRegion FramelessHelper::nonClientRegion() bool FramelessHelper::isInTitlebarArea(const QPoint& pos) { - return nonClientRegion().contains(pos); + return titleBarRegion().contains(pos); } Qt::WindowFrameSection FramelessHelper::mapPosToFrameSection(const QPoint& pos) @@ -173,6 +184,18 @@ bool FramelessHelper::isHoverResizeHandler() m_hoveredFrameSection == Qt::BottomRightSection; } +bool FramelessHelper::isClickResizeHandler() +{ + return m_clickedFrameSection == Qt::LeftSection || + m_clickedFrameSection == Qt::RightSection || + m_clickedFrameSection == Qt::TopSection || + m_clickedFrameSection == Qt::BottomSection || + m_clickedFrameSection == Qt::TopLeftSection || + m_clickedFrameSection == Qt::TopRightSection || + m_clickedFrameSection == Qt::BottomLeftSection || + m_clickedFrameSection == Qt::BottomRightSection; +} + QCursor FramelessHelper::cursorForFrameSection(Qt::WindowFrameSection frameSection) { Qt::CursorShape cursor = Qt::ArrowCursor; @@ -222,11 +245,24 @@ void FramelessHelper::unsetCursor() void FramelessHelper::updateCursor() { +#ifdef Q_OS_LINUX + if (isHoverResizeHandler()) { + Utilities::setX11CursorShape(m_window, + Utilities::getX11CursorForFrameSection(m_hoveredFrameSection)); + m_cursorChanged = true; + } else { + if (!m_cursorChanged) + return; + Utilities::resetX1CursorShape(m_window); + m_cursorChanged = false; + } +#else if (isHoverResizeHandler()) { setCursor(cursorForFrameSection(m_hoveredFrameSection)); } else { unsetCursor(); } +#endif } void FramelessHelper::updateMouse(const QPoint& pos) @@ -279,41 +315,57 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event) { auto ev = static_cast(event); updateMouse(ev->pos()); - break; - } - case QEvent::NonClientAreaMouseButtonPress: - case QEvent::MouseButtonPress: - { - auto ev = static_cast(event); - if (isHoverResizeHandler()) { - // Start system resize - startResize(ev, m_hoveredFrameSection); - filterOut = true; - } else if (isInTitlebarArea(ev->pos())) { + if (m_clickedFrameSection == Qt::TitleBarArea + && isInTitlebarArea(ev->pos())) { // Start system move startMove(ev); filterOut = true; } - + + // We don't rely on the MouseMove event to determine the resize operation, + // because when the mouse is moved out of the window, the resize cannot + // be triggered. + break; } - + case QEvent::Leave: + { + updateMouse(m_window->mapFromGlobal(QCursor::pos())); + break; + } + case QEvent::NonClientAreaMouseButtonPress: + case QEvent::MouseButtonPress: + { + auto ev = static_cast(event); + m_clickedFrameSection = m_hoveredFrameSection; + if (isHoverResizeHandler()) { + // Start system resize + startResize(ev, m_hoveredFrameSection); + filterOut = true; + } + break; + } + case QEvent::NonClientAreaMouseButtonRelease: case QEvent::MouseButtonRelease: + { + m_clickedFrameSection = Qt::NoSection; break; + } case QEvent::NonClientAreaMouseButtonDblClick: case QEvent::MouseButtonDblClick: { auto ev = static_cast(event); - if (ev->button() == Qt::LeftButton) { + if (isInTitlebarArea(ev->pos()) && ev->button() == Qt::LeftButton) { Qt::WindowStates states = m_window->windowState(); if (states & Qt::WindowMaximized) m_window->showNormal(); else m_window->showMaximized(); } + // TODO: double click resize handler break; } diff --git a/framelesshelper.h b/framelesshelper.h index f6ef065..104f7a8 100644 --- a/framelesshelper.h +++ b/framelesshelper.h @@ -59,6 +59,7 @@ public: int titleBarHeight() { return m_titleBarHeight; } int setTitleBarHeight(int height) { m_titleBarHeight = height; } QRect titleBarRect(); + QRegion titleBarRegion(); int resizeBorderThickness() { return m_resizeBorderThickness; } void setResizeBorderThickness(int thickness) { m_resizeBorderThickness = thickness; } @@ -73,6 +74,7 @@ public: Qt::WindowFrameSection mapPosToFrameSection(const QPoint& pos); bool isHoverResizeHandler(); + bool isClickResizeHandler(); QCursor cursorForFrameSection(Qt::WindowFrameSection frameSection); void setCursor(const QCursor& cursor); @@ -97,6 +99,7 @@ private: Qt::WindowFlags m_origWindowFlags; bool m_cursorChanged; Qt::WindowFrameSection m_hoveredFrameSection; + Qt::WindowFrameSection m_clickedFrameSection; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/utilities.h b/utilities.h index 5e2786b..e589f5f 100644 --- a/utilities.h +++ b/utilities.h @@ -63,6 +63,9 @@ FRAMELESSHELPER_API void sendX11ButtonReleaseEvent(QWindow *w, const QPoint &pos FRAMELESSHELPER_API void sendX11MoveResizeEvent(QWindow *w, const QPoint &pos, int section); FRAMELESSHELPER_API void startX11Moving(QWindow *w, const QPoint &pos); FRAMELESSHELPER_API void startX11Resizing(QWindow *w, const QPoint &pos, Qt::WindowFrameSection frameSection); +FRAMELESSHELPER_API void setX11CursorShape(QWindow *w, int cursorId); +FRAMELESSHELPER_API void resetX1CursorShape(QWindow *w); +FRAMELESSHELPER_API unsigned int getX11CursorForFrameSection(Qt::WindowFrameSection frameSection); #endif } diff --git a/utilities_linux.cpp b/utilities_linux.cpp index 6394a55..e07640e 100644 --- a/utilities_linux.cpp +++ b/utilities_linux.cpp @@ -173,7 +173,7 @@ void Utilities::sendX11ButtonReleaseEvent(QWindow *w, const QPoint &pos) event.xbutton.time = CurrentTime; if (XSendEvent(display, window, True, ButtonReleaseMask, &event) == 0) - qWarning() << "Cant send ButtonRelease event."; + qWarning() << "Failed to send ButtonRelease event."; XFlush(display); } @@ -202,7 +202,7 @@ void Utilities::sendX11MoveResizeEvent(QWindow *w, const QPoint &pos, int sectio event.xclient.data.l[4] = 0; if (XSendEvent(display, rootWindow, False, SubstructureRedirectMask | SubstructureNotifyMask, &event) == 0) - qWarning("Cant send Move or Resize event."); + qWarning("Failed to send Move or Resize event."); XFlush(display); } @@ -249,4 +249,77 @@ void Utilities::startX11Resizing(QWindow *w, const QPoint &pos, Qt::WindowFrameS sendX11MoveResizeEvent(w, pos, section); } +enum class X11CursorType +{ + kArrow = 2, + kTop = 138, + kTopRight = 136, + kRight = 96, + kBottomRight = 14, + kBottom = 16, + kBottomLeft = 12, + kLeft = 70, + kTopLeft = 134, +}; + +void Utilities::setX11CursorShape(QWindow *w, int cursorId) +{ + const auto display = QX11Info::display(); + const WId window_id = w->winId(); + const Cursor cursor = XCreateFontCursor(display, cursorId); + if (!cursor) { + qWarning() << "Failed to set cursor."; + } + XDefineCursor(display, window_id, cursor); + XFlush(display); +} + +void Utilities::resetX1CursorShape(QWindow *w) +{ + const auto display = QX11Info::display(); + const WId window_id = w->winId(); + XUndefineCursor(display, window_id); + XFlush(display); +} + +unsigned int Utilities::getX11CursorForFrameSection(Qt::WindowFrameSection frameSection) +{ + X11CursorType cursor = X11CursorType::kArrow; + + switch (frameSection) + { + case Qt::LeftSection: + cursor = X11CursorType::kLeft; + break; + case Qt::RightSection: + cursor = X11CursorType::kRight; + break; + case Qt::BottomSection: + cursor = X11CursorType::kBottom; + break; + case Qt::TopSection: + cursor = X11CursorType::kTop; + break; + case Qt::TopLeftSection: + cursor = X11CursorType::kTopLeft; + break; + case Qt::BottomRightSection: + cursor = X11CursorType::kBottomRight; + break; + case Qt::TopRightSection: + cursor = X11CursorType::kTopRight; + break; + case Qt::BottomLeftSection: + cursor = X11CursorType::kBottomLeft; + break; + case Qt::TitleBarArea: + cursor = X11CursorType::kArrow; + break; + default: + break; + } + + return (unsigned int)cursor; +} + FRAMELESSHELPER_END_NAMESPACE \ No newline at end of file