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