WIP: make use of Qt 5.15 startSystemResize()

This commit is contained in:
Gary Wang
2021-01-01 21:58:17 +08:00
parent 3abc16d3ff
commit dd5602b290
13 changed files with 257 additions and 196 deletions

149
app/framelesshandler.cpp Normal file
View File

@ -0,0 +1,149 @@
#include "framelesshandler.h"
#include <QMouseEvent>
#include <QWidget>
#include <QWindow>
#include <QDebug>
#include <QApplication>
FramelessHandler::FramelessHandler(QWidget *parent)
: QObject(parent)
, m_parentWidget(parent)
{
m_parentWidget->setMouseTracking(true);
m_parentWidget->installEventFilter(this);
}
bool FramelessHandler::eventFilter(QObject *o, QEvent *evt)
{
if (evt->type() != QEvent::MouseButtonPress &&
evt->type() != QEvent::MouseButtonRelease &&
evt->type() != QEvent::MouseMove) {
return false;
}
Q_ASSERT(o == m_parentWidget);
if (QApplication::activePopupWidget()) {
return false;
}
switch (evt->type()) {
case QEvent::MouseButtonPress: {
if (m_parentWidget->isMaximized()) break;
QMouseEvent *e = static_cast<QMouseEvent *>(evt);
const QRect widgetRect = m_parentWidget->rect();
const QPoint cursorPoint = m_parentWidget->mapFromGlobal(e->globalPos());
if (!widgetRect.contains(cursorPoint)) return false;
if (e->button() & Qt::LeftButton) {
m_oldMousePos = e->pos();
m_clickedOnWindow = true;
mouseMoveEvent(e);
return true;
}
} break;
case QEvent::MouseButtonRelease: {
if (m_parentWidget->isMaximized()) break;
QMouseEvent *e = static_cast<QMouseEvent *>(evt);
if (e->button() & Qt::LeftButton) {
qDebug() << "released";
m_clickedOnWindow = false;
return true;
}
} break;
case QEvent::MouseMove: {
if (m_parentWidget->isMaximized()) break;
QMouseEvent *e = static_cast<QMouseEvent *>(evt);
mouseMoveEvent(e);
return true;
} break;
default:
break;
}
return false;
}
void FramelessHandler::mouseMoveEvent(QMouseEvent * evt)
{
QPoint pos = m_parentWidget->mapFromGlobal(evt->globalPos());
Qt::Edges mode = Qt::Edges();
if (pos.x() <= m_borderWidth) {
mode.setFlag(Qt::LeftEdge);
} else if (pos.x() >= m_parentWidget->width() - m_borderWidth) {
mode.setFlag(Qt::RightEdge);
}
if (pos.y() <= m_borderWidth) {
mode.setFlag(Qt::TopEdge);
} else if (pos.y() >= m_parentWidget->height() - m_borderWidth) {
mode.setFlag(Qt::BottomEdge);
}
setMouseCursor(mode);
if ((evt->button() & Qt::LeftButton) == Qt::NoButton && !m_clickedOnWindow) {
return;
}
qDebug() << evt->button() << m_clickedOnWindow;
if (mode) {
startSystemResize(mode);
} else {
if (!startSystemMove()) {
m_parentWidget->move(evt->globalPos() - m_oldMousePos);
}
}
return;
}
void FramelessHandler::setMouseCursor(Qt::Edges edge)
{
switch (edge) {
case Qt::TopEdge:
case Qt::BottomEdge:
m_parentWidget->setCursor(Qt::SizeVerCursor);
m_parentWidget->window()->setCursor(Qt::SizeVerCursor);
break;
case Qt::LeftEdge:
case Qt::RightEdge:
m_parentWidget->setCursor(Qt::SizeHorCursor);
m_parentWidget->window()->setCursor(Qt::SizeHorCursor);
break;
case Qt::TopEdge | Qt::LeftEdge:
case Qt::BottomEdge | Qt::RightEdge:
m_parentWidget->setCursor(Qt::SizeFDiagCursor);
m_parentWidget->window()->setCursor(Qt::SizeFDiagCursor);
break;
case Qt::BottomEdge | Qt::LeftEdge:
case Qt::TopEdge | Qt::RightEdge:
m_parentWidget->setCursor(Qt::SizeBDiagCursor);
m_parentWidget->window()->setCursor(Qt::SizeBDiagCursor);
break;
default:
m_parentWidget->setCursor(Qt::ArrowCursor);
m_parentWidget->window()->setCursor(Qt::ArrowCursor);
break;
}
// qDebug() << "setMouseCursor" << edge;
}
bool FramelessHandler::startSystemResize(Qt::Edges edges)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
return m_parentWidget->window()->windowHandle()->startSystemResize(edges);
#endif // QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
return false;
}
bool FramelessHandler::startSystemMove()
{
#if not QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
return m_parentWidget->window()->windowHandle()->startSystemMove();
#endif // QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
return false;
}

36
app/framelesshandler.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef FRAMELESSHANDLER_H
#define FRAMELESSHANDLER_H
#include <QObject>
#include <QPoint>
// This concept takes from Qt's QWidgetResizeHandler, but it's not
// a public Qt API so we cannot relay on it...
QT_BEGIN_NAMESPACE
class QMouseEvent;
QT_END_NAMESPACE
class FramelessHandler : public QObject
{
Q_OBJECT
public:
explicit FramelessHandler(QWidget *parent = nullptr);
protected:
bool eventFilter(QObject *o, QEvent *event) override;
void mouseMoveEvent(QMouseEvent *evt);
void setMouseCursor(Qt::Edges edge);
bool startSystemResize(Qt::Edges edges);
bool startSystemMove();
private:
QWidget * m_parentWidget = nullptr;
QPoint m_oldMousePos;
bool m_clickedOnWindow = false;
static constexpr int m_borderWidth = 8;
};
#endif // FRAMELESSHANDLER_H

