一、framelesshelper.h

  1. #ifndef FRAMELESSHELPER_H
  2. #define FRAMELESSHELPER_H
  3. #include <QObject>
  4. #include <QApplication>
  5. #include <QHoverEvent>
  6. #include <QMouseEvent>
  7. #include <QRect>
  8. #include <QRubberBand>
  9. #include <QScopedPointer>
  10. #include <QScreen>
  11. class QWidget;
  12. class FramelessHelperPrivate;
  13. class FramelessHelper : public QObject {
  14. Q_OBJECT
  15. public:
  16. explicit FramelessHelper(QObject* parent = nullptr);
  17. ~FramelessHelper();
  18. // 激活窗体
  19. void activateOn(QWidget* topLevelWidget);
  20. // 移除窗体
  21. void removeFrom(QWidget* topLevelWidget);
  22. // 设置窗体移动
  23. void setWidgetMovable(bool movable);
  24. // 设置窗体缩放
  25. void setWidgetResizable(bool resizable);
  26. // 设置橡皮筋移动
  27. void setRubberBandOnMove(bool movable);
  28. // 设置橡皮筋缩放
  29. void setRubberBandOnResize(bool resizable);
  30. // 设置边框的宽度
  31. void setBorderWidth(int width);
  32. // 设置标题栏高度
  33. void setTitleHeight(int height);
  34. bool widgetResizable();
  35. bool widgetMovable();
  36. bool rubberBandOnMove();
  37. bool rubberBandOnResisze();
  38. int borderWidth();
  39. int titleHeight();
  40. protected:
  41. // 事件过滤,进行移动、缩放等
  42. virtual bool eventFilter(QObject* obj, QEvent* event);
  43. private:
  44. FramelessHelperPrivate* d;
  45. };
  46. #endif // FRAMELESS_HELPER_H

