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

source code of qtbase/src/widgets/widgets/qsplitter.cpp