一、framelesshelper.h
#ifndef FRAMELESSHELPER_H
#define FRAMELESSHELPER_H
#include <QObject>
#include <QApplication>
#include <QHoverEvent>
#include <QMouseEvent>
#include <QRect>
#include <QRubberBand>
#include <QScopedPointer>
#include <QScreen>
class QWidget;
class FramelessHelperPrivate;
class FramelessHelper : public QObject {
Q_OBJECT
public:
explicit FramelessHelper(QObject* parent = nullptr);
~FramelessHelper();
// 激活窗体
void activateOn(QWidget* topLevelWidget);
// 移除窗体
void removeFrom(QWidget* topLevelWidget);
// 设置窗体移动
void setWidgetMovable(bool movable);
// 设置窗体缩放
void setWidgetResizable(bool resizable);
// 设置橡皮筋移动
void setRubberBandOnMove(bool movable);
// 设置橡皮筋缩放
void setRubberBandOnResize(bool resizable);
// 设置边框的宽度
void setBorderWidth(int width);
// 设置标题栏高度
void setTitleHeight(int height);
bool widgetResizable();
bool widgetMovable();
bool rubberBandOnMove();
bool rubberBandOnResisze();
int borderWidth();
int titleHeight();
protected:
// 事件过滤,进行移动、缩放等
virtual bool eventFilter(QObject* obj, QEvent* event);
private:
FramelessHelperPrivate* d;
};
#endif // FRAMELESS_HELPER_H
二、framelesshelper.cpp
#include "framelesshelper.h"
class WidgetData;
/*****
* FramelessHelperPrivate
* 存储界面对应的数据集合,以及是否可移动、可缩放属性
*****/
class FramelessHelperPrivate {
public:
QHash<QWidget*, WidgetData*> m_widgetDataHash;
bool m_bWidgetMovable : true;
bool m_bWidgetResizable : true;
bool m_bRubberBandOnResize : true;
bool m_bRubberBandOnMove : true;
};
/*****
* CursorPosCalculator
* 计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角
*****/
class CursorPosCalculator {
public:
explicit CursorPosCalculator();
void reset();
void recalculate(const QPoint& globalMousePos, const QRect& frameRect);
public:
bool m_bOnEdges : true;
bool m_bOnLeftEdge : true;
bool m_bOnRightEdge : true;
bool m_bOnTopEdge : true;
bool m_bOnBottomEdge : true;
bool m_bOnTopLeftEdge : true;
bool m_bOnBottomLeftEdge : true;
bool m_bOnTopRightEdge : true;
bool m_bOnBottomRightEdge : true;
static int m_nBorderWidth;
static int m_nTitleHeight;
};
int CursorPosCalculator::m_nBorderWidth = 5;
int CursorPosCalculator::m_nTitleHeight = 30;
/***** CursorPosCalculator *****/
CursorPosCalculator::CursorPosCalculator()
{
reset();
}
void CursorPosCalculator::reset()
{
m_bOnEdges = false;
m_bOnLeftEdge = false;
m_bOnRightEdge = false;
m_bOnTopEdge = false;
m_bOnBottomEdge = false;
m_bOnTopLeftEdge = false;
m_bOnBottomLeftEdge = false;
m_bOnTopRightEdge = false;
m_bOnBottomRightEdge = false;
}
void CursorPosCalculator::recalculate(const QPoint& gMousePos, const QRect& frameRect)
{
int globalMouseX = gMousePos.x();
int globalMouseY = gMousePos.y();
int frameX = frameRect.x();
int frameY = frameRect.y();
int frameWidth = frameRect.width();
int frameHeight = frameRect.height();
m_bOnLeftEdge = (globalMouseX >= frameX && globalMouseX <= frameX + m_nBorderWidth);
m_bOnRightEdge = (globalMouseX >= frameX + frameWidth - m_nBorderWidth && globalMouseX <= frameX + frameWidth);
m_bOnTopEdge = (globalMouseY >= frameY && globalMouseY <= frameY + m_nBorderWidth);
m_bOnBottomEdge = (globalMouseY >= frameY + frameHeight - m_nBorderWidth && globalMouseY <= frameY + frameHeight);
m_bOnTopLeftEdge = m_bOnTopEdge && m_bOnLeftEdge;
m_bOnBottomLeftEdge = m_bOnBottomEdge && m_bOnLeftEdge;
m_bOnTopRightEdge = m_bOnTopEdge && m_bOnRightEdge;
m_bOnBottomRightEdge = m_bOnBottomEdge && m_bOnRightEdge;
m_bOnEdges = m_bOnLeftEdge || m_bOnRightEdge || m_bOnTopEdge || m_bOnBottomEdge;
}
/*****
* WidgetData
* 更新鼠标样式、移动窗体、缩放窗体
*****/
class WidgetData {
public:
explicit WidgetData(FramelessHelperPrivate* d, QWidget* pTopLevelWidget);
~WidgetData();
QWidget* widget();
public:
void handleWidgetEvent(QEvent* event); // 处理鼠标事件-划过、按下、释放、移动
void updateRubberBandStatus(); // 更新橡皮筋状态
private:
void updateCursorShape(const QPoint& gMousePos); // 更新鼠标样式
void resizeWidget(const QPoint& gMousePos); // 重置窗体大小
void moveWidget(const QPoint& gMousePos); // 移动窗体
protected:
void handleMousePressEvent(QMouseEvent* event); // 处理鼠标按下
void handleMouseReleaseEvent(QMouseEvent* event); // 处理鼠标释放
void handleMouseMoveEvent(QMouseEvent* event); // 处理鼠标移动
//void handleLeaveEvent(QEvent *event);// 处理鼠标离开
void handleHoverMoveEvent(QHoverEvent* event); // 处理鼠标进入
void handleMouseButtonDblClickEvent(QMouseEvent* event); // 处理双击左键鼠标
private:
FramelessHelperPrivate* d;
QRubberBand* m_pRubberBand;
QWidget* m_pWidget;
QPoint m_ptDragPos;
CursorPosCalculator m_pressedMousePos;
CursorPosCalculator m_moveMousePos;
bool m_bLeftButtonPressed;
bool m_bCursorShapeChanged;
bool m_bLeftButtonTitlePressed;
Qt::WindowFlags m_windowFlags;
};
/***** WidgetData *****/
WidgetData::WidgetData(FramelessHelperPrivate* _d, QWidget* pTopLevelWidget)
{
d = _d;
m_pWidget = pTopLevelWidget;
m_bLeftButtonPressed = false;
m_bCursorShapeChanged = false;
m_bLeftButtonTitlePressed = false;
m_pRubberBand = nullptr;
m_windowFlags = m_pWidget->windowFlags();
m_pWidget->setMouseTracking(true);
m_pWidget->setAttribute(Qt::WA_Hover, true);
updateRubberBandStatus();
}
WidgetData::~WidgetData()
{
m_pWidget->setMouseTracking(false);
m_pWidget->setWindowFlags(m_windowFlags);
m_pWidget->setAttribute(Qt::WA_Hover, false);
delete m_pRubberBand;
m_pRubberBand = nullptr;
}
QWidget* WidgetData::widget()
{
return m_pWidget;
}
void WidgetData::handleWidgetEvent(QEvent* event)
{
switch (event->type()) {
case QEvent::MouseButtonPress:
handleMousePressEvent(static_cast<QMouseEvent*>(event));
break;
case QEvent::MouseButtonRelease:
handleMouseReleaseEvent(static_cast<QMouseEvent*>(event));
break;
case QEvent::MouseMove:
handleMouseMoveEvent(static_cast<QMouseEvent*>(event));
break;
// case QEvent::Leave:
// handleLeaveEvent(static_cast<QMouseEvent*>(event));
// break;
case QEvent::HoverMove:
handleHoverMoveEvent(static_cast<QHoverEvent*>(event));
break;
case QEvent::MouseButtonDblClick:
handleMouseButtonDblClickEvent(static_cast<QMouseEvent*>(event));
break;
default:
break;
}
}
void WidgetData::updateRubberBandStatus()
{
if (d->m_bRubberBandOnMove || d->m_bRubberBandOnResize) {
if (nullptr == m_pRubberBand)
m_pRubberBand = new QRubberBand(QRubberBand::Rectangle);
} else {
delete m_pRubberBand;
m_pRubberBand = nullptr;
}
}
void WidgetData::updateCursorShape(const QPoint& gMousePos)
{
m_moveMousePos.recalculate(gMousePos, m_pWidget->frameGeometry());
if (m_moveMousePos.m_bOnTopLeftEdge || m_moveMousePos.m_bOnBottomRightEdge) {
m_pWidget->setCursor(Qt::SizeFDiagCursor);
m_bCursorShapeChanged = true;
} else if (m_moveMousePos.m_bOnTopRightEdge || m_moveMousePos.m_bOnBottomLeftEdge) {
m_pWidget->setCursor(Qt::SizeBDiagCursor);
m_bCursorShapeChanged = true;
} else if (m_moveMousePos.m_bOnLeftEdge || m_moveMousePos.m_bOnRightEdge) {
m_pWidget->setCursor(Qt::SizeHorCursor);
m_bCursorShapeChanged = true;
} else if (m_moveMousePos.m_bOnTopEdge || m_moveMousePos.m_bOnBottomEdge) {
m_pWidget->setCursor(Qt::SizeVerCursor);
m_bCursorShapeChanged = true;
} else {
if (m_bCursorShapeChanged) {
m_pWidget->unsetCursor();
m_bCursorShapeChanged = false;
}
}
}
void WidgetData::resizeWidget(const QPoint& gMousePos)
{
QRect origRect;
if (d->m_bRubberBandOnResize)
origRect = m_pRubberBand->frameGeometry();
else
origRect = m_pWidget->frameGeometry();
int left = origRect.left();
int top = origRect.top();
int right = origRect.right();
int bottom = origRect.bottom();
origRect.getCoords(&left, &top, &right, &bottom);
int minWidth = m_pWidget->minimumWidth();
int minHeight = m_pWidget->minimumHeight();
if (m_pressedMousePos.m_bOnTopLeftEdge) {
left = gMousePos.x();
top = gMousePos.y();
} else if (m_pressedMousePos.m_bOnBottomLeftEdge) {
left = gMousePos.x();
bottom = gMousePos.y();
} else if (m_pressedMousePos.m_bOnTopRightEdge) {
right = gMousePos.x();
top = gMousePos.y();
} else if (m_pressedMousePos.m_bOnBottomRightEdge) {
right = gMousePos.x();
bottom = gMousePos.y();
} else if (m_pressedMousePos.m_bOnLeftEdge) {
left = gMousePos.x();
} else if (m_pressedMousePos.m_bOnRightEdge) {
right = gMousePos.x();
} else if (m_pressedMousePos.m_bOnTopEdge) {
top = gMousePos.y();
} else if (m_pressedMousePos.m_bOnBottomEdge) {
bottom = gMousePos.y();
}
QRect newRect(QPoint(left, top), QPoint(right, bottom));
if (newRect.isValid()) {
if (minWidth > newRect.width()) {
if (left != origRect.left())
newRect.setLeft(origRect.left());
else
newRect.setRight(origRect.right());
}
if (minHeight > newRect.height()) {
if (top != origRect.top())
newRect.setTop(origRect.top());
else
newRect.setBottom(origRect.bottom());
}
if (d->m_bRubberBandOnResize) {
m_pRubberBand->setGeometry(newRect);
m_pRubberBand->update();
} else {
m_pWidget->setGeometry(newRect);
m_pWidget->update();
}
}
}
void WidgetData::moveWidget(const QPoint& gMousePos)
{
if (d->m_bRubberBandOnMove)
m_pRubberBand->move(gMousePos - m_ptDragPos);
else
m_pWidget->move(gMousePos - m_ptDragPos);
}
void WidgetData::handleMousePressEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton) {
m_bLeftButtonPressed = true;
if (m_pWidget->isFullScreen() || m_pWidget->isMaximized() || !d->m_bWidgetResizable)
m_bLeftButtonTitlePressed = event->pos().y() <= m_moveMousePos.m_nTitleHeight;
else
m_bLeftButtonTitlePressed = event->pos().y() <= m_moveMousePos.m_nTitleHeight && event->pos().y() > m_moveMousePos.m_nBorderWidth;
QRect frameRect = m_pWidget->frameGeometry();
m_pressedMousePos.recalculate(event->globalPos(), frameRect);
m_ptDragPos = event->globalPos() - frameRect.topLeft();
if (d->m_bRubberBandOnMove && m_bLeftButtonTitlePressed) {
m_pRubberBand->setGeometry(frameRect);
m_pRubberBand->show();
} else if (m_pressedMousePos.m_bOnEdges) {
if (d->m_bRubberBandOnResize) {
m_pRubberBand->setGeometry(frameRect);
m_pRubberBand->show();
}
}
}
}
void WidgetData::handleMouseMoveEvent(QMouseEvent* event)
{
if (m_pWidget->isFullScreen() || m_pWidget->isMaximized()) {
if (m_bCursorShapeChanged) {
m_pWidget->unsetCursor();
}
return;
}
if (m_bLeftButtonPressed) {
if (d->m_bWidgetMovable && m_bLeftButtonTitlePressed) {
moveWidget(event->globalPos());
} else if (d->m_bWidgetResizable && m_pressedMousePos.m_bOnEdges) {
resizeWidget(event->globalPos());
}
}
}
void WidgetData::handleMouseReleaseEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton) {
m_bLeftButtonPressed = false;
m_bLeftButtonTitlePressed = false;
m_pressedMousePos.reset();
if (m_pRubberBand && m_pRubberBand->isVisible()) {
m_pRubberBand->hide();
m_pWidget->setGeometry(m_pRubberBand->geometry());
}
}
}
//void WidgetData::handleLeaveEvent(QEvent *event)
//{
// Q_UNUSED(event)
// if (!m_bLeftButtonPressed)
// {
// //qDebug("handleLeaveEvent");
// m_pWidget->unsetCursor();
// }
//}
void WidgetData::handleHoverMoveEvent(QHoverEvent* event)
{
if (m_pWidget->isFullScreen() || m_pWidget->isMaximized()) {
return;
}
if (d->m_bWidgetResizable) {
updateCursorShape(m_pWidget->mapToGlobal(event->pos()));
}
}
void WidgetData::handleMouseButtonDblClickEvent(QMouseEvent* event)
{
if (event->button() == Qt::LeftButton && event->pos().y() < m_moveMousePos.m_nTitleHeight) {
if (d->m_bWidgetResizable) {
if (m_pWidget->isMaximized())
m_pWidget->showNormal();
else
m_pWidget->showMaximized();
}
}
}
/*****FramelessHelper*****/
FramelessHelper::FramelessHelper(QObject* parent)
: QObject(parent)
, d(new FramelessHelperPrivate)
{
d->m_bWidgetMovable = true;
d->m_bWidgetResizable = true;
d->m_bRubberBandOnResize = false;
d->m_bRubberBandOnMove = false;
}
FramelessHelper::~FramelessHelper()
{
QList<QWidget*> keys = d->m_widgetDataHash.keys();
int size = keys.size();
for (int i = 0; i < size; ++i) {
delete d->m_widgetDataHash.take(keys[i]);
}
delete d;
}
bool FramelessHelper::eventFilter(QObject* obj, QEvent* event)
{
switch (event->type()) {
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
case QEvent::HoverMove:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::Leave: {
WidgetData* data = d->m_widgetDataHash.value(static_cast<QWidget*>(obj));
if (data) {
data->handleWidgetEvent(event);
return true;
}
} break;
default:
break;
}
return QObject::eventFilter(obj, event);
}
void FramelessHelper::activateOn(QWidget* topLevelWidget)
{
if (!d->m_widgetDataHash.contains(topLevelWidget)) {
WidgetData* data = new WidgetData(d, topLevelWidget);
d->m_widgetDataHash.insert(topLevelWidget, data);
topLevelWidget->installEventFilter(this);
}
}
void FramelessHelper::removeFrom(QWidget* topLevelWidget)
{
WidgetData* data = d->m_widgetDataHash.take(topLevelWidget);
if (data) {
topLevelWidget->removeEventFilter(this);
delete data;
}
}
void FramelessHelper::setRubberBandOnMove(bool movable)
{
d->m_bRubberBandOnMove = movable;
QList<WidgetData*> list = d->m_widgetDataHash.values();
foreach (WidgetData* data, list) {
data->updateRubberBandStatus();
}
}
void FramelessHelper::setWidgetMovable(bool movable)
{
d->m_bWidgetMovable = movable;
}
void FramelessHelper::setWidgetResizable(bool resizable)
{
d->m_bWidgetResizable = resizable;
}
void FramelessHelper::setRubberBandOnResize(bool resizable)
{
d->m_bRubberBandOnResize = resizable;
QList<WidgetData*> list = d->m_widgetDataHash.values();
foreach (WidgetData* data, list) {
data->updateRubberBandStatus();
}
}
void FramelessHelper::setBorderWidth(int width)
{
if (width > 0) {
CursorPosCalculator::m_nBorderWidth = width;
}
}
void FramelessHelper::setTitleHeight(int height)
{
if (height > 0) {
CursorPosCalculator::m_nTitleHeight = height;
}
}
bool FramelessHelper::widgetMovable()
{
return d->m_bWidgetMovable;
}
bool FramelessHelper::widgetResizable()
{
return d->m_bWidgetResizable;
}
bool FramelessHelper::rubberBandOnMove()
{
return d->m_bRubberBandOnMove;
}
bool FramelessHelper::rubberBandOnResisze()
{
return d->m_bRubberBandOnResize;
}
int FramelessHelper::borderWidth()
{
return CursorPosCalculator::m_nBorderWidth;
}
int FramelessHelper::titleHeight()
{
return CursorPosCalculator::m_nTitleHeight;
}
三、使用
// 窗口助手
FramelessHelper* helper=new FramelessHelper(this);
helper->activateOn(this); // 激活当前窗体
helper->setTitleHeight(this->topBar->height()); // 设置窗体的标题栏高度
helper->setWidgetMovable(true); // 设置窗体可移动
helper->setWidgetResizable(true); // 设置窗体可缩放
helper->setRubberBandOnMove(false); // 设置橡皮筋效果-可移动
helper->setRubberBandOnResize(false); // 设置橡皮筋效果-可缩放
四、去除窗口标题栏及背景透明
setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground);
五、参考链接
https://developer.aliyun.com/article/24438