PageRenderTime 124ms CodeModel.GetById 21ms RepoModel.GetById 3ms app.codeStats 0ms

/src/qt/qtbase/src/widgets/widgets/qsplitter.cpp

https://gitlab.com/x33n/phantomjs
C++ | 1733 lines | 1019 code | 189 blank | 525 comment | 239 complexity | 01f6c01a37c6179cfe4fc9285f386e70 MD5 | raw file
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
  4. ** Contact: http://www.qt-project.org/legal
  5. **
  6. ** This file is part of the QtWidgets module of the Qt Toolkit.
  7. **
  8. ** $QT_BEGIN_LICENSE:LGPL$
  9. ** Commercial License Usage
  10. ** Licensees holding valid commercial Qt licenses may use this file in
  11. ** accordance with the commercial license agreement provided with the
  12. ** Software or, alternatively, in accordance with the terms contained in
  13. ** a written agreement between you and Digia. For licensing terms and
  14. ** conditions see http://qt.digia.com/licensing. For further information
  15. ** use the contact form at http://qt.digia.com/contact-us.
  16. **
  17. ** GNU Lesser General Public License Usage
  18. ** Alternatively, this file may be used under the terms of the GNU Lesser
  19. ** General Public License version 2.1 as published by the Free Software
  20. ** Foundation and appearing in the file LICENSE.LGPL included in the
  21. ** packaging of this file. Please review the following information to
  22. ** ensure the GNU Lesser General Public License version 2.1 requirements
  23. ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
  24. **
  25. ** In addition, as a special exception, Digia gives you certain additional
  26. ** rights. These rights are described in the Digia Qt LGPL Exception
  27. ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
  28. **
  29. ** GNU General Public License Usage
  30. ** Alternatively, this file may be used under the terms of the GNU
  31. ** General Public License version 3.0 as published by the Free Software
  32. ** Foundation and appearing in the file LICENSE.GPL included in the
  33. ** packaging of this file. Please review the following information to
  34. ** ensure the GNU General Public License version 3.0 requirements will be
  35. ** met: http://www.gnu.org/copyleft/gpl.html.
  36. **
  37. **
  38. ** $QT_END_LICENSE$
  39. **
  40. ****************************************************************************/
  41. #include "qsplitter.h"
  42. #ifndef QT_NO_SPLITTER
  43. #include "qapplication.h"
  44. #include "qcursor.h"
  45. #include "qdrawutil.h"
  46. #include "qevent.h"
  47. #include "qlayout.h"
  48. #include "qlist.h"
  49. #include "qpainter.h"
  50. #include "qrubberband.h"
  51. #include "qstyle.h"
  52. #include "qstyleoption.h"
  53. #include "qtextstream.h"
  54. #include "qvarlengtharray.h"
  55. #include "qvector.h"
  56. #include "private/qlayoutengine_p.h"
  57. #include "private/qsplitter_p.h"
  58. #include "qtimer.h"
  59. #include "qdebug.h"
  60. #include <ctype.h>
  61. QT_BEGIN_NAMESPACE
  62. //#define QSPLITTER_DEBUG
  63. /*!
  64. \class QSplitterHandle
  65. \brief The QSplitterHandle class provides handle functionality for the splitter.
  66. \ingroup organizers
  67. \inmodule QtWidgets
  68. QSplitterHandle is typically what people think about when they think about
  69. a splitter. It is the handle that is used to resize the widgets.
  70. A typical developer using QSplitter will never have to worry about
  71. QSplitterHandle. It is provided for developers who want splitter handles
  72. that provide extra features, such as popup menus.
  73. The typical way one would create splitter handles is to subclass QSplitter and then
  74. reimplement QSplitter::createHandle() to instantiate the custom splitter
  75. handle. For example, a minimum QSplitter subclass might look like this:
  76. \snippet splitterhandle/splitter.h 0
  77. The \l{QSplitter::}{createHandle()} implementation simply constructs a
  78. custom splitter handle, called \c Splitter in this example:
  79. \snippet splitterhandle/splitter.cpp 1
  80. Information about a given handle can be obtained using functions like
  81. orientation() and opaqueResize(), and is retrieved from its parent splitter.
  82. Details like these can be used to give custom handles different appearances
  83. depending on the splitter's orientation.
  84. The complexity of a custom handle subclass depends on the tasks that it
  85. needs to perform. A simple subclass might only provide a paintEvent()
  86. implementation:
  87. \snippet splitterhandle/splitter.cpp 0
  88. In this example, a predefined gradient is set up differently depending on
  89. the orientation of the handle. QSplitterHandle provides a reasonable
  90. size hint for the handle, so the subclass does not need to provide a
  91. reimplementation of sizeHint() unless the handle has special size
  92. requirements.
  93. \sa QSplitter
  94. */
  95. /*!
  96. Creates a QSplitter handle with the given \a orientation and
  97. \a parent.
  98. */
  99. QSplitterHandle::QSplitterHandle(Qt::Orientation orientation, QSplitter *parent)
  100. : QWidget(*new QSplitterHandlePrivate, parent, 0)
  101. {
  102. Q_D(QSplitterHandle);
  103. d->s = parent;
  104. setOrientation(orientation);
  105. }
  106. /*!
  107. Destructor.
  108. */
  109. QSplitterHandle::~QSplitterHandle()
  110. {
  111. }
  112. /*!
  113. Sets the orientation of the splitter handle to \a orientation.
  114. This is usually propagated from the QSplitter.
  115. \sa QSplitter::setOrientation()
  116. */
  117. void QSplitterHandle::setOrientation(Qt::Orientation orientation)
  118. {
  119. Q_D(QSplitterHandle);
  120. d->orient = orientation;
  121. #ifndef QT_NO_CURSOR
  122. setCursor(orientation == Qt::Horizontal ? Qt::SplitHCursor : Qt::SplitVCursor);
  123. #endif
  124. }
  125. /*!
  126. Returns the handle's orientation. This is usually propagated from the QSplitter.
  127. \sa QSplitter::orientation()
  128. */
  129. Qt::Orientation QSplitterHandle::orientation() const
  130. {
  131. Q_D(const QSplitterHandle);
  132. return d->orient;
  133. }
  134. /*!
  135. Returns \c true if widgets are resized dynamically (opaquely), otherwise
  136. returns \c false. This value is controlled by the QSplitter.
  137. \sa QSplitter::opaqueResize()
  138. */
  139. bool QSplitterHandle::opaqueResize() const
  140. {
  141. Q_D(const QSplitterHandle);
  142. return d->s->opaqueResize();
  143. }
  144. /*!
  145. Returns the splitter associated with this splitter handle.
  146. \sa QSplitter::handle()
  147. */
  148. QSplitter *QSplitterHandle::splitter() const
  149. {
  150. return d_func()->s;
  151. }
  152. /*!
  153. Tells the splitter to move this handle to position \a pos, which is
  154. the distance from the left or top edge of the widget.
  155. Note that \a pos is also measured from the left (or top) for
  156. right-to-left languages. This function will map \a pos to the
  157. appropriate position before calling QSplitter::moveSplitter().
  158. \sa QSplitter::moveSplitter(), closestLegalPosition()
  159. */
  160. void QSplitterHandle::moveSplitter(int pos)
  161. {
  162. Q_D(QSplitterHandle);
  163. if (d->s->isRightToLeft() && d->orient == Qt::Horizontal)
  164. pos = d->s->contentsRect().width() - pos;
  165. d->s->moveSplitter(pos, d->s->indexOf(this));
  166. }
  167. /*!
  168. Returns the closest legal position to \a pos of the splitter
  169. handle. The positions are measured from the left or top edge of
  170. the splitter, even for right-to-left languages.
  171. \sa QSplitter::closestLegalPosition(), moveSplitter()
  172. */
  173. int QSplitterHandle::closestLegalPosition(int pos)
  174. {
  175. Q_D(QSplitterHandle);
  176. QSplitter *s = d->s;
  177. if (s->isRightToLeft() && d->orient == Qt::Horizontal) {
  178. int w = s->contentsRect().width();
  179. return w - s->closestLegalPosition(w - pos, s->indexOf(this));
  180. }
  181. return s->closestLegalPosition(pos, s->indexOf(this));
  182. }
  183. /*!
  184. \reimp
  185. */
  186. QSize QSplitterHandle::sizeHint() const
  187. {
  188. Q_D(const QSplitterHandle);
  189. int hw = d->s->handleWidth();
  190. QStyleOption opt(0);
  191. opt.init(d->s);
  192. opt.state = QStyle::State_None;
  193. return parentWidget()->style()->sizeFromContents(QStyle::CT_Splitter, &opt, QSize(hw, hw), d->s)
  194. .expandedTo(QApplication::globalStrut());
  195. }
  196. /*!
  197. \reimp
  198. */
  199. void QSplitterHandle::resizeEvent(QResizeEvent *event)
  200. {
  201. Q_D(const QSplitterHandle);
  202. // When splitters are only 1 or 0 pixel large we increase the
  203. // actual grab area to five pixels
  204. // Note that QSplitter uses contentsRect for layouting
  205. // and ensures that handles are drawn on top of widgets
  206. // We simply use the contents margins for draggin and only
  207. // paint the mask area
  208. bool useTinyMode = (d->s->handleWidth() <= 1);
  209. setAttribute(Qt::WA_MouseNoMask, useTinyMode);
  210. if (useTinyMode) {
  211. if (orientation() == Qt::Horizontal)
  212. setContentsMargins(2, 0, 2, 0);
  213. else
  214. setContentsMargins(0, 2, 0, 2);
  215. setMask(QRegion(contentsRect()));
  216. }
  217. QWidget::resizeEvent(event);
  218. }
  219. /*!
  220. \reimp
  221. */
  222. bool QSplitterHandle::event(QEvent *event)
  223. {
  224. Q_D(QSplitterHandle);
  225. switch(event->type()) {
  226. case QEvent::HoverEnter:
  227. d->hover = true;
  228. update();
  229. break;
  230. case QEvent::HoverLeave:
  231. d->hover = false;
  232. update();
  233. break;
  234. default:
  235. break;
  236. }
  237. return QWidget::event(event);
  238. }
  239. /*!
  240. \reimp
  241. */
  242. void QSplitterHandle::mouseMoveEvent(QMouseEvent *e)
  243. {
  244. Q_D(QSplitterHandle);
  245. if (!(e->buttons() & Qt::LeftButton))
  246. return;
  247. int pos = d->pick(parentWidget()->mapFromGlobal(e->globalPos()))
  248. - d->mouseOffset;
  249. if (opaqueResize()) {
  250. moveSplitter(pos);
  251. } else {
  252. d->s->setRubberBand(closestLegalPosition(pos));
  253. }
  254. }
  255. /*!
  256. \reimp
  257. */
  258. void QSplitterHandle::mousePressEvent(QMouseEvent *e)
  259. {
  260. Q_D(QSplitterHandle);
  261. if (e->button() == Qt::LeftButton) {
  262. d->mouseOffset = d->pick(e->pos());
  263. d->pressed = true;
  264. update();
  265. }
  266. }
  267. /*!
  268. \reimp
  269. */
  270. void QSplitterHandle::mouseReleaseEvent(QMouseEvent *e)
  271. {
  272. Q_D(QSplitterHandle);
  273. if (!opaqueResize() && e->button() == Qt::LeftButton) {
  274. int pos = d->pick(parentWidget()->mapFromGlobal(e->globalPos()))
  275. - d->mouseOffset;
  276. d->s->setRubberBand(-1);
  277. moveSplitter(pos);
  278. }
  279. if (e->button() == Qt::LeftButton) {
  280. d->pressed = false;
  281. update();
  282. }
  283. }
  284. /*!
  285. \reimp
  286. */
  287. void QSplitterHandle::paintEvent(QPaintEvent *)
  288. {
  289. Q_D(QSplitterHandle);
  290. QPainter p(this);
  291. QStyleOption opt(0);
  292. opt.rect = contentsRect();
  293. opt.palette = palette();
  294. if (orientation() == Qt::Horizontal)
  295. opt.state = QStyle::State_Horizontal;
  296. else
  297. opt.state = QStyle::State_None;
  298. if (d->hover)
  299. opt.state |= QStyle::State_MouseOver;
  300. if (d->pressed)
  301. opt.state |= QStyle::State_Sunken;
  302. if (isEnabled())
  303. opt.state |= QStyle::State_Enabled;
  304. parentWidget()->style()->drawControl(QStyle::CE_Splitter, &opt, &p, d->s);
  305. }
  306. int QSplitterLayoutStruct::getWidgetSize(Qt::Orientation orient)
  307. {
  308. if (sizer == -1) {
  309. QSize s = widget->sizeHint();
  310. const int presizer = pick(s, orient);
  311. const int realsize = pick(widget->size(), orient);
  312. if (!s.isValid() || (widget->testAttribute(Qt::WA_Resized) && (realsize > presizer))) {
  313. sizer = pick(widget->size(), orient);
  314. } else {
  315. sizer = presizer;
  316. }
  317. QSizePolicy p = widget->sizePolicy();
  318. int sf = (orient == Qt::Horizontal) ? p.horizontalStretch() : p.verticalStretch();
  319. if (sf > 1)
  320. sizer *= sf;
  321. }
  322. return sizer;
  323. }
  324. int QSplitterLayoutStruct::getHandleSize(Qt::Orientation orient)
  325. {
  326. return pick(handle->sizeHint(), orient);
  327. }
  328. void QSplitterPrivate::init()
  329. {
  330. Q_Q(QSplitter);
  331. QSizePolicy sp(QSizePolicy::Expanding, QSizePolicy::Preferred);
  332. if (orient == Qt::Vertical)
  333. sp.transpose();
  334. q->setSizePolicy(sp);
  335. q->setAttribute(Qt::WA_WState_OwnSizePolicy, false);
  336. }
  337. void QSplitterPrivate::recalc(bool update)
  338. {
  339. Q_Q(QSplitter);
  340. int n = list.count();
  341. /*
  342. Splitter handles before the first visible widget or right
  343. before a hidden widget must be hidden.
  344. */
  345. bool first = true;
  346. bool allInvisible = n != 0;
  347. for (int i = 0; i < n ; ++i) {
  348. QSplitterLayoutStruct *s = list.at(i);
  349. bool widgetHidden = s->widget->isHidden();
  350. if (allInvisible && !widgetHidden && !s->collapsed)
  351. allInvisible = false;
  352. s->handle->setHidden(first || widgetHidden);
  353. if (!widgetHidden)
  354. first = false;
  355. }
  356. if (allInvisible)
  357. for (int i = 0; i < n ; ++i) {
  358. QSplitterLayoutStruct *s = list.at(i);
  359. if (!s->widget->isHidden()) {
  360. s->collapsed = false;
  361. break;
  362. }
  363. }
  364. int fi = 2 * q->frameWidth();
  365. int maxl = fi;
  366. int minl = fi;
  367. int maxt = QWIDGETSIZE_MAX;
  368. int mint = fi;
  369. /*
  370. calculate min/max sizes for the whole splitter
  371. */
  372. bool empty = true;
  373. for (int j = 0; j < n; j++) {
  374. QSplitterLayoutStruct *s = list.at(j);
  375. if (!s->widget->isHidden()) {
  376. empty = false;
  377. if (!s->handle->isHidden()) {
  378. minl += s->getHandleSize(orient);
  379. maxl += s->getHandleSize(orient);
  380. }
  381. QSize minS = qSmartMinSize(s->widget);
  382. minl += pick(minS);
  383. maxl += pick(s->widget->maximumSize());
  384. mint = qMax(mint, trans(minS));
  385. int tm = trans(s->widget->maximumSize());
  386. if (tm > 0)
  387. maxt = qMin(maxt, tm);
  388. }
  389. }
  390. if (empty) {
  391. if (qobject_cast<QSplitter *>(parent)) {
  392. // nested splitters; be nice
  393. maxl = maxt = 0;
  394. } else {
  395. // QSplitter with no children yet
  396. maxl = QWIDGETSIZE_MAX;
  397. }
  398. } else {
  399. maxl = qMin<int>(maxl, QWIDGETSIZE_MAX);
  400. }
  401. if (maxt < mint)
  402. maxt = mint;
  403. if (update) {
  404. if (orient == Qt::Horizontal) {
  405. q->setMaximumSize(maxl, maxt);
  406. if (q->isWindow())
  407. q->setMinimumSize(minl,mint);
  408. } else {
  409. q->setMaximumSize(maxt, maxl);
  410. if (q->isWindow())
  411. q->setMinimumSize(mint,minl);
  412. }
  413. doResize();
  414. q->updateGeometry();
  415. } else {
  416. firstShow = true;
  417. }
  418. }
  419. void QSplitterPrivate::doResize()
  420. {
  421. Q_Q(QSplitter);
  422. QRect r = q->contentsRect();
  423. int n = list.count();
  424. QVector<QLayoutStruct> a(n*2);
  425. int i;
  426. bool noStretchFactorsSet = true;
  427. for (i = 0; i < n; ++i) {
  428. QSizePolicy p = list.at(i)->widget->sizePolicy();
  429. int sf = orient == Qt::Horizontal ? p.horizontalStretch() : p.verticalStretch();
  430. if (sf != 0) {
  431. noStretchFactorsSet = false;
  432. break;
  433. }
  434. }
  435. int j=0;
  436. for (i = 0; i < n; ++i) {
  437. QSplitterLayoutStruct *s = list.at(i);
  438. #ifdef QSPLITTER_DEBUG
  439. qDebug("widget %d hidden: %d collapsed: %d handle hidden: %d", i, s->widget->isHidden(),
  440. s->collapsed, s->handle->isHidden());
  441. #endif
  442. a[j].init();
  443. if (s->handle->isHidden()) {
  444. a[j].maximumSize = 0;
  445. } else {
  446. a[j].sizeHint = a[j].minimumSize = a[j].maximumSize = s->getHandleSize(orient);
  447. a[j].empty = false;
  448. }
  449. ++j;
  450. a[j].init();
  451. if (s->widget->isHidden() || s->collapsed) {
  452. a[j].maximumSize = 0;
  453. } else {
  454. a[j].minimumSize = pick(qSmartMinSize(s->widget));
  455. a[j].maximumSize = pick(s->widget->maximumSize());
  456. a[j].empty = false;
  457. bool stretch = noStretchFactorsSet;
  458. if (!stretch) {
  459. QSizePolicy p = s->widget->sizePolicy();
  460. int sf = orient == Qt::Horizontal ? p.horizontalStretch() : p.verticalStretch();
  461. stretch = (sf != 0);
  462. }
  463. if (stretch) {
  464. a[j].stretch = s->getWidgetSize(orient);
  465. a[j].sizeHint = a[j].minimumSize;
  466. a[j].expansive = true;
  467. } else {
  468. a[j].sizeHint = qMax(s->getWidgetSize(orient), a[j].minimumSize);
  469. }
  470. }
  471. ++j;
  472. }
  473. qGeomCalc(a, 0, n*2, pick(r.topLeft()), pick(r.size()), 0);
  474. #ifdef QSPLITTER_DEBUG
  475. for (i = 0; i < n*2; ++i) {
  476. qDebug("%*s%d: stretch %d, sh %d, minS %d, maxS %d, exp %d, emp %d -> %d, %d",
  477. i, "", i,
  478. a[i].stretch,
  479. a[i].sizeHint,
  480. a[i].minimumSize,
  481. a[i].maximumSize,
  482. a[i].expansive,
  483. a[i].empty,
  484. a[i].pos,
  485. a[i].size);
  486. }
  487. #endif
  488. for (i = 0; i < n; ++i) {
  489. QSplitterLayoutStruct *s = list.at(i);
  490. setGeo(s, a[i*2+1].pos, a[i*2+1].size, false);
  491. }
  492. }
  493. void QSplitterPrivate::storeSizes()
  494. {
  495. for (int i = 0; i < list.size(); ++i) {
  496. QSplitterLayoutStruct *sls = list.at(i);
  497. sls->sizer = pick(sls->rect.size());
  498. }
  499. }
  500. void QSplitterPrivate::addContribution(int index, int *min, int *max, bool mayCollapse) const
  501. {
  502. QSplitterLayoutStruct *s = list.at(index);
  503. if (!s->widget->isHidden()) {
  504. if (!s->handle->isHidden()) {
  505. *min += s->getHandleSize(orient);
  506. *max += s->getHandleSize(orient);
  507. }
  508. if (mayCollapse || !s->collapsed)
  509. *min += pick(qSmartMinSize(s->widget));
  510. *max += pick(s->widget->maximumSize());
  511. }
  512. }
  513. int QSplitterPrivate::findWidgetJustBeforeOrJustAfter(int index, int delta, int &collapsibleSize) const
  514. {
  515. if (delta < 0)
  516. index += delta;
  517. do {
  518. QWidget *w = list.at(index)->widget;
  519. if (!w->isHidden()) {
  520. if (collapsible(list.at(index)))
  521. collapsibleSize = pick(qSmartMinSize(w));
  522. return index;
  523. }
  524. index += delta;
  525. } while (index >= 0 && index < list.count());
  526. return -1;
  527. }
  528. /*
  529. For the splitter handle with index \a index, \a min and \a max give the range without collapsing any widgets,
  530. and \a farMin and farMax give the range with collapsing included.
  531. */
  532. void QSplitterPrivate::getRange(int index, int *farMin, int *min, int *max, int *farMax) const
  533. {
  534. Q_Q(const QSplitter);
  535. int n = list.count();
  536. if (index <= 0 || index >= n)
  537. return;
  538. int collapsibleSizeBefore = 0;
  539. int idJustBefore = findWidgetJustBeforeOrJustAfter(index, -1, collapsibleSizeBefore);
  540. int collapsibleSizeAfter = 0;
  541. int idJustAfter = findWidgetJustBeforeOrJustAfter(index, +1, collapsibleSizeAfter);
  542. int minBefore = 0;
  543. int minAfter = 0;
  544. int maxBefore = 0;
  545. int maxAfter = 0;
  546. int i;
  547. for (i = 0; i < index; ++i)
  548. addContribution(i, &minBefore, &maxBefore, i == idJustBefore);
  549. for (i = index; i < n; ++i)
  550. addContribution(i, &minAfter, &maxAfter, i == idJustAfter);
  551. QRect r = q->contentsRect();
  552. int farMinVal;
  553. int minVal;
  554. int maxVal;
  555. int farMaxVal;
  556. int smartMinBefore = qMax(minBefore, pick(r.size()) - maxAfter);
  557. int smartMaxBefore = qMin(maxBefore, pick(r.size()) - minAfter);
  558. minVal = pick(r.topLeft()) + smartMinBefore;
  559. maxVal = pick(r.topLeft()) + smartMaxBefore;
  560. farMinVal = minVal;
  561. if (minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter)
  562. farMinVal -= collapsibleSizeBefore;
  563. farMaxVal = maxVal;
  564. if (pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore)
  565. farMaxVal += collapsibleSizeAfter;
  566. if (farMin)
  567. *farMin = farMinVal;
  568. if (min)
  569. *min = minVal;
  570. if (max)
  571. *max = maxVal;
  572. if (farMax)
  573. *farMax = farMaxVal;
  574. }
  575. int QSplitterPrivate::adjustPos(int pos, int index, int *farMin, int *min, int *max, int *farMax) const
  576. {
  577. const int Threshold = 40;
  578. getRange(index, farMin, min, max, farMax);
  579. if (pos >= *min) {
  580. if (pos <= *max) {
  581. return pos;
  582. } else {
  583. int delta = pos - *max;
  584. int width = *farMax - *max;
  585. if (delta > width / 2 && delta >= qMin(Threshold, width)) {
  586. return *farMax;
  587. } else {
  588. return *max;
  589. }
  590. }
  591. } else {
  592. int delta = *min - pos;
  593. int width = *min - *farMin;
  594. if (delta > width / 2 && delta >= qMin(Threshold, width)) {
  595. return *farMin;
  596. } else {
  597. return *min;
  598. }
  599. }
  600. }
  601. bool QSplitterPrivate::collapsible(QSplitterLayoutStruct *s) const
  602. {
  603. if (s->collapsible != Default) {
  604. return (bool)s->collapsible;
  605. } else {
  606. return childrenCollapsible;
  607. }
  608. }
  609. void QSplitterPrivate::updateHandles()
  610. {
  611. Q_Q(QSplitter);
  612. recalc(q->isVisible());
  613. }
  614. void QSplitterPrivate::setSizes_helper(const QList<int> &sizes, bool clampNegativeSize)
  615. {
  616. int j = 0;
  617. for (int i = 0; i < list.size(); ++i) {
  618. QSplitterLayoutStruct *s = list.at(i);
  619. s->collapsed = false;
  620. s->sizer = sizes.value(j++);
  621. if (clampNegativeSize && s->sizer < 0)
  622. s->sizer = 0;
  623. int smartMinSize = pick(qSmartMinSize(s->widget));
  624. // Make sure that we reset the collapsed state.
  625. if (s->sizer == 0) {
  626. if (collapsible(s) && smartMinSize > 0) {
  627. s->collapsed = true;
  628. } else {
  629. s->sizer = smartMinSize;
  630. }
  631. } else {
  632. if (s->sizer < smartMinSize)
  633. s->sizer = smartMinSize;
  634. }
  635. }
  636. doResize();
  637. }
  638. void QSplitterPrivate::setGeo(QSplitterLayoutStruct *sls, int p, int s, bool allowCollapse)
  639. {
  640. Q_Q(QSplitter);
  641. QWidget *w = sls->widget;
  642. QRect r;
  643. QRect contents = q->contentsRect();
  644. if (orient == Qt::Horizontal) {
  645. r.setRect(p, contents.y(), s, contents.height());
  646. } else {
  647. r.setRect(contents.x(), p, contents.width(), s);
  648. }
  649. sls->rect = r;
  650. int minSize = pick(qSmartMinSize(w));
  651. if (orient == Qt::Horizontal && q->isRightToLeft())
  652. r.moveRight(contents.width() - r.left());
  653. if (allowCollapse)
  654. sls->collapsed = s <= 0 && minSize > 0 && !w->isHidden();
  655. // Hide the child widget, but without calling hide() so that
  656. // the splitter handle is still shown.
  657. if (sls->collapsed)
  658. r.moveTopLeft(QPoint(-r.width()-1, -r.height()-1));
  659. w->setGeometry(r);
  660. if (!sls->handle->isHidden()) {
  661. QSplitterHandle *h = sls->handle;
  662. QSize hs = h->sizeHint();
  663. int left, top, right, bottom;
  664. h->getContentsMargins(&left, &top, &right, &bottom);
  665. if (orient==Qt::Horizontal) {
  666. if (q->isRightToLeft())
  667. p = contents.width() - p + hs.width();
  668. h->setGeometry(p-hs.width() - left, contents.y(), hs.width() + left + right, contents.height());
  669. } else {
  670. h->setGeometry(contents.x(), p-hs.height() - top, contents.width(), hs.height() + top + bottom);
  671. }
  672. }
  673. }
  674. void QSplitterPrivate::doMove(bool backwards, int hPos, int index, int delta, bool mayCollapse,
  675. int *positions, int *widths)
  676. {
  677. if (index < 0 || index >= list.count())
  678. return;
  679. #ifdef QSPLITTER_DEBUG
  680. qDebug() << "QSplitterPrivate::doMove" << backwards << hPos << index << delta << mayCollapse;
  681. #endif
  682. QSplitterLayoutStruct *s = list.at(index);
  683. QWidget *w = s->widget;
  684. int nextId = backwards ? index - delta : index + delta;
  685. if (w->isHidden()) {
  686. doMove(backwards, hPos, nextId, delta, collapsible(nextId), positions, widths);
  687. } else {
  688. int hs =s->handle->isHidden() ? 0 : s->getHandleSize(orient);
  689. int ws = backwards ? hPos - pick(s->rect.topLeft())
  690. : pick(s->rect.bottomRight()) - hPos -hs + 1;
  691. if (ws > 0 || (!s->collapsed && !mayCollapse)) {
  692. ws = qMin(ws, pick(w->maximumSize()));
  693. ws = qMax(ws, pick(qSmartMinSize(w)));
  694. } else {
  695. ws = 0;
  696. }
  697. positions[index] = backwards ? hPos - ws : hPos + hs;
  698. widths[index] = ws;
  699. doMove(backwards, backwards ? hPos - ws - hs : hPos + hs + ws, nextId, delta,
  700. collapsible(nextId), positions, widths);
  701. }
  702. }
  703. QSplitterLayoutStruct *QSplitterPrivate::findWidget(QWidget *w) const
  704. {
  705. for (int i = 0; i < list.size(); ++i) {
  706. if (list.at(i)->widget == w)
  707. return list.at(i);
  708. }
  709. return 0;
  710. }
  711. /*!
  712. \internal
  713. */
  714. void QSplitterPrivate::insertWidget_helper(int index, QWidget *widget, bool show)
  715. {
  716. Q_Q(QSplitter);
  717. QBoolBlocker b(blockChildAdd);
  718. bool needShow = show && q->isVisible() &&
  719. !(widget->isHidden() && widget->testAttribute(Qt::WA_WState_ExplicitShowHide));
  720. if (widget->parentWidget() != q)
  721. widget->setParent(q);
  722. if (needShow)
  723. widget->show();
  724. insertWidget(index, widget);
  725. recalc(q->isVisible());
  726. }
  727. /*
  728. Inserts the widget \a w at position \a index in the splitter's list of widgets.
  729. If \a w is already in the splitter, it will be moved to the new position.
  730. */
  731. QSplitterLayoutStruct *QSplitterPrivate::insertWidget(int index, QWidget *w)
  732. {
  733. Q_Q(QSplitter);
  734. QSplitterLayoutStruct *sls = 0;
  735. int i;
  736. int last = list.count();
  737. for (i = 0; i < list.size(); ++i) {
  738. QSplitterLayoutStruct *s = list.at(i);
  739. if (s->widget == w) {
  740. sls = s;
  741. --last;
  742. break;
  743. }
  744. }
  745. if (index < 0 || index > last)
  746. index = last;
  747. if (sls) {
  748. list.move(i,index);
  749. } else {
  750. QSplitterHandle *newHandle = 0;
  751. sls = new QSplitterLayoutStruct;
  752. QString tmp = QLatin1String("qt_splithandle_");
  753. tmp += w->objectName();
  754. newHandle = q->createHandle();
  755. newHandle->setObjectName(tmp);
  756. sls->handle = newHandle;
  757. sls->widget = w;
  758. w->lower();
  759. list.insert(index,sls);
  760. if (newHandle && q->isVisible())
  761. newHandle->show(); // will trigger sending of post events
  762. }
  763. return sls;
  764. }
  765. /*!
  766. \class QSplitter
  767. \brief The QSplitter class implements a splitter widget.
  768. \ingroup organizers
  769. \inmodule QtWidgets
  770. A splitter lets the user control the size of child widgets by dragging the
  771. boundary between them. Any number of widgets may be controlled by a
  772. single splitter. The typical use of a QSplitter is to create several
  773. widgets and add them using insertWidget() or addWidget().
  774. The following example will show a QListView, QTreeView, and
  775. QTextEdit side by side, with two splitter handles:
  776. \snippet splitter/splitter.cpp 0
  777. If a widget is already inside a QSplitter when insertWidget() or
  778. addWidget() is called, it will move to the new position. This can be used
  779. to reorder widgets in the splitter later. You can use indexOf(),
  780. widget(), and count() to get access to the widgets inside the splitter.
  781. A default QSplitter lays out its children horizontally (side by side); you
  782. can use setOrientation(Qt::Vertical) to lay its
  783. children out vertically.
  784. By default, all widgets can be as large or as small as the user
  785. wishes, between the \l minimumSizeHint() (or \l minimumSize())
  786. and \l maximumSize() of the widgets.
  787. QSplitter resizes its children dynamically by default. If you
  788. would rather have QSplitter resize the children only at the end of
  789. a resize operation, call setOpaqueResize(false).
  790. The initial distribution of size between the widgets is determined by
  791. multiplying the initial size with the stretch factor.
  792. You can also use setSizes() to set the sizes
  793. of all the widgets. The function sizes() returns the sizes set by the user.
  794. Alternatively, you can save and restore the sizes of the widgets from a
  795. QByteArray using saveState() and restoreState() respectively.
  796. When you hide() a child, its space will be distributed among the
  797. other children. It will be reinstated when you show() it again.
  798. \note Adding a QLayout to a QSplitter is not supported (either through
  799. setLayout() or making the QSplitter a parent of the QLayout); use addWidget()
  800. instead (see example above).
  801. \sa QSplitterHandle, QHBoxLayout, QVBoxLayout, QTabWidget
  802. */
  803. /*!
  804. Constructs a horizontal splitter with the \a parent
  805. argument passed on to the QFrame constructor.
  806. \sa setOrientation()
  807. */
  808. QSplitter::QSplitter(QWidget *parent)
  809. : QFrame(*new QSplitterPrivate, parent)
  810. {
  811. Q_D(QSplitter);
  812. d->orient = Qt::Horizontal;
  813. d->init();
  814. }
  815. /*!
  816. Constructs a splitter with the given \a orientation and \a parent.
  817. \sa setOrientation()
  818. */
  819. QSplitter::QSplitter(Qt::Orientation orientation, QWidget *parent)
  820. : QFrame(*new QSplitterPrivate, parent)
  821. {
  822. Q_D(QSplitter);
  823. d->orient = orientation;
  824. d->init();
  825. }
  826. /*!
  827. Destroys the splitter. All children are deleted.
  828. */
  829. QSplitter::~QSplitter()
  830. {
  831. Q_D(QSplitter);
  832. delete d->rubberBand;
  833. while (!d->list.isEmpty())
  834. delete d->list.takeFirst();
  835. }
  836. /*!
  837. Updates the splitter's state. You should not need to call this
  838. function.
  839. */
  840. void QSplitter::refresh()
  841. {
  842. Q_D(QSplitter);
  843. d->recalc(true);
  844. }
  845. /*!
  846. \property QSplitter::orientation
  847. \brief the orientation of the splitter
  848. By default, the orientation is horizontal (i.e., the widgets are
  849. laid out side by side). The possible orientations are
  850. Qt::Horizontal and Qt::Vertical.
  851. \sa QSplitterHandle::orientation()
  852. */
  853. void QSplitter::setOrientation(Qt::Orientation orientation)
  854. {
  855. Q_D(QSplitter);
  856. if (d->orient == orientation)
  857. return;
  858. if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
  859. QSizePolicy sp = sizePolicy();
  860. sp.transpose();
  861. setSizePolicy(sp);
  862. setAttribute(Qt::WA_WState_OwnSizePolicy, false);
  863. }
  864. d->orient = orientation;
  865. for (int i = 0; i < d->list.size(); ++i) {
  866. QSplitterLayoutStruct *s = d->list.at(i);
  867. s->handle->setOrientation(orientation);
  868. }
  869. d->recalc(isVisible());
  870. }
  871. Qt::Orientation QSplitter::orientation() const
  872. {
  873. Q_D(const QSplitter);
  874. return d->orient;
  875. }
  876. /*!
  877. \property QSplitter::childrenCollapsible
  878. \brief whether child widgets can be resized down to size 0 by the user
  879. By default, children are collapsible. It is possible to enable
  880. and disable the collapsing of individual children using
  881. setCollapsible().
  882. \sa setCollapsible()
  883. */
  884. void QSplitter::setChildrenCollapsible(bool collapse)
  885. {
  886. Q_D(QSplitter);
  887. d->childrenCollapsible = collapse;
  888. }
  889. bool QSplitter::childrenCollapsible() const
  890. {
  891. Q_D(const QSplitter);
  892. return d->childrenCollapsible;
  893. }
  894. /*!
  895. Sets whether the child widget at \a index is collapsible to \a collapse.
  896. By default, children are collapsible, meaning that the user can
  897. resize them down to size 0, even if they have a non-zero
  898. minimumSize() or minimumSizeHint(). This behavior can be changed
  899. on a per-widget basis by calling this function, or globally for
  900. all the widgets in the splitter by setting the \l
  901. childrenCollapsible property.
  902. \sa childrenCollapsible
  903. */
  904. void QSplitter::setCollapsible(int index, bool collapse)
  905. {
  906. Q_D(QSplitter);
  907. if (index < 0 || index >= d->list.size()) {
  908. qWarning("QSplitter::setCollapsible: Index %d out of range", index);
  909. return;
  910. }
  911. d->list.at(index)->collapsible = collapse ? 1 : 0;
  912. }
  913. /*!
  914. Returns \c true if the widget at \a index is collapsible, otherwise returns \c false.
  915. */
  916. bool QSplitter::isCollapsible(int index) const
  917. {
  918. Q_D(const QSplitter);
  919. if (index < 0 || index >= d->list.size()) {
  920. qWarning("QSplitter::isCollapsible: Index %d out of range", index);
  921. return false;
  922. }
  923. return d->list.at(index)->collapsible;
  924. }
  925. /*!
  926. \reimp
  927. */
  928. void QSplitter::resizeEvent(QResizeEvent *)
  929. {
  930. Q_D(QSplitter);
  931. d->doResize();
  932. }
  933. /*!
  934. Adds the given \a widget to the splitter's layout after all the other
  935. items.
  936. If \a widget is already in the splitter, it will be moved to the new position.
  937. \sa insertWidget(), widget(), indexOf()
  938. */
  939. void QSplitter::addWidget(QWidget *widget)
  940. {
  941. Q_D(QSplitter);
  942. insertWidget(d->list.count(), widget);
  943. }
  944. /*!
  945. Inserts the \a widget specified into the splitter's layout at the
  946. given \a index.
  947. If \a widget is already in the splitter, it will be moved to the new position.
  948. if \a index is an invalid index, then the widget will be inserted at the end.
  949. \sa addWidget(), indexOf(), widget()
  950. */
  951. void QSplitter::insertWidget(int index, QWidget *widget)
  952. {
  953. Q_D(QSplitter);
  954. d->insertWidget_helper(index, widget, true);
  955. }
  956. /*!
  957. \fn int QSplitter::indexOf(QWidget *widget) const
  958. Returns the index in the splitter's layout of the specified \a widget. This
  959. also works for handles.
  960. Handles are numbered from 0. There are as many handles as there
  961. are child widgets, but the handle at position 0 is always hidden.
  962. \sa count(), widget()
  963. */
  964. int QSplitter::indexOf(QWidget *w) const
  965. {
  966. Q_D(const QSplitter);
  967. for (int i = 0; i < d->list.size(); ++i) {
  968. QSplitterLayoutStruct *s = d->list.at(i);
  969. if (s->widget == w || s->handle == w)
  970. return i;
  971. }
  972. return -1;
  973. }
  974. /*!
  975. Returns a new splitter handle as a child widget of this splitter.
  976. This function can be reimplemented in subclasses to provide support
  977. for custom handles.
  978. \sa handle(), indexOf()
  979. */
  980. QSplitterHandle *QSplitter::createHandle()
  981. {
  982. Q_D(QSplitter);
  983. return new QSplitterHandle(d->orient, this);
  984. }
  985. /*!
  986. Returns the handle to the left (or above) for the item in the
  987. splitter's layout at the given \a index. The handle at index 0 is
  988. always hidden.
  989. For right-to-left languages such as Arabic and Hebrew, the layout
  990. of horizontal splitters is reversed. The handle will be to the
  991. right of the widget at \a index.
  992. \sa count(), widget(), indexOf(), createHandle(), setHandleWidth()
  993. */
  994. QSplitterHandle *QSplitter::handle(int index) const
  995. {
  996. Q_D(const QSplitter);
  997. if (index < 0 || index >= d->list.size())
  998. return 0;
  999. return d->list.at(index)->handle;
  1000. }
  1001. /*!
  1002. Returns the widget at the given \a index in the splitter's layout.
  1003. \sa count(), handle(), indexOf(), insertWidget()
  1004. */
  1005. QWidget *QSplitter::widget(int index) const
  1006. {
  1007. Q_D(const QSplitter);
  1008. if (index < 0 || index >= d->list.size())
  1009. return 0;
  1010. return d->list.at(index)->widget;
  1011. }
  1012. /*!
  1013. Returns the number of widgets contained in the splitter's layout.
  1014. \sa widget(), handle()
  1015. */
  1016. int QSplitter::count() const
  1017. {
  1018. Q_D(const QSplitter);
  1019. return d->list.count();
  1020. }
  1021. /*!
  1022. \reimp
  1023. Tells the splitter that the child widget described by \a c has been
  1024. inserted or removed.
  1025. This method is also used to handle the situation where a widget is created
  1026. with the splitter as a parent but not explicitly added with insertWidget()
  1027. or addWidget(). This is for compatibility and not the recommended way of
  1028. putting widgets into a splitter in new code. Please use insertWidget() or
  1029. addWidget() in new code.
  1030. \sa addWidget(), insertWidget()
  1031. */
  1032. void QSplitter::childEvent(QChildEvent *c)
  1033. {
  1034. Q_D(QSplitter);
  1035. if (!c->child()->isWidgetType()) {
  1036. if (c->type() == QEvent::ChildAdded && qobject_cast<QLayout *>(c->child()))
  1037. qWarning("Adding a QLayout to a QSplitter is not supported.");
  1038. return;
  1039. }
  1040. QWidget *w = static_cast<QWidget*>(c->child());
  1041. if (c->added() && !d->blockChildAdd && !w->isWindow() && !d->findWidget(w)) {
  1042. d->insertWidget_helper(d->list.count(), w, false);
  1043. } else if (c->polished() && !d->blockChildAdd) {
  1044. if (isVisible() && !(w->isHidden() && w->testAttribute(Qt::WA_WState_ExplicitShowHide)))
  1045. w->show();
  1046. } else if (c->type() == QEvent::ChildRemoved) {
  1047. for (int i = 0; i < d->list.size(); ++i) {
  1048. QSplitterLayoutStruct *s = d->list.at(i);
  1049. if (s->widget == w) {
  1050. d->list.removeAt(i);
  1051. delete s;
  1052. d->recalc(isVisible());
  1053. return;
  1054. }
  1055. }
  1056. }
  1057. }
  1058. /*!
  1059. Displays a rubber band at position \a pos. If \a pos is negative, the
  1060. rubber band is removed.
  1061. */
  1062. void QSplitter::setRubberBand(int pos)
  1063. {
  1064. Q_D(QSplitter);
  1065. if (pos < 0) {
  1066. if (d->rubberBand)
  1067. d->rubberBand->deleteLater();
  1068. return;
  1069. }
  1070. QRect r = contentsRect();
  1071. const int rBord = 3; // customizable?
  1072. int hw = handleWidth();
  1073. if (!d->rubberBand) {
  1074. QBoolBlocker b(d->blockChildAdd);
  1075. d->rubberBand = new QRubberBand(QRubberBand::Line, this);
  1076. // For accessibility to identify this special widget.
  1077. d->rubberBand->setObjectName(QLatin1String("qt_rubberband"));
  1078. }
  1079. const QRect newGeom = d->orient == Qt::Horizontal ? QRect(QPoint(pos + hw / 2 - rBord, r.y()), QSize(2 * rBord, r.height()))
  1080. : QRect(QPoint(r.x(), pos + hw / 2 - rBord), QSize(r.width(), 2 * rBord));
  1081. d->rubberBand->setGeometry(newGeom);
  1082. d->rubberBand->show();
  1083. }
  1084. /*!
  1085. \reimp
  1086. */
  1087. bool QSplitter::event(QEvent *e)
  1088. {
  1089. Q_D(QSplitter);
  1090. switch (e->type()) {
  1091. case QEvent::Hide:
  1092. // Reset firstShow to false here since things can be done to the splitter in between
  1093. if (!d->firstShow)
  1094. d->firstShow = true;
  1095. break;
  1096. case QEvent::Show:
  1097. if (!d->firstShow)
  1098. break;
  1099. d->firstShow = false;
  1100. // fall through
  1101. case QEvent::HideToParent:
  1102. case QEvent::ShowToParent:
  1103. case QEvent::LayoutRequest:
  1104. d->recalc(isVisible());
  1105. break;
  1106. default:
  1107. ;
  1108. }
  1109. return QWidget::event(e);
  1110. }
  1111. /*!
  1112. \fn QSplitter::splitterMoved(int pos, int index)
  1113. This signal is emitted when the splitter handle at a particular \a
  1114. index has been moved to position \a pos.
  1115. For right-to-left languages such as Arabic and Hebrew, the layout
  1116. of horizontal splitters is reversed. \a pos is then the
  1117. distance from the right edge of the widget.
  1118. \sa moveSplitter()
  1119. */
  1120. /*!
  1121. Moves the left or top edge of the splitter handle at \a index as
  1122. close as possible to position \a pos, which is the distance from the
  1123. left or top edge of the widget.
  1124. For right-to-left languages such as Arabic and Hebrew, the layout
  1125. of horizontal splitters is reversed. \a pos is then the distance
  1126. from the right edge of the widget.
  1127. \sa splitterMoved(), closestLegalPosition(), getRange()
  1128. */
  1129. void QSplitter::moveSplitter(int pos, int index)
  1130. {
  1131. Q_D(QSplitter);
  1132. QSplitterLayoutStruct *s = d->list.at(index);
  1133. int farMin;
  1134. int min;
  1135. int max;
  1136. int farMax;
  1137. #ifdef QSPLITTER_DEBUG
  1138. int debugp = pos;
  1139. #endif
  1140. pos = d->adjustPos(pos, index, &farMin, &min, &max, &farMax);
  1141. int oldP = d->pick(s->rect.topLeft());
  1142. #ifdef QSPLITTER_DEBUG
  1143. qDebug() << "QSplitter::moveSplitter" << debugp << index << "adjusted" << pos << "oldP" << oldP;
  1144. #endif
  1145. QVarLengthArray<int, 32> poss(d->list.count());
  1146. QVarLengthArray<int, 32> ws(d->list.count());
  1147. bool upLeft;
  1148. d->doMove(false, pos, index, +1, (d->collapsible(s) && (pos > max)), poss.data(), ws.data());
  1149. d->doMove(true, pos, index - 1, +1, (d->collapsible(index - 1) && (pos < min)), poss.data(), ws.data());
  1150. upLeft = (pos < oldP);
  1151. int wid, delta, count = d->list.count();
  1152. if (upLeft) {
  1153. wid = 0;
  1154. delta = 1;
  1155. } else {
  1156. wid = count - 1;
  1157. delta = -1;
  1158. }
  1159. for (; wid >= 0 && wid < count; wid += delta) {
  1160. QSplitterLayoutStruct *sls = d->list.at( wid );
  1161. if (!sls->widget->isHidden())
  1162. d->setGeo(sls, poss[wid], ws[wid], true);
  1163. }
  1164. d->storeSizes();
  1165. emit splitterMoved(pos, index);
  1166. }
  1167. /*!
  1168. Returns the valid range of the splitter at \a index in
  1169. *\a{min} and *\a{max} if \a min and \a max are not 0.
  1170. */
  1171. void QSplitter::getRange(int index, int *min, int *max) const
  1172. {
  1173. Q_D(const QSplitter);
  1174. d->getRange(index, min, 0, 0, max);
  1175. }
  1176. /*!
  1177. Returns the closest legal position to \a pos of the widget at \a index.
  1178. For right-to-left languages such as Arabic and Hebrew, the layout
  1179. of horizontal splitters is reversed. Positions are then measured
  1180. from the right edge of the widget.
  1181. \sa getRange()
  1182. */
  1183. int QSplitter::closestLegalPosition(int pos, int index)
  1184. {
  1185. Q_D(QSplitter);
  1186. int x, i, n, u;
  1187. return d->adjustPos(pos, index, &u, &n, &i, &x);
  1188. }
  1189. /*!
  1190. \property QSplitter::opaqueResize
  1191. \brief whether resizing is opaque
  1192. The default resize behavior is style dependent (determined by the
  1193. SH_Splitter_OpaqueResize style hint). However, you can override it
  1194. by calling setOpaqueResize()
  1195. \sa QStyle::StyleHint
  1196. */
  1197. bool QSplitter::opaqueResize() const
  1198. {
  1199. Q_D(const QSplitter);
  1200. return d->opaqueResizeSet ? d->opaque : style()->styleHint(QStyle::SH_Splitter_OpaqueResize, 0, this);
  1201. }
  1202. void QSplitter::setOpaqueResize(bool on)
  1203. {
  1204. Q_D(QSplitter);
  1205. d->opaqueResizeSet = true;
  1206. d->opaque = on;
  1207. }
  1208. /*!
  1209. \reimp
  1210. */
  1211. QSize QSplitter::sizeHint() const
  1212. {
  1213. Q_D(const QSplitter);
  1214. ensurePolished();
  1215. int l = 0;
  1216. int t = 0;
  1217. for (int i = 0; i < d->list.size(); ++i) {
  1218. QWidget *w = d->list.at(i)->widget;
  1219. if (w->isHidden())
  1220. continue;
  1221. QSize s = w->sizeHint();
  1222. if (s.isValid()) {
  1223. l += d->pick(s);
  1224. t = qMax(t, d->trans(s));
  1225. }
  1226. }
  1227. return orientation() == Qt::Horizontal ? QSize(l, t) : QSize(t, l);
  1228. }
  1229. /*!
  1230. \reimp
  1231. */
  1232. QSize QSplitter::minimumSizeHint() const
  1233. {
  1234. Q_D(const QSplitter);
  1235. ensurePolished();
  1236. int l = 0;
  1237. int t = 0;
  1238. for (int i = 0; i < d->list.size(); ++i) {
  1239. QSplitterLayoutStruct *s = d->list.at(i);
  1240. if (!s || !s->widget)
  1241. continue;
  1242. if (s->widget->isHidden())
  1243. continue;
  1244. QSize widgetSize = qSmartMinSize(s->widget);
  1245. if (widgetSize.isValid()) {
  1246. l += d->pick(widgetSize);
  1247. t = qMax(t, d->trans(widgetSize));
  1248. }
  1249. if (!s->handle || s->handle->isHidden())
  1250. continue;
  1251. QSize splitterSize = s->handle->sizeHint();
  1252. if (splitterSize.isValid()) {
  1253. l += d->pick(splitterSize);
  1254. t = qMax(t, d->trans(splitterSize));
  1255. }
  1256. }
  1257. return orientation() == Qt::Horizontal ? QSize(l, t) : QSize(t, l);
  1258. }
  1259. /*!
  1260. Returns a list of the size parameters of all the widgets in this splitter.
  1261. If the splitter's orientation is horizontal, the list contains the
  1262. widgets width in pixels, from left to right; if the orientation is
  1263. vertical, the list contains the widgets' heights in pixels,
  1264. from top to bottom.
  1265. Giving the values to another splitter's setSizes() function will
  1266. produce a splitter with the same layout as this one.
  1267. Note that invisible widgets have a size of 0.
  1268. \sa setSizes()
  1269. */
  1270. QList<int> QSplitter::sizes() const
  1271. {
  1272. Q_D(const QSplitter);
  1273. ensurePolished();
  1274. QList<int> list;
  1275. for (int i = 0; i < d->list.size(); ++i) {
  1276. QSplitterLayoutStruct *s = d->list.at(i);
  1277. list.append(d->pick(s->rect.size()));
  1278. }
  1279. return list;
  1280. }
  1281. /*!
  1282. Sets the child widgets' respective sizes to the values given in the \a list.
  1283. If the splitter is horizontal, the values set the width of each
  1284. widget in pixels, from left to right. If the splitter is vertical, the
  1285. height of each widget is set, from top to bottom.
  1286. Extra values in the \a list are ignored. If \a list contains too few
  1287. values, the result is undefined, but the program will still be well-behaved.
  1288. The overall size of the splitter widget is not affected.
  1289. Instead, any additional/missing space is distributed amongst the
  1290. widgets according to the relative weight of the sizes.
  1291. If you specify a size of 0, the widget will be invisible. The size policies
  1292. of the widgets are preserved. That is, a value smaller than the minimal size
  1293. hint of the respective widget will be replaced by the value of the hint.
  1294. \sa sizes()
  1295. */
  1296. void QSplitter::setSizes(const QList<int> &list)
  1297. {
  1298. Q_D(QSplitter);
  1299. d->setSizes_helper(list, true);
  1300. }
  1301. /*!
  1302. \property QSplitter::handleWidth
  1303. \brief the width of the splitter handles
  1304. By default, this property contains a value that depends on the user's platform
  1305. and style preferences.
  1306. If you set handleWidth to 1 or 0, the actual grab area will grow to overlap a
  1307. few pixels of its respective widgets.
  1308. */
  1309. int QSplitter::handleWidth() const
  1310. {
  1311. Q_D(const QSplitter);
  1312. if (d->handleWidth >= 0) {
  1313. return d->handleWidth;
  1314. } else {
  1315. return style()->pixelMetric(QStyle::PM_SplitterWidth, 0, this);
  1316. }
  1317. }
  1318. void QSplitter::setHandleWidth(int width)
  1319. {
  1320. Q_D(QSplitter);
  1321. d->handleWidth = width;
  1322. d->updateHandles();
  1323. }
  1324. /*!
  1325. \reimp
  1326. */
  1327. void QSplitter::changeEvent(QEvent *ev)
  1328. {
  1329. Q_D(QSplitter);
  1330. if(ev->type() == QEvent::StyleChange)
  1331. d->updateHandles();
  1332. QFrame::changeEvent(ev);
  1333. }
  1334. static const qint32 SplitterMagic = 0xff;
  1335. /*!
  1336. Saves the state of the splitter's layout.
  1337. Typically this is used in conjunction with QSettings to remember the size
  1338. for a future session. A version number is stored as part of the data.
  1339. Here is an example:
  1340. \snippet splitter/splitter.cpp 1
  1341. \sa restoreState()
  1342. */
  1343. QByteArray QSplitter::saveState() const
  1344. {
  1345. Q_D(const QSplitter);
  1346. int version = 1;
  1347. QByteArray data;
  1348. QDataStream stream(&data, QIODevice::WriteOnly);
  1349. stream << qint32(SplitterMagic);
  1350. stream << qint32(version);
  1351. QList<int> list;
  1352. for (int i = 0; i < d->list.size(); ++i) {
  1353. QSplitterLayoutStruct *s = d->list.at(i);
  1354. list.append(s->sizer);
  1355. }
  1356. stream << list;
  1357. stream << childrenCollapsible();
  1358. stream << qint32(handleWidth());
  1359. stream << opaqueResize();
  1360. stream << qint32(orientation());
  1361. stream << d->opaqueResizeSet;
  1362. return data;
  1363. }
  1364. /*!
  1365. Restores the splitter's layout to the \a state specified.
  1366. Returns \c true if the state is restored; otherwise returns \c false.
  1367. Typically this is used in conjunction with QSettings to restore the size
  1368. from a past session. Here is an example:
  1369. Restore the splitter's state:
  1370. \snippet splitter/splitter.cpp 2
  1371. A failure to restore the splitter's layout may result from either
  1372. invalid or out-of-date data in the supplied byte array.
  1373. \sa saveState()
  1374. */
  1375. bool QSplitter::restoreState(const QByteArray &state)
  1376. {
  1377. Q_D(QSplitter);
  1378. int version = 1;
  1379. QByteArray sd = state;
  1380. QDataStream stream(&sd, QIODevice::ReadOnly);
  1381. QList<int> list;
  1382. bool b;
  1383. qint32 i;
  1384. qint32 marker;
  1385. qint32 v;
  1386. stream >> marker;
  1387. stream >> v;
  1388. if (marker != SplitterMagic || v > version)
  1389. return false;
  1390. stream >> list;
  1391. d->setSizes_helper(list, false);
  1392. stream >> b;
  1393. setChildrenCollapsible(b);
  1394. stream >> i;
  1395. setHandleWidth(i);
  1396. stream >> b;
  1397. setOpaqueResize(b);
  1398. stream >> i;
  1399. setOrientation(Qt::Orientation(i));
  1400. d->doResize();
  1401. if (v >= 1)
  1402. stream >> d->opaqueResizeSet;
  1403. return true;
  1404. }
  1405. /*!
  1406. Updates the size policy of the widget at position \a index to
  1407. have a stretch factor of \a stretch.
  1408. \a stretch is not the effective stretch factor; the effective
  1409. stretch factor is calculated by taking the initial size of the
  1410. widget and multiplying it with \a stretch.
  1411. This function is provided for convenience. It is equivalent to
  1412. \snippet code/src_gui_widgets_qsplitter.cpp 0
  1413. \sa setSizes(), widget()
  1414. */
  1415. void QSplitter::setStretchFactor(int index, int stretch)
  1416. {
  1417. Q_D(QSplitter);
  1418. if (index <= -1 || index >= d->list.count())
  1419. return;
  1420. QWidget *widget = d->list.at(index)->widget;
  1421. QSizePolicy sp = widget->sizePolicy();
  1422. sp.setHorizontalStretch(stretch);
  1423. sp.setVerticalStretch(stretch);
  1424. widget->setSizePolicy(sp);
  1425. }
  1426. /*!
  1427. \relates QSplitter
  1428. \obsolete
  1429. Use \a ts << \a{splitter}.saveState() instead.
  1430. */
  1431. QTextStream& operator<<(QTextStream& ts, const QSplitter& splitter)
  1432. {
  1433. ts << splitter.saveState() << endl;
  1434. return ts;
  1435. }
  1436. /*!
  1437. \relates QSplitter
  1438. \obsolete
  1439. Use \a ts >> \a{splitter}.restoreState() instead.
  1440. */
  1441. QTextStream& operator>>(QTextStream& ts, QSplitter& splitter)
  1442. {
  1443. QString line = ts.readLine();
  1444. line = line.simplified();
  1445. line.replace(QLatin1Char(' '), QString());
  1446. line = line.toUpper();
  1447. splitter.restoreState(line.toLatin1());
  1448. return ts;
  1449. }
  1450. QT_END_NAMESPACE
  1451. #endif // QT_NO_SPLITTER