148 lines
4.6 KiB
C++
148 lines
4.6 KiB
C++
// SPDX-FileCopyrightText: 2022 Gary Wang <wzc782970009@gmail.com>
|
|
// SPDX-FileCopyrightText: 2023 Tad Young <yyc12321@outlook.com>
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include "framelesswindow.h"
|
|
|
|
#include <QMouseEvent>
|
|
#include <QHoverEvent>
|
|
#include <QApplication>
|
|
#include <QVBoxLayout>
|
|
#include <QWindow>
|
|
|
|
FramelessWindow::FramelessWindow(QWidget *parent)
|
|
: QWidget(parent)
|
|
, m_centralLayout(new QVBoxLayout(this))
|
|
, m_oldCursorShape(Qt::ArrowCursor)
|
|
, m_oldEdges()
|
|
{
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint);
|
|
#else
|
|
// There is a bug in Qt 5 that will make pressing Meta+Up cause the app
|
|
// fullscreen under Windows, see QTBUG-91226 to learn more.
|
|
// The bug seems no longer exists in Qt 6 (I only tested it under Qt 6.3.0).
|
|
this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint);
|
|
#endif // QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
this->setMouseTracking(true);
|
|
this->setAttribute(Qt::WA_Hover, true);
|
|
this->installEventFilter(this);
|
|
|
|
m_centralLayout->setContentsMargins(QMargins());
|
|
}
|
|
|
|
void FramelessWindow::setCentralWidget(QWidget *widget)
|
|
{
|
|
if (m_centralWidget) {
|
|
m_centralLayout->removeWidget(m_centralWidget);
|
|
m_centralWidget->deleteLater();
|
|
}
|
|
|
|
m_centralLayout->addWidget(widget);
|
|
m_centralWidget = widget;
|
|
}
|
|
|
|
void FramelessWindow::installResizeCapture(QObject* widget)
|
|
{
|
|
widget->installEventFilter(this);
|
|
}
|
|
|
|
bool FramelessWindow::eventFilter(QObject* o, QEvent* e)
|
|
{
|
|
switch (e->type()) {
|
|
case QEvent::HoverMove:
|
|
{
|
|
QWidget* wg = qobject_cast<QWidget*>(o);
|
|
if (wg != nullptr)
|
|
return mouseHover(static_cast<QHoverEvent*>(e), wg);
|
|
|
|
break;
|
|
}
|
|
case QEvent::MouseButtonPress:
|
|
return mousePress(static_cast<QMouseEvent*>(e));
|
|
}
|
|
|
|
return QWidget::eventFilter(o, e);
|
|
}
|
|
|
|
bool FramelessWindow::mouseHover(QHoverEvent* event, QWidget* wg)
|
|
{
|
|
if (!isMaximized() && !isFullScreen()) {
|
|
QWindow* win = window()->windowHandle();
|
|
Qt::Edges edges = this->getEdgesByPos(wg->mapToGlobal(event->oldPos()), win->frameGeometry());
|
|
|
|
// backup & restore cursor shape
|
|
if (edges && !m_oldEdges)
|
|
// entering the edge. backup cursor shape
|
|
m_oldCursorShape = win->cursor().shape();
|
|
if (!edges && m_oldEdges)
|
|
// leaving the edge. restore cursor shape
|
|
win->setCursor(m_oldCursorShape);
|
|
|
|
// save the latest edges status
|
|
m_oldEdges = edges;
|
|
|
|
// show resize cursor shape if cursor is within border
|
|
if (edges) {
|
|
win->setCursor(this->getCursorByEdge(edges, Qt::ArrowCursor));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FramelessWindow::mousePress(QMouseEvent* event)
|
|
{
|
|
if (event->buttons() & Qt::LeftButton && !isMaximized() && !isFullScreen()) {
|
|
QWindow* win = window()->windowHandle();
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
Qt::Edges edges = this->getEdgesByPos(event->globalPosition().toPoint(), win->frameGeometry());
|
|
#else
|
|
Qt::Edges edges = this->getEdgesByPos(event->globalPos(), win->frameGeometry());
|
|
#endif // QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
|
|
if (edges) {
|
|
win->startSystemResize(edges);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Qt::CursorShape FramelessWindow::getCursorByEdge(const Qt::Edges& edges, Qt::CursorShape default_cursor)
|
|
{
|
|
if ((edges == (Qt::TopEdge | Qt::LeftEdge)) || (edges == (Qt::RightEdge | Qt::BottomEdge)))
|
|
return Qt::SizeFDiagCursor;
|
|
else if ((edges == (Qt::TopEdge | Qt::RightEdge)) || (edges == (Qt::LeftEdge | Qt::BottomEdge)))
|
|
return Qt::SizeBDiagCursor;
|
|
else if (edges & (Qt::TopEdge | Qt::BottomEdge))
|
|
return Qt::SizeVerCursor;
|
|
else if (edges & (Qt::LeftEdge | Qt::RightEdge))
|
|
return Qt::SizeHorCursor;
|
|
else
|
|
return default_cursor;
|
|
}
|
|
|
|
Qt::Edges FramelessWindow::getEdgesByPos(const QPoint gpos, const QRect& winrect)
|
|
{
|
|
const int borderWidth = 8;
|
|
Qt::Edges edges;
|
|
|
|
int x = gpos.x() - winrect.x();
|
|
int y = gpos.y() - winrect.y();
|
|
|
|
if (x < borderWidth)
|
|
edges |= Qt::LeftEdge;
|
|
if (x > (winrect.width() - borderWidth))
|
|
edges |= Qt::RightEdge;
|
|
if (y < borderWidth)
|
|
edges |= Qt::TopEdge;
|
|
if (y > (winrect.height() - borderWidth))
|
|
edges |= Qt::BottomEdge;
|
|
|
|
return edges;
|
|
}
|
|
|