View File

@ -1,10 +1,12 @@
#include "framelesswindow.h"
#include "framelesshandler.h"
#include <QVBoxLayout>
FramelessWindow::FramelessWindow(QWidget *parent)
: QWidget(parent)
, m_centralLayout(new QVBoxLayout(this))
, m_framelessHandler(new FramelessHandler(this))
{
this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint);

View File

@ -7,6 +7,7 @@ QT_BEGIN_NAMESPACE
class QVBoxLayout;
QT_END_NAMESPACE
class FramelessHandler;
class FramelessWindow : public QWidget
{
Q_OBJECT
@ -20,6 +21,7 @@ signals:
private:
QVBoxLayout * m_centralLayout = nullptr;
QWidget * m_centralWidget = nullptr; // just a pointer, doesn't take the ownership.
FramelessHandler * m_framelessHandler = nullptr;
};
#endif // FRAMELESSWINDOW_H

View File

@ -315,42 +315,6 @@ void MainWindow::leaveEvent(QEvent *event)
return FramelessWindow::leaveEvent(event);
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton && !isMaximized()) {
m_clickedOnWindow = true;
m_oldMousePos = event->pos();
// qDebug() << m_oldMousePos << m_graphicsView->transform().m11()
// << m_graphicsView->transform().m22() << m_graphicsView->matrix().m12();
event->accept();
}
return FramelessWindow::mousePressEvent(event);
}
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton && m_clickedOnWindow && !isMaximized()) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
if (!window()->windowHandle()->startSystemMove()) {
move(event->globalPos() - m_oldMousePos);
}
#else
move(event->globalPos() - m_oldMousePos);
#endif // QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
event->accept();
}
return FramelessWindow::mouseMoveEvent(event);
}
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
m_clickedOnWindow = false;
return FramelessWindow::mouseReleaseEvent(event);
}
void MainWindow::mouseDoubleClickEvent(QMouseEvent *event)
{
switch (Settings::instance()->doubleClickBehavior()) {
@ -515,93 +479,6 @@ void MainWindow::contextMenuEvent(QContextMenuEvent *event)
return FramelessWindow::contextMenuEvent(event);
}
bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
#ifdef _WIN32
// https://stackoverflow.com/questions/43505580/qt-windows-resizable-frameless-window
// Too lazy to do this now.. just stackoverflow it and did a copy and paste..
Q_UNUSED(eventType)
MSG* msg = static_cast<MSG*>(message);
if (msg->message == WM_NCHITTEST) {
if (isMaximized()) {
return false;
}
*result = 0;
const LONG borderWidth = 8;
RECT winrect;
GetWindowRect(reinterpret_cast<HWND>(winId()), &winrect);
// must be short to correctly work with multiple monitors (negative coordinates)
short x = msg->lParam & 0x0000FFFF;
short y = (msg->lParam & 0xFFFF0000) >> 16;
bool resizeWidth = minimumWidth() != maximumWidth();
bool resizeHeight = minimumHeight() != maximumHeight();
if (resizeWidth) {
//left border
if (x >= winrect.left && x < winrect.left + borderWidth) {
*result = HTLEFT;
}
//right border
if (x < winrect.right && x >= winrect.right - borderWidth) {
*result = HTRIGHT;
}
}
if (resizeHeight) {
//bottom border
if (y < winrect.bottom && y >= winrect.bottom - borderWidth) {
*result = HTBOTTOM;
}
//top border
if (y >= winrect.top && y < winrect.top + borderWidth) {
*result = HTTOP;
}
}
if (resizeWidth && resizeHeight) {
//bottom left corner
if (x >= winrect.left && x < winrect.left + borderWidth &&
y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
*result = HTBOTTOMLEFT;
}
//bottom right corner
if (x < winrect.right && x >= winrect.right - borderWidth &&
y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
*result = HTBOTTOMRIGHT;
}
//top left corner
if (x >= winrect.left && x < winrect.left + borderWidth &&
y >= winrect.top && y < winrect.top + borderWidth)
{
*result = HTTOPLEFT;
}
//top right corner
if (x < winrect.right && x >= winrect.right - borderWidth &&
y >= winrect.top && y < winrect.top + borderWidth)
{
*result = HTTOPRIGHT;
}
}
if (*result != 0)
return true;
QWidget *action = QApplication::widgetAt(QCursor::pos());
if (action == this) {
*result = HTCAPTION;
return true;
}
}
return false;
#else
return FramelessWindow::nativeEvent(eventType, message, result);
#endif // _WIN32
}
QSize MainWindow::sizeHint() const
{
return QSize(710, 530);

View File

@ -41,16 +41,11 @@ protected slots:
void showEvent(QShowEvent *event) override;
void enterEvent(QEvent *event) override;
void leaveEvent(QEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mouseDoubleClickEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void contextMenuEvent(QContextMenuEvent *event) override;
bool nativeEvent(const QByteArray& eventType, void* message, long* result) override;
QSize sizeHint() const override;
void centerWindow();
@ -64,7 +59,6 @@ protected slots:
void toggleMaximize();
private:
QPoint m_oldMousePos;
QPropertyAnimation *m_fadeOutAnimation;
QPropertyAnimation *m_floatUpAnimation;
QParallelAnimationGroup *m_exitAnimationGroup;
@ -75,7 +69,6 @@ private:
NavigatorView *m_gv;
BottomButtonGroup *m_bottomButtonGroup;
bool m_protectedMode = false;
bool m_clickedOnWindow = false;
QList<QUrl> m_files;
int m_currentFileIndex = -1;