ctkSizeGrip.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0.txt
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QApplication>
  16. #include <QDebug>
  17. #include <QEvent>
  18. #include <QLayout>
  19. #include <QMouseEvent>
  20. #include <QPaintEvent>
  21. #include <QStyle>
  22. #include <QStyleOption>
  23. #include <QPainter>
  24. // CTK includes
  25. #include "ctkSizeGrip.h"
  26. //------------------------------------------------------------------------------
  27. class ctkSizeGripPrivate
  28. {
  29. Q_DECLARE_PUBLIC(ctkSizeGrip)
  30. protected:
  31. ctkSizeGrip* const q_ptr;
  32. public:
  33. ctkSizeGripPrivate(ctkSizeGrip& object);
  34. void init();
  35. QWidget* WidgetToResize;
  36. Qt::Orientations Orientations;
  37. bool Resize;
  38. bool IgnoreWidgetMinimumSizeHint;
  39. QSize WidgetSizeHint;
  40. QRect WidgetGeom;
  41. QSize WidgetMinSize;
  42. QSize WidgetMaxSize;
  43. QPoint StartPos;
  44. bool Hover;
  45. bool Pressed;
  46. };
  47. //------------------------------------------------------------------------------
  48. ctkSizeGripPrivate::ctkSizeGripPrivate(ctkSizeGrip& object)
  49. : q_ptr(&object)
  50. , WidgetToResize(0)
  51. , Orientations(Qt::Horizontal | Qt::Vertical)
  52. , Resize(false)
  53. , IgnoreWidgetMinimumSizeHint(true)
  54. , WidgetSizeHint(-1,-1) ///< the default widget sizeHint should be used.
  55. , Hover(false)
  56. , Pressed(false)
  57. {
  58. }
  59. //------------------------------------------------------------------------------
  60. void ctkSizeGripPrivate::init()
  61. {
  62. Q_Q(ctkSizeGrip);
  63. q->setOrientations(Qt::Horizontal | Qt::Vertical);
  64. }
  65. //------------------------------------------------------------------------------
  66. ctkSizeGrip::ctkSizeGrip(QWidget* parent)
  67. : QWidget(parent)
  68. , d_ptr(new ctkSizeGripPrivate(*this))
  69. {
  70. Q_D(ctkSizeGrip);
  71. d->WidgetToResize = parent;
  72. d->init();
  73. }
  74. //------------------------------------------------------------------------------
  75. ctkSizeGrip::ctkSizeGrip(QWidget* target, QWidget* parent)
  76. : QWidget(parent)
  77. , d_ptr(new ctkSizeGripPrivate(*this))
  78. {
  79. Q_D(ctkSizeGrip);
  80. d->WidgetToResize = target;
  81. d->init();
  82. }
  83. //------------------------------------------------------------------------------
  84. ctkSizeGrip::~ctkSizeGrip()
  85. {
  86. }
  87. //------------------------------------------------------------------------------
  88. void ctkSizeGrip::setOrientations(Qt::Orientations newOrientations)
  89. {
  90. Q_D(ctkSizeGrip);
  91. d->Orientations = newOrientations;
  92. QCursor newCursor;
  93. QSizePolicy newSizePolicy;
  94. switch(d->Orientations)
  95. {
  96. case Qt::Horizontal:
  97. newCursor = Qt::SizeHorCursor;
  98. newSizePolicy = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
  99. break;
  100. case Qt::Vertical:
  101. newCursor = Qt::SizeVerCursor;
  102. newSizePolicy = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
  103. break;
  104. default:
  105. newCursor = this->isLeftToRight() ?
  106. Qt::SizeFDiagCursor : Qt::SizeBDiagCursor;
  107. newSizePolicy = QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
  108. break;
  109. };
  110. this->setCursor(newCursor);
  111. this->setSizePolicy(newSizePolicy);
  112. this->updateGeometry(); // sizeHint might change
  113. }
  114. //------------------------------------------------------------------------------
  115. Qt::Orientations ctkSizeGrip::orientations()const
  116. {
  117. Q_D(const ctkSizeGrip);
  118. return d->Orientations;
  119. }
  120. //------------------------------------------------------------------------------
  121. void ctkSizeGrip::setWidgetToResize(QWidget* target)
  122. {
  123. Q_D(ctkSizeGrip);
  124. d->WidgetToResize = target;
  125. d->WidgetSizeHint = QSize();
  126. }
  127. //------------------------------------------------------------------------------
  128. QWidget* ctkSizeGrip::widgetToResize()const
  129. {
  130. Q_D(const ctkSizeGrip);
  131. return d->WidgetToResize;
  132. }
  133. //------------------------------------------------------------------------------
  134. void ctkSizeGrip::setResizeWidget(bool resize)
  135. {
  136. Q_D(ctkSizeGrip);
  137. d->Resize = resize;
  138. this->setWidgetSizeHint(d->WidgetSizeHint);
  139. }
  140. //------------------------------------------------------------------------------
  141. bool ctkSizeGrip::resizeWidget()const
  142. {
  143. Q_D(const ctkSizeGrip);
  144. return d->Resize;
  145. }
  146. //------------------------------------------------------------------------------
  147. void ctkSizeGrip::setWidgetSizeHint(QSize sizeHint)
  148. {
  149. Q_D(ctkSizeGrip);
  150. if (d->Resize && d->WidgetToResize)
  151. {
  152. QSize newSize = d->WidgetToResize->size();
  153. if (sizeHint.width() >= 0)
  154. {
  155. newSize.setWidth(sizeHint.width());
  156. }
  157. if (sizeHint.height() >= 0)
  158. {
  159. newSize.setHeight(sizeHint.height());
  160. }
  161. d->WidgetToResize->resize(newSize);
  162. }
  163. if (d->WidgetSizeHint != sizeHint)
  164. {
  165. d->WidgetSizeHint = sizeHint;
  166. emit widgetSizeHintChanged(d->WidgetSizeHint);
  167. }
  168. }
  169. //------------------------------------------------------------------------------
  170. QSize ctkSizeGrip::widgetSizeHint()const
  171. {
  172. Q_D(const ctkSizeGrip);
  173. return d->WidgetSizeHint;
  174. }
  175. //------------------------------------------------------------------------------
  176. QSize ctkSizeGrip::sizeHint() const
  177. {
  178. Q_D(const ctkSizeGrip);
  179. QSize minSize;
  180. QStyle::ContentsType contents;
  181. switch (d->Orientations)
  182. {
  183. case Qt::Horizontal:
  184. case Qt::Vertical:
  185. {
  186. contents = QStyle::CT_Splitter;
  187. int splitterWidth = this->style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this);
  188. minSize = QSize(splitterWidth, splitterWidth);
  189. break;
  190. }
  191. default:
  192. contents = QStyle::CT_SizeGrip;
  193. minSize = QSize(13, 13);
  194. break;
  195. };
  196. QStyleOption opt(0);
  197. opt.init(this);
  198. return (this->style()->sizeFromContents(contents, &opt, minSize, this).
  199. expandedTo(QApplication::globalStrut()));
  200. }
  201. //------------------------------------------------------------------------------
  202. void ctkSizeGrip::paintEvent(QPaintEvent *event)
  203. {
  204. Q_UNUSED(event);
  205. Q_D(ctkSizeGrip);
  206. QPainter painter(this);
  207. switch (d->Orientations)
  208. {
  209. case Qt::Horizontal:
  210. case Qt::Vertical:
  211. {
  212. QStyleOption opt(0);
  213. opt.rect = rect();
  214. opt.palette = palette();
  215. opt.state = (d->Orientations == Qt::Horizontal) ?
  216. QStyle::State_Horizontal : QStyle::State_None;
  217. if (d->Hover)
  218. {
  219. opt.state |= QStyle::State_MouseOver;
  220. }
  221. if (d->Pressed)
  222. {
  223. opt.state |= QStyle::State_Sunken;
  224. }
  225. if (isEnabled())
  226. {
  227. opt.state |= QStyle::State_Enabled;
  228. }
  229. this->style()->drawControl(QStyle::CE_Splitter, &opt, &painter, this);
  230. break;
  231. }
  232. default:
  233. {
  234. QStyleOptionSizeGrip opt;
  235. opt.init(this);
  236. opt.corner = this->isLeftToRight() ? Qt::BottomRightCorner : Qt::BottomLeftCorner;
  237. style()->drawControl(QStyle::CE_SizeGrip, &opt, &painter, this);
  238. break;
  239. }
  240. };
  241. }
  242. //------------------------------------------------------------------------------
  243. bool ctkSizeGrip::event(QEvent *event)
  244. {
  245. Q_D(ctkSizeGrip);
  246. switch(event->type())
  247. {
  248. case QEvent::HoverEnter:
  249. d->Hover = true;
  250. update();
  251. break;
  252. case QEvent::HoverLeave:
  253. d->Hover = false;
  254. update();
  255. break;
  256. default:
  257. break;
  258. }
  259. return this->Superclass::event(event);
  260. }
  261. //------------------------------------------------------------------------------
  262. void ctkSizeGrip::mousePressEvent(QMouseEvent * e)
  263. {
  264. if (e->button() != Qt::LeftButton)
  265. {
  266. QWidget::mousePressEvent(e);
  267. return;
  268. }
  269. Q_D(ctkSizeGrip);
  270. d->StartPos = e->globalPos();
  271. d->Pressed = true;
  272. d->WidgetGeom = d->WidgetToResize->geometry();
  273. d->WidgetMinSize = d->WidgetToResize->minimumSize();
  274. d->WidgetMaxSize = d->WidgetToResize->maximumSize();
  275. }
  276. //------------------------------------------------------------------------------
  277. void ctkSizeGrip::mouseMoveEvent(QMouseEvent * e)
  278. {
  279. if (e->buttons() != Qt::LeftButton)
  280. {
  281. this->Superclass::mouseMoveEvent(e);
  282. return;
  283. }
  284. Q_D(ctkSizeGrip);
  285. if (!d->Pressed || d->WidgetToResize->testAttribute(Qt::WA_WState_ConfigPending))
  286. {
  287. return;
  288. }
  289. QPoint newPos(e->globalPos());
  290. QSize offset(newPos.x() - d->StartPos.x(), newPos.y() - d->StartPos.y());
  291. QSize widgetSizeHint = d->WidgetGeom.size();
  292. if (d->Orientations & Qt::Vertical)
  293. {
  294. widgetSizeHint.rheight() = d->WidgetGeom.height() + offset.height();
  295. }
  296. if (d->Orientations & Qt::Horizontal)
  297. {
  298. widgetSizeHint.rwidth() = d->WidgetGeom.width() + offset.width() * (this->isLeftToRight() ? 1 : -1);
  299. }
  300. // Make sure we don't allow "unreasonable" sizes.
  301. widgetSizeHint = widgetSizeHint.expandedTo(d->WidgetMinSize).boundedTo(d->WidgetMaxSize);
  302. if (!d->IgnoreWidgetMinimumSizeHint)
  303. {
  304. widgetSizeHint = QLayout::closestAcceptableSize(d->WidgetToResize, widgetSizeHint);
  305. }
  306. else
  307. {
  308. // Here we can't use QLayout::closestAcceptableSize as it internally uses
  309. // the widget minimumSizeHint to expand the size.
  310. // This usually allows only enlarging the widget but prevent shrinking the
  311. // widget.
  312. // Manually assess the closest acceptable size
  313. // Respect the heightForWidth ratio
  314. if (d->WidgetToResize->heightForWidth(widgetSizeHint.width()) != -1)
  315. {
  316. widgetSizeHint.rheight() = d->WidgetToResize->heightForWidth(widgetSizeHint.width());
  317. }
  318. }
  319. widgetSizeHint = widgetSizeHint.expandedTo(QApplication::globalStrut());
  320. this->setWidgetSizeHint(
  321. QSize(d->Orientations & Qt::Horizontal ? widgetSizeHint.width() : -1,
  322. d->Orientations & Qt::Vertical ? widgetSizeHint.height() : -1));
  323. }
  324. //------------------------------------------------------------------------------
  325. void ctkSizeGrip::mouseReleaseEvent(QMouseEvent *mouseEvent)
  326. {
  327. if (mouseEvent->button() != Qt::LeftButton)
  328. {
  329. this->Superclass::mouseReleaseEvent(mouseEvent);
  330. return;
  331. }
  332. Q_D(ctkSizeGrip);
  333. d->Pressed = false;
  334. d->StartPos = QPoint();
  335. }
  336. //------------------------------------------------------------------------------
  337. void ctkSizeGrip::mouseDoubleClickEvent(QMouseEvent *mouseEvent)
  338. {
  339. if (mouseEvent->button() != Qt::LeftButton)
  340. {
  341. this->Superclass::mouseDoubleClickEvent(mouseEvent);
  342. return;
  343. }
  344. this->setWidgetSizeHint(QSize(-1, -1));
  345. }