一、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_OBJECTpublic: 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