二、framelesshelper.cpp

  1. #include "framelesshelper.h"
  2. class WidgetData;
  3. /*****
  4. * FramelessHelperPrivate
  5. * 存储界面对应的数据集合,以及是否可移动、可缩放属性
  6. *****/
  7. class FramelessHelperPrivate {
  8. public:
  9. QHash<QWidget*, WidgetData*> m_widgetDataHash;
  10. bool m_bWidgetMovable : true;
  11. bool m_bWidgetResizable : true;
  12. bool m_bRubberBandOnResize : true;
  13. bool m_bRubberBandOnMove : true;
  14. };
  15. /*****
  16. * CursorPosCalculator
  17. * 计算鼠标是否位于左、上、右、下、左上角、左下角、右上角、右下角
  18. *****/
  19. class CursorPosCalculator {
  20. public:
  21. explicit CursorPosCalculator();
  22. void reset();
  23. void recalculate(const QPoint& globalMousePos, const QRect& frameRect);
  24. public:
  25. bool m_bOnEdges : true;
  26. bool m_bOnLeftEdge : true;
  27. bool m_bOnRightEdge : true;
  28. bool m_bOnTopEdge : true;
  29. bool m_bOnBottomEdge : true;
  30. bool m_bOnTopLeftEdge : true;
  31. bool m_bOnBottomLeftEdge : true;
  32. bool m_bOnTopRightEdge : true;
  33. bool m_bOnBottomRightEdge : true;
  34. static int m_nBorderWidth;
  35. static int m_nTitleHeight;
  36. };
  37. int CursorPosCalculator::m_nBorderWidth = 5;
  38. int CursorPosCalculator::m_nTitleHeight = 30;
  39. /***** CursorPosCalculator *****/
  40. CursorPosCalculator::CursorPosCalculator()
  41. {
  42. reset();
  43. }
  44. void CursorPosCalculator::reset()
  45. {
  46. m_bOnEdges = false;
  47. m_bOnLeftEdge = false;
  48. m_bOnRightEdge = false;
  49. m_bOnTopEdge = false;
  50. m_bOnBottomEdge = false;
  51. m_bOnTopLeftEdge = false;
  52. m_bOnBottomLeftEdge = false;
  53. m_bOnTopRightEdge = false;
  54. m_bOnBottomRightEdge = false;
  55. }
  56. void CursorPosCalculator::recalculate(const QPoint& gMousePos, const QRect& frameRect)
  57. {
  58. int globalMouseX = gMousePos.x();
  59. int globalMouseY = gMousePos.y();
  60. int frameX = frameRect.x();
  61. int frameY = frameRect.y();
  62. int frameWidth = frameRect.width();
  63. int frameHeight = frameRect.height();
  64. m_bOnLeftEdge = (globalMouseX >= frameX && globalMouseX <= frameX + m_nBorderWidth);
  65. m_bOnRightEdge = (globalMouseX >= frameX + frameWidth - m_nBorderWidth && globalMouseX <= frameX + frameWidth);
  66. m_bOnTopEdge = (globalMouseY >= frameY && globalMouseY <= frameY + m_nBorderWidth);
  67. m_bOnBottomEdge = (globalMouseY >= frameY + frameHeight - m_nBorderWidth && globalMouseY <= frameY + frameHeight);
  68. m_bOnTopLeftEdge = m_bOnTopEdge && m_bOnLeftEdge;
  69. m_bOnBottomLeftEdge = m_bOnBottomEdge && m_bOnLeftEdge;
  70. m_bOnTopRightEdge = m_bOnTopEdge && m_bOnRightEdge;
  71. m_bOnBottomRightEdge = m_bOnBottomEdge && m_bOnRightEdge;
  72. m_bOnEdges = m_bOnLeftEdge || m_bOnRightEdge || m_bOnTopEdge || m_bOnBottomEdge;
  73. }
  74. /*****
  75. * WidgetData
  76. * 更新鼠标样式、移动窗体、缩放窗体
  77. *****/
  78. class WidgetData {
  79. public:
  80. explicit WidgetData(FramelessHelperPrivate* d, QWidget* pTopLevelWidget);
  81. ~WidgetData();
  82. QWidget* widget();
  83. public:
  84. void handleWidgetEvent(QEvent* event); // 处理鼠标事件-划过、按下、释放、移动
  85. void updateRubberBandStatus(); // 更新橡皮筋状态
  86. private:
  87. void updateCursorShape(const QPoint& gMousePos); // 更新鼠标样式
  88. void resizeWidget(const QPoint& gMousePos); // 重置窗体大小
  89. void moveWidget(const QPoint& gMousePos); // 移动窗体
  90. protected:
  91. void handleMousePressEvent(QMouseEvent* event); // 处理鼠标按下
  92. void handleMouseReleaseEvent(QMouseEvent* event); // 处理鼠标释放
  93. void handleMouseMoveEvent(QMouseEvent* event); // 处理鼠标移动
  94. //void handleLeaveEvent(QEvent *event);// 处理鼠标离开
  95. void handleHoverMoveEvent(QHoverEvent* event); // 处理鼠标进入
  96. void handleMouseButtonDblClickEvent(QMouseEvent* event); // 处理双击左键鼠标
  97. private:
  98. FramelessHelperPrivate* d;
  99. QRubberBand* m_pRubberBand;
  100. QWidget* m_pWidget;
  101. QPoint m_ptDragPos;
  102. CursorPosCalculator m_pressedMousePos;
  103. CursorPosCalculator m_moveMousePos;
  104. bool m_bLeftButtonPressed;
  105. bool m_bCursorShapeChanged;
  106. bool m_bLeftButtonTitlePressed;
  107. Qt::WindowFlags m_windowFlags;
  108. };
  109. /***** WidgetData *****/
  110. WidgetData::WidgetData(FramelessHelperPrivate* _d, QWidget* pTopLevelWidget)
  111. {
  112. d = _d;
  113. m_pWidget = pTopLevelWidget;
  114. m_bLeftButtonPressed = false;
  115. m_bCursorShapeChanged = false;
  116. m_bLeftButtonTitlePressed = false;
  117. m_pRubberBand = nullptr;
  118. m_windowFlags = m_pWidget->windowFlags();
  119. m_pWidget->setMouseTracking(true);
  120. m_pWidget->setAttribute(Qt::WA_Hover, true);
  121. updateRubberBandStatus();
  122. }
  123. WidgetData::~WidgetData()
  124. {
  125. m_pWidget->setMouseTracking(false);
  126. m_pWidget->setWindowFlags(m_windowFlags);
  127. m_pWidget->setAttribute(Qt::WA_Hover, false);
  128. delete m_pRubberBand;
  129. m_pRubberBand = nullptr;
  130. }
  131. QWidget* WidgetData::widget()
  132. {
  133. return m_pWidget;
  134. }
  135. void WidgetData::handleWidgetEvent(QEvent* event)
  136. {
  137. switch (event->type()) {
  138. case QEvent::MouseButtonPress:
  139. handleMousePressEvent(static_cast<QMouseEvent*>(event));
  140. break;
  141. case QEvent::MouseButtonRelease:
  142. handleMouseReleaseEvent(static_cast<QMouseEvent*>(event));
  143. break;
  144. case QEvent::MouseMove:
  145. handleMouseMoveEvent(static_cast<QMouseEvent*>(event));
  146. break;
  147. // case QEvent::Leave:
  148. // handleLeaveEvent(static_cast<QMouseEvent*>(event));
  149. // break;
  150. case QEvent::HoverMove:
  151. handleHoverMoveEvent(static_cast<QHoverEvent*>(event));
  152. break;
  153. case QEvent::MouseButtonDblClick:
  154. handleMouseButtonDblClickEvent(static_cast<QMouseEvent*>(event));
  155. break;
  156. default:
  157. break;
  158. }
  159. }
  160. void WidgetData::updateRubberBandStatus()
  161. {
  162. if (d->m_bRubberBandOnMove || d->m_bRubberBandOnResize) {
  163. if (nullptr == m_pRubberBand)
  164. m_pRubberBand = new QRubberBand(QRubberBand::Rectangle);
  165. } else {
  166. delete m_pRubberBand;
  167. m_pRubberBand = nullptr;
  168. }
  169. }
  170. void WidgetData::updateCursorShape(const QPoint& gMousePos)
  171. {
  172. m_moveMousePos.recalculate(gMousePos, m_pWidget->frameGeometry());
  173. if (m_moveMousePos.m_bOnTopLeftEdge || m_moveMousePos.m_bOnBottomRightEdge) {
  174. m_pWidget->setCursor(Qt::SizeFDiagCursor);
  175. m_bCursorShapeChanged = true;
  176. } else if (m_moveMousePos.m_bOnTopRightEdge || m_moveMousePos.m_bOnBottomLeftEdge) {
  177. m_pWidget->setCursor(Qt::SizeBDiagCursor);
  178. m_bCursorShapeChanged = true;
  179. } else if (m_moveMousePos.m_bOnLeftEdge || m_moveMousePos.m_bOnRightEdge) {
  180. m_pWidget->setCursor(Qt::SizeHorCursor);
  181. m_bCursorShapeChanged = true;
  182. } else if (m_moveMousePos.m_bOnTopEdge || m_moveMousePos.m_bOnBottomEdge) {
  183. m_pWidget->setCursor(Qt::SizeVerCursor);
  184. m_bCursorShapeChanged = true;
  185. } else {
  186. if (m_bCursorShapeChanged) {
  187. m_pWidget->unsetCursor();
  188. m_bCursorShapeChanged = false;
  189. }
  190. }
  191. }
  192. void WidgetData::resizeWidget(const QPoint& gMousePos)
  193. {
  194. QRect origRect;
  195. if (d->m_bRubberBandOnResize)
  196. origRect = m_pRubberBand->frameGeometry();
  197. else
  198. origRect = m_pWidget->frameGeometry();
  199. int left = origRect.left();
  200. int top = origRect.top();
  201. int right = origRect.right();
  202. int bottom = origRect.bottom();
  203. origRect.getCoords(&left, &top, &right, &bottom);
  204. int minWidth = m_pWidget->minimumWidth();
  205. int minHeight = m_pWidget->minimumHeight();
  206. if (m_pressedMousePos.m_bOnTopLeftEdge) {
  207. left = gMousePos.x();
  208. top = gMousePos.y();
  209. } else if (m_pressedMousePos.m_bOnBottomLeftEdge) {
  210. left = gMousePos.x();
  211. bottom = gMousePos.y();
  212. } else if (m_pressedMousePos.m_bOnTopRightEdge) {
  213. right = gMousePos.x();
  214. top = gMousePos.y();
  215. } else if (m_pressedMousePos.m_bOnBottomRightEdge) {
  216. right = gMousePos.x();
  217. bottom = gMousePos.y();
  218. } else if (m_pressedMousePos.m_bOnLeftEdge) {
  219. left = gMousePos.x();
  220. } else if (m_pressedMousePos.m_bOnRightEdge) {
  221. right = gMousePos.x();
  222. } else if (m_pressedMousePos.m_bOnTopEdge) {
  223. top = gMousePos.y();
  224. } else if (m_pressedMousePos.m_bOnBottomEdge) {
  225. bottom = gMousePos.y();
  226. }
  227. QRect newRect(QPoint(left, top), QPoint(right, bottom));
  228. if (newRect.isValid()) {
  229. if (minWidth > newRect.width()) {
  230. if (left != origRect.left())
  231. newRect.setLeft(origRect.left());
  232. else
  233. newRect.setRight(origRect.right());
  234. }
  235. if (minHeight > newRect.height()) {
  236. if (top != origRect.top())
  237. newRect.setTop(origRect.top());
  238. else
  239. newRect.setBottom(origRect.bottom());
  240. }
  241. if (d->m_bRubberBandOnResize) {
  242. m_pRubberBand->setGeometry(newRect);
  243. m_pRubberBand->update();
  244. } else {
  245. m_pWidget->setGeometry(newRect);
  246. m_pWidget->update();
  247. }
  248. }
  249. }
  250. void WidgetData::moveWidget(const QPoint& gMousePos)
  251. {
  252. if (d->m_bRubberBandOnMove)
  253. m_pRubberBand->move(gMousePos - m_ptDragPos);
  254. else
  255. m_pWidget->move(gMousePos - m_ptDragPos);
  256. }
  257. void WidgetData::handleMousePressEvent(QMouseEvent* event)
  258. {
  259. if (event->button() == Qt::LeftButton) {
  260. m_bLeftButtonPressed = true;
  261. if (m_pWidget->isFullScreen() || m_pWidget->isMaximized() || !d->m_bWidgetResizable)
  262. m_bLeftButtonTitlePressed = event->pos().y() <= m_moveMousePos.m_nTitleHeight;
  263. else
  264. m_bLeftButtonTitlePressed = event->pos().y() <= m_moveMousePos.m_nTitleHeight && event->pos().y() > m_moveMousePos.m_nBorderWidth;
  265. QRect frameRect = m_pWidget->frameGeometry();
  266. m_pressedMousePos.recalculate(event->globalPos(), frameRect);
  267. m_ptDragPos = event->globalPos() - frameRect.topLeft();
  268. if (d->m_bRubberBandOnMove && m_bLeftButtonTitlePressed) {
  269. m_pRubberBand->setGeometry(frameRect);
  270. m_pRubberBand->show();
  271. } else if (m_pressedMousePos.m_bOnEdges) {
  272. if (d->m_bRubberBandOnResize) {
  273. m_pRubberBand->setGeometry(frameRect);
  274. m_pRubberBand->show();
  275. }
  276. }
  277. }
  278. }
  279. void WidgetData::handleMouseMoveEvent(QMouseEvent* event)
  280. {
  281. if (m_pWidget->isFullScreen() || m_pWidget->isMaximized()) {
  282. if (m_bCursorShapeChanged) {
  283. m_pWidget->unsetCursor();
  284. }
  285. return;
  286. }
  287. if (m_bLeftButtonPressed) {
  288. if (d->m_bWidgetMovable && m_bLeftButtonTitlePressed) {
  289. moveWidget(event->globalPos());
  290. } else if (d->m_bWidgetResizable && m_pressedMousePos.m_bOnEdges) {
  291. resizeWidget(event->globalPos());
  292. }
  293. }
  294. }
  295. void WidgetData::handleMouseReleaseEvent(QMouseEvent* event)
  296. {
  297. if (event->button() == Qt::LeftButton) {
  298. m_bLeftButtonPressed = false;
  299. m_bLeftButtonTitlePressed = false;
  300. m_pressedMousePos.reset();
  301. if (m_pRubberBand && m_pRubberBand->isVisible()) {
  302. m_pRubberBand->hide();
  303. m_pWidget->setGeometry(m_pRubberBand->geometry());
  304. }
  305. }
  306. }
  307. //void WidgetData::handleLeaveEvent(QEvent *event)
  308. //{
  309. // Q_UNUSED(event)
  310. // if (!m_bLeftButtonPressed)
  311. // {
  312. // //qDebug("handleLeaveEvent");
  313. // m_pWidget->unsetCursor();
  314. // }
  315. //}
  316. void WidgetData::handleHoverMoveEvent(QHoverEvent* event)
  317. {
  318. if (m_pWidget->isFullScreen() || m_pWidget->isMaximized()) {
  319. return;
  320. }
  321. if (d->m_bWidgetResizable) {
  322. updateCursorShape(m_pWidget->mapToGlobal(event->pos()));
  323. }
  324. }
  325. void WidgetData::handleMouseButtonDblClickEvent(QMouseEvent* event)
  326. {
  327. if (event->button() == Qt::LeftButton && event->pos().y() < m_moveMousePos.m_nTitleHeight) {
  328. if (d->m_bWidgetResizable) {
  329. if (m_pWidget->isMaximized())
  330. m_pWidget->showNormal();
  331. else
  332. m_pWidget->showMaximized();
  333. }
  334. }
  335. }
  336. /*****FramelessHelper*****/
  337. FramelessHelper::FramelessHelper(QObject* parent)
  338. : QObject(parent)
  339. , d(new FramelessHelperPrivate)
  340. {
  341. d->m_bWidgetMovable = true;
  342. d->m_bWidgetResizable = true;
  343. d->m_bRubberBandOnResize = false;
  344. d->m_bRubberBandOnMove = false;
  345. }
  346. FramelessHelper::~FramelessHelper()
  347. {
  348. QList<QWidget*> keys = d->m_widgetDataHash.keys();
  349. int size = keys.size();
  350. for (int i = 0; i < size; ++i) {
  351. delete d->m_widgetDataHash.take(keys[i]);
  352. }
  353. delete d;
  354. }
  355. bool FramelessHelper::eventFilter(QObject* obj, QEvent* event)
  356. {
  357. switch (event->type()) {
  358. case QEvent::MouseButtonDblClick:
  359. case QEvent::MouseMove:
  360. case QEvent::HoverMove:
  361. case QEvent::MouseButtonPress:
  362. case QEvent::MouseButtonRelease:
  363. case QEvent::Leave: {
  364. WidgetData* data = d->m_widgetDataHash.value(static_cast<QWidget*>(obj));
  365. if (data) {
  366. data->handleWidgetEvent(event);
  367. return true;
  368. }
  369. } break;
  370. default:
  371. break;
  372. }
  373. return QObject::eventFilter(obj, event);
  374. }
  375. void FramelessHelper::activateOn(QWidget* topLevelWidget)
  376. {
  377. if (!d->m_widgetDataHash.contains(topLevelWidget)) {
  378. WidgetData* data = new WidgetData(d, topLevelWidget);
  379. d->m_widgetDataHash.insert(topLevelWidget, data);
  380. topLevelWidget->installEventFilter(this);
  381. }
  382. }
  383. void FramelessHelper::removeFrom(QWidget* topLevelWidget)
  384. {
  385. WidgetData* data = d->m_widgetDataHash.take(topLevelWidget);
  386. if (data) {
  387. topLevelWidget->removeEventFilter(this);
  388. delete data;
  389. }
  390. }
  391. void FramelessHelper::setRubberBandOnMove(bool movable)
  392. {
  393. d->m_bRubberBandOnMove = movable;
  394. QList<WidgetData*> list = d->m_widgetDataHash.values();
  395. foreach (WidgetData* data, list) {
  396. data->updateRubberBandStatus();
  397. }
  398. }
  399. void FramelessHelper::setWidgetMovable(bool movable)
  400. {
  401. d->m_bWidgetMovable = movable;
  402. }
  403. void FramelessHelper::setWidgetResizable(bool resizable)
  404. {
  405. d->m_bWidgetResizable = resizable;
  406. }
  407. void FramelessHelper::setRubberBandOnResize(bool resizable)
  408. {
  409. d->m_bRubberBandOnResize = resizable;
  410. QList<WidgetData*> list = d->m_widgetDataHash.values();
  411. foreach (WidgetData* data, list) {
  412. data->updateRubberBandStatus();
  413. }
  414. }
  415. void FramelessHelper::setBorderWidth(int width)
  416. {
  417. if (width > 0) {
  418. CursorPosCalculator::m_nBorderWidth = width;
  419. }
  420. }
  421. void FramelessHelper::setTitleHeight(int height)
  422. {
  423. if (height > 0) {
  424. CursorPosCalculator::m_nTitleHeight = height;
  425. }
  426. }
  427. bool FramelessHelper::widgetMovable()
  428. {
  429. return d->m_bWidgetMovable;
  430. }
  431. bool FramelessHelper::widgetResizable()
  432. {
  433. return d->m_bWidgetResizable;
  434. }
  435. bool FramelessHelper::rubberBandOnMove()
  436. {
  437. return d->m_bRubberBandOnMove;
  438. }
  439. bool FramelessHelper::rubberBandOnResisze()
  440. {
  441. return d->m_bRubberBandOnResize;
  442. }
  443. int FramelessHelper::borderWidth()
  444. {
  445. return CursorPosCalculator::m_nBorderWidth;
  446. }
  447. int FramelessHelper::titleHeight()
  448. {
  449. return CursorPosCalculator::m_nTitleHeight;
  450. }

三、使用

  1. // 窗口助手
  2. FramelessHelper* helper=new FramelessHelper(this);
  3. helper->activateOn(this); // 激活当前窗体
  4. helper->setTitleHeight(this->topBar->height()); // 设置窗体的标题栏高度
  5. helper->setWidgetMovable(true); // 设置窗体可移动
  6. helper->setWidgetResizable(true); // 设置窗体可缩放
  7. helper->setRubberBandOnMove(false); // 设置橡皮筋效果-可移动
  8. helper->setRubberBandOnResize(false); // 设置橡皮筋效果-可缩放

四、去除窗口标题栏及背景透明

  1. setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
  2. setAttribute(Qt::WA_TranslucentBackground);

五、参考链接

https://developer.aliyun.com/article/24438