WidgetResizeHandler.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. #include "WidgetResizeHandler.h"
  2. #include <QFrame>
  3. #include <QApplication>
  4. #include <QDesktopWidget>
  5. #include <QCursor>
  6. #if QT_CONFIG(sizegrip)
  7. #include <QSizeGrip>
  8. #endif
  9. #include <QEvent>
  10. #include <QDebug>
  11. #include "../titlebar/TitleBar.h"
  12. #define RANGE 8
  13. static bool resizeHorizontalDirectionFixed = false;
  14. static bool resizeVerticalDirectionFixed = false;
  15. WidgetResizeHandler::WidgetResizeHandler(QWidget *parent, QWidget *cw)
  16. : QObject(parent), m_widget(parent), m_childWidget(cw ? cw : parent),
  17. m_fw(0), m_extrahei(0), m_buttonDown(false),
  18. m_moveResizeMode(false), m_sizeprotect(true), m_movingEnabled(true)
  19. {
  20. m_mode = Nowhere;
  21. m_widget->installEventFilter(this);
  22. m_widget->setMouseTracking(true);
  23. QFrame *frame = qobject_cast<QFrame*>(m_widget);
  24. m_range = frame ? frame->frameWidth() : RANGE;
  25. m_range = qMax(RANGE, m_range);
  26. m_activeForMove = m_activeForResize = true;
  27. m_widget->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint);
  28. m_titleBar = new TitleBar(m_widget);
  29. m_widget->resize(500, 500);
  30. m_titleBar->raise();
  31. setTitleBarHeight(m_titleBar->height());
  32. }
  33. void WidgetResizeHandler::setTitleBar(TitleBar * tBar)
  34. {
  35. if (tBar != m_titleBar) {
  36. m_titleBar->deleteLater();
  37. m_titleBar = tBar;
  38. m_titleBar->setParent(m_widget);
  39. m_titleBar->raise();
  40. setTitleBarHeight(m_titleBar->height());
  41. }
  42. }
  43. TitleBar * WidgetResizeHandler::getTitleBar() const
  44. {
  45. return m_titleBar;
  46. }
  47. void WidgetResizeHandler::setResizeEnabled(bool isEnabled)
  48. {
  49. setActive(Resize, isEnabled);
  50. }
  51. bool WidgetResizeHandler::getResizeEnabled() const
  52. {
  53. return m_activeForResize;
  54. }
  55. void WidgetResizeHandler::setActive(Action ac, bool b)
  56. {
  57. if (ac & Move)
  58. m_activeForMove = b;
  59. if (ac & Resize)
  60. m_activeForResize = b;
  61. if (!isActive())
  62. setMouseCursor(Nowhere);
  63. }
  64. bool WidgetResizeHandler::isActive(Action ac) const
  65. {
  66. bool b = false;
  67. if (ac & Move) b = m_activeForMove;
  68. if (ac & Resize) b |= m_activeForResize;
  69. return b;
  70. }
  71. bool WidgetResizeHandler::eventFilter(QObject *o, QEvent *ee)
  72. {
  73. if (o == m_widget)
  74. {
  75. qDebug() << ee;
  76. }
  77. if (!isActive()
  78. || (ee->type() != QEvent::MouseButtonPress
  79. && ee->type() != QEvent::MouseButtonRelease
  80. && ee->type() != QEvent::MouseMove
  81. && ee->type() != QEvent::KeyPress
  82. && ee->type() != QEvent::ShortcutOverride
  83. && ee->type() != QEvent::MouseButtonDblClick
  84. && ee->type() != QEvent::Resize)
  85. )
  86. return false;
  87. Q_ASSERT(o == m_widget);
  88. QWidget *w = m_widget;
  89. if (QApplication::activePopupWidget())
  90. {
  91. if (m_buttonDown && ee->type() == QEvent::MouseButtonRelease)
  92. m_buttonDown = false;
  93. return false;
  94. }
  95. switch (ee->type())
  96. {
  97. case QEvent::MouseButtonPress:
  98. {
  99. QMouseEvent *e = static_cast<QMouseEvent *>(ee);
  100. if (w->isMaximized())
  101. break;
  102. const QRect widgetRect = m_widget->rect().marginsAdded(QMargins(m_range, m_range, m_range, m_range));
  103. const QPoint cursorPoint = m_widget->mapFromGlobal(e->globalPos());
  104. if (!widgetRect.contains(cursorPoint) || m_mode == Nowhere)
  105. return false;
  106. if (e->button() == Qt::LeftButton)
  107. {
  108. #if 0 // Used to be included in Qt4 for Q_WS_X11
  109. /*
  110. Implicit grabs do not stop the X server from changing
  111. the cursor in children, which looks *really* bad when
  112. doing resizingk, so we grab the cursor. Note that we do
  113. not do this on Windows since double clicks are lost due
  114. to the grab (see change 198463).
  115. */
  116. if (e->spontaneous())
  117. # if !defined(QT_NO_CURSOR)
  118. widget->grabMouse(widget->cursor());
  119. # else
  120. widget->grabMouse();
  121. # endif // QT_NO_CURSOR
  122. #endif
  123. m_buttonDown = false;
  124. emit activate();
  125. bool me = m_movingEnabled;
  126. m_movingEnabled = (me && o == m_widget);
  127. mouseMoveEvent(e);
  128. m_movingEnabled = me;
  129. m_buttonDown = true;
  130. m_moveOffset = m_widget->mapFromGlobal(e->globalPos());
  131. m_invertedMoveOffset = m_widget->rect().bottomRight() - m_moveOffset;
  132. if (m_mode == Center)
  133. {
  134. if (m_movingEnabled)
  135. return true;
  136. }
  137. else
  138. {
  139. return true;
  140. }
  141. }
  142. }
  143. break;
  144. case QEvent::MouseButtonRelease:
  145. if (w->isMaximized())
  146. break;
  147. if (w->pos().ry() <= 0)
  148. {
  149. w->move(QPoint(w->pos().rx(), 0));
  150. }
  151. if (static_cast<QMouseEvent *>(ee)->button() == Qt::LeftButton)
  152. {
  153. m_moveResizeMode = false;
  154. m_buttonDown = false;
  155. m_widget->releaseMouse();
  156. m_widget->releaseKeyboard();
  157. if (m_mode == Center)
  158. {
  159. if (m_movingEnabled)
  160. return true;
  161. }
  162. else
  163. {
  164. return true;
  165. }
  166. }
  167. break;
  168. case QEvent::MouseMove:
  169. {
  170. if (w->isMaximized())
  171. {
  172. break;
  173. }
  174. QMouseEvent *e = static_cast<QMouseEvent *>(ee);
  175. m_buttonDown = m_buttonDown && (e->buttons() & Qt::LeftButton); // safety, state machine broken!
  176. bool me = m_movingEnabled;
  177. m_movingEnabled = (me && o == m_widget && (m_buttonDown || m_moveResizeMode));
  178. mouseMoveEvent(e);
  179. m_movingEnabled = me;
  180. if (m_mode == Center)
  181. {
  182. if (m_movingEnabled)
  183. return true;
  184. }
  185. else
  186. {
  187. return true;
  188. }
  189. }
  190. break;
  191. case QEvent::KeyPress:
  192. keyPressEvent(static_cast<QKeyEvent *>(ee));
  193. break;
  194. case QEvent::ShortcutOverride:
  195. if (m_buttonDown)
  196. {
  197. ee->accept();
  198. return true;
  199. }
  200. break;
  201. case QEvent::MouseButtonDblClick:
  202. {
  203. QMouseEvent *e = static_cast<QMouseEvent *>(ee);
  204. if (e && e->pos().y() <= m_titleBarHeight)
  205. {
  206. if (w->isMaximized())
  207. {
  208. w->showNormal();
  209. }
  210. else
  211. {
  212. w->showMaximized();
  213. }
  214. }
  215. break;
  216. }
  217. case QEvent::Resize:
  218. {
  219. m_titleBar->resize(m_widget->width(), m_titleBar->height());
  220. }
  221. default:
  222. break;
  223. }
  224. return false;
  225. }
  226. void WidgetResizeHandler::mouseMoveEvent(QMouseEvent *e)
  227. {
  228. QPoint pos = m_widget->mapFromGlobal(e->globalPos());
  229. if (!m_moveResizeMode && !m_buttonDown)
  230. {
  231. if (pos.y() <= m_range && pos.x() <= m_range)
  232. m_mode = TopLeft;
  233. else if (pos.y() >= m_widget->height() - m_range && pos.x() >= m_widget->width() - m_range)
  234. m_mode = BottomRight;
  235. else if (pos.y() >= m_widget->height() - m_range && pos.x() <= m_range)
  236. m_mode = BottomLeft;
  237. else if (pos.y() <= m_range && pos.x() >= m_widget->width() - m_range)
  238. m_mode = TopRight;
  239. else if (pos.y() <= m_range)
  240. m_mode = Top;
  241. else if (pos.y() >= m_widget->height() - m_range)
  242. m_mode = Bottom;
  243. else if (pos.x() <= m_range)
  244. m_mode = Left;
  245. else if (pos.x() >= m_widget->width() - m_range)
  246. m_mode = Right;
  247. else if (m_widget->rect().contains(pos))
  248. m_mode = Center;
  249. else
  250. m_mode = Nowhere;
  251. if (m_widget->isMinimized() || !isActive(Resize))
  252. m_mode = Center;
  253. #ifndef QT_NO_CURSOR
  254. setMouseCursor(m_mode);
  255. #endif
  256. return;
  257. }
  258. if (m_mode == Center && !m_movingEnabled)
  259. return;
  260. if (m_widget->testAttribute(Qt::WA_WState_ConfigPending))
  261. return;
  262. QPoint globalPos = (!m_widget->isWindow() && m_widget->parentWidget()) ?
  263. m_widget->parentWidget()->mapFromGlobal(e->globalPos()) : e->globalPos();
  264. if (!m_widget->isWindow() && !m_widget->parentWidget()->rect().contains(globalPos))
  265. {
  266. if (globalPos.x() < 0)
  267. globalPos.rx() = 0;
  268. if (globalPos.y() < 0)
  269. globalPos.ry() = 0;
  270. if (m_sizeprotect && globalPos.x() > m_widget->parentWidget()->width())
  271. globalPos.rx() = m_widget->parentWidget()->width();
  272. if (m_sizeprotect && globalPos.y() > m_widget->parentWidget()->height())
  273. globalPos.ry() = m_widget->parentWidget()->height();
  274. }
  275. QPoint p = globalPos + m_invertedMoveOffset;
  276. QPoint pp = globalPos - m_moveOffset;
  277. // Workaround for window managers which refuse to move a tool window partially offscreen.
  278. if (QGuiApplication::platformName() == QLatin1String("xcb"))
  279. {
  280. const QRect desktop = qApp->desktop()->availableGeometry(m_widget);
  281. pp.rx() = qMax(pp.x(), desktop.left());
  282. pp.ry() = qMax(pp.y(), desktop.top());
  283. p.rx() = qMin(p.x(), desktop.right());
  284. p.ry() = qMin(p.y(), desktop.bottom());
  285. }
  286. QSize ms = qSmartMinSize(m_childWidget->sizeHint(), m_childWidget->minimumSizeHint(),
  287. m_childWidget->minimumSize(), m_childWidget->maximumSize(),
  288. m_childWidget->sizePolicy());
  289. int mw = ms.width();
  290. int mh = ms.height();
  291. if (m_childWidget != m_widget)
  292. {
  293. mw += 2 * m_fw;
  294. mh += 2 * m_fw + m_extrahei;
  295. }
  296. QSize maxsize(m_childWidget->maximumSize());
  297. if (m_childWidget != m_widget)
  298. maxsize += QSize(2 * m_fw, 2 * m_fw + m_extrahei);
  299. QSize mpsize(m_widget->geometry().right() - pp.x() + 1,
  300. m_widget->geometry().bottom() - pp.y() + 1);
  301. mpsize = mpsize.expandedTo(m_widget->minimumSize()).expandedTo(QSize(mw, mh))
  302. .boundedTo(maxsize);
  303. QPoint mp(m_widget->geometry().right() - mpsize.width() + 1,
  304. m_widget->geometry().bottom() - mpsize.height() + 1);
  305. QRect geom = m_widget->geometry();
  306. switch (m_mode)
  307. {
  308. case TopLeft:
  309. geom = QRect(mp, m_widget->geometry().bottomRight());
  310. break;
  311. case BottomRight:
  312. geom = QRect(m_widget->geometry().topLeft(), p);
  313. break;
  314. case BottomLeft:
  315. geom = QRect(QPoint(mp.x(), m_widget->geometry().y()), QPoint(m_widget->geometry().right(), p.y()));
  316. break;
  317. case TopRight:
  318. geom = QRect(QPoint(m_widget->geometry().x(), mp.y()), QPoint(p.x(), m_widget->geometry().bottom()));
  319. break;
  320. case Top:
  321. geom = QRect(QPoint(m_widget->geometry().left(), mp.y()), m_widget->geometry().bottomRight());
  322. break;
  323. case Bottom:
  324. geom = QRect(m_widget->geometry().topLeft(), QPoint(m_widget->geometry().right(), p.y()));
  325. break;
  326. case Left:
  327. geom = QRect(QPoint(mp.x(), m_widget->geometry().top()), m_widget->geometry().bottomRight());
  328. break;
  329. case Right:
  330. geom = QRect(m_widget->geometry().topLeft(), QPoint(p.x(), m_widget->geometry().bottom()));
  331. break;
  332. case Center:
  333. geom.moveTopLeft(pp);
  334. break;
  335. default:
  336. break;
  337. }
  338. geom = QRect(geom.topLeft(),
  339. geom.size().expandedTo(m_widget->minimumSize())
  340. .expandedTo(QSize(mw, mh))
  341. .boundedTo(maxsize));
  342. if (geom != m_widget->geometry() &&
  343. (m_widget->isWindow() || m_widget->parentWidget()->rect().intersects(geom)))
  344. {
  345. if (m_mode == Center)
  346. {
  347. m_widget->move(geom.topLeft());
  348. }
  349. else
  350. m_widget->setGeometry(geom);
  351. }
  352. }
  353. void WidgetResizeHandler::setMouseCursor(MousePosition m)
  354. {
  355. #ifdef QT_NO_CURSOR
  356. Q_UNUSED(m);
  357. #else
  358. QObjectList children = m_widget->children();
  359. for (int i = 0; i < children.size(); ++i)
  360. {
  361. if (QWidget *w = qobject_cast<QWidget*>(children.at(i)))
  362. {
  363. if (!w->testAttribute(Qt::WA_SetCursor))
  364. {
  365. w->setCursor(Qt::ArrowCursor);
  366. }
  367. }
  368. }
  369. switch (m)
  370. {
  371. case TopLeft:
  372. case BottomRight:
  373. m_widget->setCursor(Qt::SizeFDiagCursor);
  374. break;
  375. case BottomLeft:
  376. case TopRight:
  377. m_widget->setCursor(Qt::SizeBDiagCursor);
  378. break;
  379. case Top:
  380. case Bottom:
  381. m_widget->setCursor(Qt::SizeVerCursor);
  382. break;
  383. case Left:
  384. case Right:
  385. m_widget->setCursor(Qt::SizeHorCursor);
  386. break;
  387. default:
  388. m_widget->setCursor(Qt::ArrowCursor);
  389. break;
  390. }
  391. #endif // QT_NO_CURSOR
  392. }
  393. void WidgetResizeHandler::keyPressEvent(QKeyEvent * e)
  394. {
  395. if (!isMove() && !isResize())
  396. return;
  397. bool is_control = e->modifiers() & Qt::ControlModifier;
  398. int delta = is_control ? 1 : 8;
  399. QPoint pos = QCursor::pos();
  400. switch (e->key())
  401. {
  402. case Qt::Key_Left:
  403. pos.rx() -= delta;
  404. if (pos.x() <= qApp->desktop()->geometry().left())
  405. {
  406. if (m_mode == TopLeft || m_mode == BottomLeft)
  407. {
  408. m_moveOffset.rx() += delta;
  409. m_invertedMoveOffset.rx() += delta;
  410. }
  411. else
  412. {
  413. m_moveOffset.rx() -= delta;
  414. m_invertedMoveOffset.rx() -= delta;
  415. }
  416. }
  417. if (isResize() && !resizeHorizontalDirectionFixed)
  418. {
  419. resizeHorizontalDirectionFixed = true;
  420. if (m_mode == BottomRight)
  421. m_mode = BottomLeft;
  422. else if (m_mode == TopRight)
  423. m_mode = TopLeft;
  424. #ifndef QT_NO_CURSOR
  425. setMouseCursor(m_mode);
  426. m_widget->grabMouse(m_widget->cursor());
  427. #else
  428. widget->grabMouse();
  429. #endif
  430. }
  431. break;
  432. case Qt::Key_Right:
  433. pos.rx() += delta;
  434. if (pos.x() >= qApp->desktop()->geometry().right())
  435. {
  436. if (m_mode == TopRight || m_mode == BottomRight)
  437. {
  438. m_moveOffset.rx() += delta;
  439. m_invertedMoveOffset.rx() += delta;
  440. }
  441. else
  442. {
  443. m_moveOffset.rx() -= delta;
  444. m_invertedMoveOffset.rx() -= delta;
  445. }
  446. }
  447. if (isResize() && !resizeHorizontalDirectionFixed)
  448. {
  449. resizeHorizontalDirectionFixed = true;
  450. if (m_mode == BottomLeft)
  451. m_mode = BottomRight;
  452. else if (m_mode == TopLeft)
  453. m_mode = TopRight;
  454. #ifndef QT_NO_CURSOR
  455. setMouseCursor(m_mode);
  456. m_widget->grabMouse(m_widget->cursor());
  457. #else
  458. widget->grabMouse();
  459. #endif
  460. }
  461. break;
  462. case Qt::Key_Up:
  463. pos.ry() -= delta;
  464. if (pos.y() <= qApp->desktop()->geometry().top())
  465. {
  466. if (m_mode == TopLeft || m_mode == TopRight)
  467. {
  468. m_moveOffset.ry() += delta;
  469. m_invertedMoveOffset.ry() += delta;
  470. }
  471. else
  472. {
  473. m_moveOffset.ry() -= delta;
  474. m_invertedMoveOffset.ry() -= delta;
  475. }
  476. }
  477. if (isResize() && !resizeVerticalDirectionFixed)
  478. {
  479. resizeVerticalDirectionFixed = true;
  480. if (m_mode == BottomLeft)
  481. m_mode = TopLeft;
  482. else if (m_mode == BottomRight)
  483. m_mode = TopRight;
  484. #ifndef QT_NO_CURSOR
  485. setMouseCursor(m_mode);
  486. m_widget->grabMouse(m_widget->cursor());
  487. #else
  488. widget->grabMouse();
  489. #endif
  490. }
  491. break;
  492. case Qt::Key_Down:
  493. pos.ry() += delta;
  494. if (pos.y() >= qApp->desktop()->geometry().bottom())
  495. {
  496. if (m_mode == BottomLeft || m_mode == BottomRight)
  497. {
  498. m_moveOffset.ry() += delta;
  499. m_invertedMoveOffset.ry() += delta;
  500. }
  501. else
  502. {
  503. m_moveOffset.ry() -= delta;
  504. m_invertedMoveOffset.ry() -= delta;
  505. }
  506. }
  507. if (isResize() && !resizeVerticalDirectionFixed)
  508. {
  509. resizeVerticalDirectionFixed = true;
  510. if (m_mode == TopLeft)
  511. m_mode = BottomLeft;
  512. else if (m_mode == TopRight)
  513. m_mode = BottomRight;
  514. #ifndef QT_NO_CURSOR
  515. setMouseCursor(m_mode);
  516. m_widget->grabMouse(m_widget->cursor());
  517. #else
  518. widget->grabMouse();
  519. #endif
  520. }
  521. break;
  522. case Qt::Key_Space:
  523. case Qt::Key_Return:
  524. case Qt::Key_Enter:
  525. case Qt::Key_Escape:
  526. m_moveResizeMode = false;
  527. m_widget->releaseMouse();
  528. m_widget->releaseKeyboard();
  529. m_buttonDown = false;
  530. break;
  531. default:
  532. return;
  533. }
  534. QCursor::setPos(pos);
  535. }
  536. QSize WidgetResizeHandler::qSmartMinSize(const QSize &sizeHint, const QSize &minSizeHint, const QSize &minSize, const QSize &maxSize, const QSizePolicy &sizePolicy)
  537. {
  538. QSize s(0, 0);
  539. if (sizePolicy.horizontalPolicy() != QSizePolicy::Ignored)
  540. {
  541. if (sizePolicy.horizontalPolicy() & QSizePolicy::ShrinkFlag)
  542. s.setWidth(minSizeHint.width());
  543. else
  544. s.setWidth(qMax(sizeHint.width(), minSizeHint.width()));
  545. }
  546. if (sizePolicy.verticalPolicy() != QSizePolicy::Ignored)
  547. {
  548. if (sizePolicy.verticalPolicy() & QSizePolicy::ShrinkFlag)
  549. {
  550. s.setHeight(minSizeHint.height());
  551. }
  552. else
  553. {
  554. s.setHeight(qMax(sizeHint.height(), minSizeHint.height()));
  555. }
  556. }
  557. s = s.boundedTo(maxSize);
  558. if (minSize.width() > 0)
  559. s.setWidth(minSize.width());
  560. if (minSize.height() > 0)
  561. s.setHeight(minSize.height());
  562. return s.expandedTo(QSize(0, 0));
  563. }
  564. void WidgetResizeHandler::doResize()
  565. {
  566. if (!m_activeForResize)
  567. return;
  568. m_moveResizeMode = true;
  569. m_moveOffset = m_widget->mapFromGlobal(QCursor::pos());
  570. if (m_moveOffset.x() < m_widget->width() / 2)
  571. {
  572. if (m_moveOffset.y() < m_widget->height() / 2)
  573. m_mode = TopLeft;
  574. else
  575. m_mode = BottomLeft;
  576. }
  577. else
  578. {
  579. if (m_moveOffset.y() < m_widget->height() / 2)
  580. m_mode = TopRight;
  581. else
  582. m_mode = BottomRight;
  583. }
  584. m_invertedMoveOffset = m_widget->rect().bottomRight() - m_moveOffset;
  585. #ifndef QT_NO_CURSOR
  586. setMouseCursor(m_mode);
  587. m_widget->grabMouse(m_widget->cursor());
  588. #else
  589. widget->grabMouse();
  590. #endif
  591. m_widget->grabKeyboard();
  592. resizeHorizontalDirectionFixed = false;
  593. resizeVerticalDirectionFixed = false;
  594. }
  595. void WidgetResizeHandler::doMove()
  596. {
  597. if (!m_activeForMove)
  598. return;
  599. m_mode = Center;
  600. m_moveResizeMode = true;
  601. m_moveOffset = m_widget->mapFromGlobal(QCursor::pos());
  602. m_invertedMoveOffset = m_widget->rect().bottomRight() - m_moveOffset;
  603. #ifndef QT_NO_CURSOR
  604. m_widget->grabMouse(Qt::SizeAllCursor);
  605. #else
  606. widget->grabMouse();
  607. #endif
  608. m_widget->grabKeyboard();
  609. }