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()
{
FramelessHelper* helper = new FramelessHelper(windowHandle());
helper->setResizeBorderThickness(8);
helper->setResizeBorderThickness(4);
helper->setTitleBarHeight(40);
helper->setResizable(true);
helper->install();

View File

@ -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<QMouseEvent *>(event);
updateMouse(ev->pos());
break;
}
case QEvent::NonClientAreaMouseButtonPress:
case QEvent::MouseButtonPress:
{
auto ev = static_cast<QMouseEvent *>(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<QMouseEvent *>(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<QMouseEvent *>(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;
}

View File

@ -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

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 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
}

View File

@ -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