fix move and resize determination

This commit is contained in:
Altair Wei 2021-09-20 14:47:49 +08:00
parent 1aed38e882
commit 96f49ded2f
5 changed files with 149 additions and 18 deletions

View File

@ -21,7 +21,7 @@ FLWindow::~FLWindow()
void FLWindow::initFramelessWindow() void FLWindow::initFramelessWindow()
{ {
FramelessHelper* helper = new FramelessHelper(windowHandle()); FramelessHelper* helper = new FramelessHelper(windowHandle());
helper->setResizeBorderThickness(8); helper->setResizeBorderThickness(4);
helper->setTitleBarHeight(40); helper->setTitleBarHeight(40);
helper->setResizable(true); helper->setResizable(true);
helper->install(); helper->install();

View File

@ -37,6 +37,8 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
FramelessHelper::FramelessHelper(QWindow *window) FramelessHelper::FramelessHelper(QWindow *window)
: QObject(window) : QObject(window)
, m_window(window) , m_window(window)
, m_hoveredFrameSection(Qt::NoSection)
, m_clickedFrameSection(Qt::NoSection)
{ {
Q_ASSERT(window != nullptr && window->isTopLevel()); Q_ASSERT(window != nullptr && window->isTopLevel());
} }
@ -89,6 +91,14 @@ QRect FramelessHelper::titleBarRect()
return QRect(0, 0, windowSize().width(), titleBarHeight()); return QRect(0, 0, windowSize().width(), titleBarHeight());
} }
QRegion FramelessHelper::titleBarRegion()
{
// TODO: consider HitTestVisibleObject
QRegion region(titleBarRect());
return region;
}
QRect FramelessHelper::clientRect() QRect FramelessHelper::clientRect()
{ {
QRect rect(0, 0, windowSize().width(), windowSize().height()); QRect rect(0, 0, windowSize().width(), windowSize().height());
@ -101,6 +111,7 @@ QRect FramelessHelper::clientRect()
QRegion FramelessHelper::nonClientRegion() QRegion FramelessHelper::nonClientRegion()
{ {
// TODO: consider HitTestVisibleObject
QRegion region(QRect(QPoint(0, 0), windowSize())); QRegion region(QRect(QPoint(0, 0), windowSize()));
region -= clientRect(); region -= clientRect();
return region; return region;
@ -108,7 +119,7 @@ QRegion FramelessHelper::nonClientRegion()
bool FramelessHelper::isInTitlebarArea(const QPoint& pos) bool FramelessHelper::isInTitlebarArea(const QPoint& pos)
{ {
return nonClientRegion().contains(pos); return titleBarRegion().contains(pos);
} }
Qt::WindowFrameSection FramelessHelper::mapPosToFrameSection(const QPoint& pos) Qt::WindowFrameSection FramelessHelper::mapPosToFrameSection(const QPoint& pos)
@ -173,6 +184,18 @@ bool FramelessHelper::isHoverResizeHandler()
m_hoveredFrameSection == Qt::BottomRightSection; 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) QCursor FramelessHelper::cursorForFrameSection(Qt::WindowFrameSection frameSection)
{ {
Qt::CursorShape cursor = Qt::ArrowCursor; Qt::CursorShape cursor = Qt::ArrowCursor;
@ -222,11 +245,24 @@ void FramelessHelper::unsetCursor()
void FramelessHelper::updateCursor() 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()) { if (isHoverResizeHandler()) {
setCursor(cursorForFrameSection(m_hoveredFrameSection)); setCursor(cursorForFrameSection(m_hoveredFrameSection));
} else { } else {
unsetCursor(); unsetCursor();
} }
#endif
} }
void FramelessHelper::updateMouse(const QPoint& pos) void FramelessHelper::updateMouse(const QPoint& pos)
@ -279,41 +315,57 @@ bool FramelessHelper::eventFilter(QObject *object, QEvent *event)
{ {
auto ev = static_cast<QMouseEvent *>(event); auto ev = static_cast<QMouseEvent *>(event);
updateMouse(ev->pos()); updateMouse(ev->pos());
break;
}
case QEvent::NonClientAreaMouseButtonPress: if (m_clickedFrameSection == Qt::TitleBarArea
case QEvent::MouseButtonPress: && isInTitlebarArea(ev->pos())) {
{
auto ev = static_cast<QMouseEvent *>(event);
if (isHoverResizeHandler()) {
// Start system resize
startResize(ev, m_hoveredFrameSection);
filterOut = true;
} else if (isInTitlebarArea(ev->pos())) {
// Start system move // Start system move
startMove(ev); startMove(ev);
filterOut = true; 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; break;
} }
case QEvent::Leave:
{
updateMouse(m_window->mapFromGlobal(QCursor::pos()));
break;
}
case QEvent::NonClientAreaMouseButtonPress:
case QEvent::MouseButtonPress:
{
auto ev = static_cast<QMouseEvent *>(event);
m_clickedFrameSection = m_hoveredFrameSection;
if (isHoverResizeHandler()) {
// Start system resize
startResize(ev, m_hoveredFrameSection);
filterOut = true;
}
break;
}
case QEvent::NonClientAreaMouseButtonRelease: case QEvent::NonClientAreaMouseButtonRelease:
case QEvent::MouseButtonRelease: case QEvent::MouseButtonRelease:
{
m_clickedFrameSection = Qt::NoSection;
break; break;
}
case QEvent::NonClientAreaMouseButtonDblClick: case QEvent::NonClientAreaMouseButtonDblClick:
case QEvent::MouseButtonDblClick: case QEvent::MouseButtonDblClick:
{ {
auto ev = static_cast<QMouseEvent *>(event); auto ev = static_cast<QMouseEvent *>(event);
if (ev->button() == Qt::LeftButton) { if (isInTitlebarArea(ev->pos()) && ev->button() == Qt::LeftButton) {
Qt::WindowStates states = m_window->windowState(); Qt::WindowStates states = m_window->windowState();
if (states & Qt::WindowMaximized) if (states & Qt::WindowMaximized)
m_window->showNormal(); m_window->showNormal();
else else
m_window->showMaximized(); m_window->showMaximized();
} }
// TODO: double click resize handler
break; break;
} }

View File

@ -59,6 +59,7 @@ public:
int titleBarHeight() { return m_titleBarHeight; } int titleBarHeight() { return m_titleBarHeight; }
int setTitleBarHeight(int height) { m_titleBarHeight = height; } int setTitleBarHeight(int height) { m_titleBarHeight = height; }
QRect titleBarRect(); QRect titleBarRect();
QRegion titleBarRegion();
int resizeBorderThickness() { return m_resizeBorderThickness; } int resizeBorderThickness() { return m_resizeBorderThickness; }
void setResizeBorderThickness(int thickness) { m_resizeBorderThickness = thickness; } void setResizeBorderThickness(int thickness) { m_resizeBorderThickness = thickness; }
@ -73,6 +74,7 @@ public:
Qt::WindowFrameSection mapPosToFrameSection(const QPoint& pos); Qt::WindowFrameSection mapPosToFrameSection(const QPoint& pos);
bool isHoverResizeHandler(); bool isHoverResizeHandler();
bool isClickResizeHandler();
QCursor cursorForFrameSection(Qt::WindowFrameSection frameSection); QCursor cursorForFrameSection(Qt::WindowFrameSection frameSection);
void setCursor(const QCursor& cursor); void setCursor(const QCursor& cursor);
@ -97,6 +99,7 @@ private:
Qt::WindowFlags m_origWindowFlags; Qt::WindowFlags m_origWindowFlags;
bool m_cursorChanged; bool m_cursorChanged;
Qt::WindowFrameSection m_hoveredFrameSection; Qt::WindowFrameSection m_hoveredFrameSection;
Qt::WindowFrameSection m_clickedFrameSection;
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -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 sendX11MoveResizeEvent(QWindow *w, const QPoint &pos, int section);
FRAMELESSHELPER_API void startX11Moving(QWindow *w, const QPoint &pos); FRAMELESSHELPER_API void startX11Moving(QWindow *w, const QPoint &pos);
FRAMELESSHELPER_API void startX11Resizing(QWindow *w, const QPoint &pos, Qt::WindowFrameSection frameSection); 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 #endif
} }

View File

@ -173,7 +173,7 @@ void Utilities::sendX11ButtonReleaseEvent(QWindow *w, const QPoint &pos)
event.xbutton.time = CurrentTime; event.xbutton.time = CurrentTime;
if (XSendEvent(display, window, True, ButtonReleaseMask, &event) == 0) if (XSendEvent(display, window, True, ButtonReleaseMask, &event) == 0)
qWarning() << "Cant send ButtonRelease event."; qWarning() << "Failed to send ButtonRelease event.";
XFlush(display); XFlush(display);
} }
@ -202,7 +202,7 @@ void Utilities::sendX11MoveResizeEvent(QWindow *w, const QPoint &pos, int sectio
event.xclient.data.l[4] = 0; event.xclient.data.l[4] = 0;
if (XSendEvent(display, rootWindow, if (XSendEvent(display, rootWindow,
False, SubstructureRedirectMask | SubstructureNotifyMask, &event) == 0) False, SubstructureRedirectMask | SubstructureNotifyMask, &event) == 0)
qWarning("Cant send Move or Resize event."); qWarning("Failed to send Move or Resize event.");
XFlush(display); XFlush(display);
} }
@ -249,4 +249,77 @@ void Utilities::startX11Resizing(QWindow *w, const QPoint &pos, Qt::WindowFrameS
sendX11MoveResizeEvent(w, pos, section); 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 FRAMELESSHELPER_END_NAMESPACE