Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsplitter.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
5#include "qsplitter.h"
6
7#include "qapplication.h"
8#include "qcursor.h"
9#include "qdrawutil.h"
10#include "qevent.h"
11#include "qlayout.h"
12#include "qlist.h"
13#include "qpainter.h"
14#if QT_CONFIG(rubberband)
15#include "qrubberband.h"
16#endif
17#include "qstyle.h"
18#include "qstyleoption.h"
19#include "qtextstream.h"
21#include "private/qlayoutengine_p.h"
22#include "private/qsplitter_p.h"
23#include "qdebug.h"
24
25#include <ctype.h>
26
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, d->s->indexOf(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(this));
184 }
185 return s->closestLegalPosition(pos, s->indexOf(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(d->s);
197 opt.state = QStyle::State_None;
198 return parentWidget()->style()->sizeFromContents(QStyle::CT_Splitter, &opt, QSize(hw, hw), 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, useTinyMode);
217 if (useTinyMode) {
218 if (orientation() == Qt::Horizontal)
219 setContentsMargins(handleMargin, 0, handleMargin, 0);
220 else
221 setContentsMargins(0, handleMargin, 0, handleMargin);
222 setMask(QRegion(contentsRect()));
223 } else {
224 setContentsMargins(0, 0, 0, 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(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(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(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(QStyle::CE_Splitter, &opt, &p, 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(s, orient);
332 const int realsize = pick(widget->size(), orient);
333 if (!s.isValid() || (widget->testAttribute(Qt::WA_Resized) && (realsize > presizer))) {
334 sizer = pick(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(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, 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(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(s->widget);
410 minl += pick(minS);
411 maxl += pick(qSmartMaxSize(s->widget));
412 mint = qMax(mint, trans(minS));
413 int tm = trans(qSmartMaxSize(s->widget));
414 if (tm > 0)
415 maxt = qMin(maxt, tm);
416 }
417 }
418
419 if (empty) {
420 if (qobject_cast<QSplitter *>(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>(maxl, QWIDGETSIZE_MAX);
429 }
430 if (maxt < mint)
431 maxt = mint;
432
433 if (update) {
434 if (orient == Qt::Horizontal) {
435 q->setMaximumSize(maxl, maxt);
436 if (q->isWindow())
437 q->setMinimumSize(minl,mint);
438 } else {
439 q->setMaximumSize(maxt, maxl);
440 if (q->isWindow())
441 q->setMinimumSize(mint,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(qSmartMinSize(s->widget));
490 a[j].maximumSize = pick(qSmartMaxSize(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(s->getWidgetSize(orient), a[j].minimumSize);
505 }
506 }
507 ++j;
508 }
509
510 qGeomCalc(a, 0, n*2, pick(r.topLeft()), pick(r.size()), 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, a[i*2+1].pos, a[i*2+1].size, 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(sls->rect.size());
538 }
539}
540
541void QSplitterPrivate::addContribution(int index, int *min, int *max, bool mayCollapse) const
542{
543 QSplitterLayoutStruct *s = list.at(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(qSmartMinSize(s->widget));
551
552 *max += pick(qSmartMaxSize(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(index)->widget;
562 if (!w->isHidden()) {
563 if (collapsible(list.at(index)))
564 collapsibleSize = pick(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, -1, collapsibleSizeBefore);
586
587 int collapsibleSizeAfter = 0;
588 int idJustAfter = findWidgetJustBeforeOrJustAfter(index, +1, 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(i, &minBefore, &maxBefore, i == idJustBefore);
598 for (i = index; i < n; ++i)
599 addContribution(i, &minAfter, &maxAfter, 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(minBefore, pick(r.size()) - maxAfter);
608 int smartMaxBefore = qMin(maxBefore, pick(r.size()) - minAfter);
609
610 minVal = pick(r.topLeft()) + smartMinBefore;
611 maxVal = pick(r.topLeft()) + smartMaxBefore;
612
613 farMinVal = minVal;
614 if (minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter)
615 farMinVal -= collapsibleSizeBefore;
616 farMaxVal = maxVal;
617 if (pick(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(Threshold, 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(Threshold, 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(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(j++);
685 if (clampNegativeSize && s->sizer < 0)
686 s->sizer = 0;
687 int smartMinSize = pick(qSmartMinSize(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() && !QWidgetPrivate::get(w)->isExplicitlyHidden();
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(p, contents.y(), s, contents.height());
723 } else {
724 r.setRect(contents.x(), p, contents.width(), s);
725 }
726 sls->rect = r;
727
728 int minSize = pick(qSmartMinSize(w));
729
730 if (orient == Qt::Horizontal && q->isRightToLeft())
731 r.moveRight(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(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(p-hs.width() - m.left(), contents.y(), hs.width() + m.left() + m.right(), contents.height());
751 } else {
752 h->setGeometry(contents.x(), p-hs.height() - m.top(), contents.width(), 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(index);
768 QWidget *w = s->widget;
769
770 int nextId = backwards ? index - delta : index + delta;
771
772 if (w->isHidden()) {
773 doMove(backwards, hPos, nextId, delta, collapsible(nextId), positions, widths);
774 } else {
775 int hs =s->handle->isHidden() ? 0 : s->getHandleSize(orient);
776
777 int ws = backwards ? hPos - pick(s->rect.topLeft())
778 : pick(s->rect.bottomRight()) - hPos -hs + 1;
779 if (ws > 0 || (!s->collapsed && !mayCollapse)) {
780 ws = qMin(ws, pick(qSmartMaxSize(w)));
781 ws = qMax(ws, pick(qSmartMinSize(w)));
782 } else {
783 ws = 0;
784 }
785 positions[index] = backwards ? hPos - ws : hPos + hs;
786 widths[index] = ws;
787 doMove(backwards, backwards ? hPos - ws - hs : hPos + hs + ws, nextId, delta,
788 collapsible(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 QScopedValueRollback b(blockChildAdd, true);
810 const bool needShow = show && shouldShowWidget(widget);
811 if (widget->parentWidget() != q)
812 widget->setParent(q);
813 if (needShow)
814 widget->show();
815 insertWidget(index, widget);
816 recalc(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(i,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(index,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 \section1 Security Considerations
910
911 The restoreState() function deserializes a versioned binary blob that
912 describes the sizes and orientation of the splitter's children. The
913 format's magic number and version are validated, but the individual fields
914 are not otherwise sanity-checked once the outer structure is accepted.
915
916 Only pass restoreState() a QByteArray that was previously produced by
917 saveState() and persisted by the same, or a compatible, version of your
918 application, typically through QSettings. Do not call restoreState()
919 with data of unknown or untrusted origin, such as a file downloaded from
920 the network, a synced or shared configuration file, or data supplied by
921 another, potentially compromised, application.
922
923 \sa QSplitterHandle, QHBoxLayout, QVBoxLayout, QTabWidget
924*/
925
926
927/*!
928 Constructs a horizontal splitter with the \a parent
929 argument passed on to the QFrame constructor.
930
931 \sa setOrientation()
932*/
933QSplitter::QSplitter(QWidget *parent)
934 : QSplitter(Qt::Horizontal, parent)
935{
936}
937
938
939/*!
940 Constructs a splitter with the given \a orientation and \a parent.
941
942 \sa setOrientation()
943*/
944QSplitter::QSplitter(Qt::Orientation orientation, QWidget *parent)
945 : QFrame(*new QSplitterPrivate, parent)
946{
947 Q_D(QSplitter);
948 d->orient = orientation;
949 d->init();
950}
951
952
953/*!
954 Destroys the splitter. All children are deleted.
955*/
956
957QSplitter::~QSplitter()
958{
959 Q_D(QSplitter);
960#if QT_CONFIG(rubberband)
961 delete d->rubberBand;
962#endif
963 while (!d->list.isEmpty())
964 delete d->list.takeFirst();
965}
966
967/*!
968 Updates the splitter's state. You should not need to call this
969 function.
970*/
971void QSplitter::refresh()
972{
973 Q_D(QSplitter);
974 d->recalc(true);
975}
976
977/*!
978 \property QSplitter::orientation
979 \brief the orientation of the splitter
980
981 By default, the orientation is horizontal (i.e., the widgets are
982 laid out side by side). The possible orientations are
983 Qt::Horizontal and Qt::Vertical.
984
985 \sa QSplitterHandle::orientation()
986*/
987
988void QSplitter::setOrientation(Qt::Orientation orientation)
989{
990 Q_D(QSplitter);
991 if (d->orient == orientation)
992 return;
993
994 if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
995 setSizePolicy(sizePolicy().transposed());
996 setAttribute(Qt::WA_WState_OwnSizePolicy, false);
997 }
998
999 d->orient = orientation;
1000
1001 for (int i = 0; i < d->list.size(); ++i) {
1002 QSplitterLayoutStruct *s = d->list.at(i);
1003 s->handle->setOrientation(orientation);
1004 }
1005 d->recalc(isVisible());
1006}
1007
1008Qt::Orientation QSplitter::orientation() const
1009{
1010 Q_D(const QSplitter);
1011 return d->orient;
1012}
1013
1014/*!
1015 \property QSplitter::childrenCollapsible
1016 \brief whether child widgets can be resized down to size 0 by the user
1017
1018 By default, children are collapsible. It is possible to enable
1019 and disable the collapsing of individual children using
1020 setCollapsible().
1021
1022 \sa setCollapsible()
1023*/
1024
1025void QSplitter::setChildrenCollapsible(bool collapse)
1026{
1027 Q_D(QSplitter);
1028 d->childrenCollapsible = collapse;
1029}
1030
1031bool QSplitter::childrenCollapsible() const
1032{
1033 Q_D(const QSplitter);
1034 return d->childrenCollapsible;
1035}
1036
1037/*!
1038 Sets whether the child widget at \a index is collapsible to \a collapse.
1039
1040 By default, children are collapsible, meaning that the user can
1041 resize them down to size 0, even if they have a non-zero
1042 minimumSize() or minimumSizeHint(). This behavior can be changed
1043 on a per-widget basis by calling this function, or globally for
1044 all the widgets in the splitter by setting the \l
1045 childrenCollapsible property.
1046
1047 \sa childrenCollapsible
1048*/
1049
1050void QSplitter::setCollapsible(int index, bool collapse)
1051{
1052 Q_D(QSplitter);
1053
1054 if (Q_UNLIKELY(index < 0 || index >= d->list.size())) {
1055 qWarning("QSplitter::setCollapsible: Index %d out of range", index);
1056 return;
1057 }
1058 d->list.at(index)->collapsible = collapse ? 1 : 0;
1059}
1060
1061/*!
1062 Returns \c true if the widget at \a index is collapsible, otherwise returns \c false.
1063*/
1064bool QSplitter::isCollapsible(int index) const
1065{
1066 Q_D(const QSplitter);
1067 if (Q_UNLIKELY(index < 0 || index >= d->list.size())) {
1068 qWarning("QSplitter::isCollapsible: Index %d out of range", index);
1069 return false;
1070 }
1071 return d->list.at(index)->collapsible;
1072}
1073
1074/*!
1075 \reimp
1076*/
1077void QSplitter::resizeEvent(QResizeEvent *)
1078{
1079 Q_D(QSplitter);
1080 d->doResize();
1081}
1082
1083/*!
1084 Adds the given \a widget to the splitter's layout after all the other
1085 items.
1086
1087 If \a widget is already in the splitter, it will be moved to the new position.
1088
1089 \note The splitter takes ownership of the widget.
1090
1091 \sa insertWidget(), widget(), indexOf()
1092*/
1093void QSplitter::addWidget(QWidget *widget)
1094{
1095 Q_D(QSplitter);
1096 insertWidget(d->list.size(), widget);
1097}
1098
1099/*!
1100 Inserts the \a widget specified into the splitter's layout at the
1101 given \a index.
1102
1103 If \a widget is already in the splitter, it will be moved to the new position.
1104
1105 If \a index is an invalid index, then the widget will be inserted at the end.
1106
1107 \note The splitter takes ownership of the widget.
1108
1109 \sa addWidget(), indexOf(), widget()
1110*/
1111void QSplitter::insertWidget(int index, QWidget *widget)
1112{
1113 Q_D(QSplitter);
1114 d->insertWidget_helper(index, widget, true);
1115}
1116
1117/*!
1118 \since 5.9
1119
1120 Replaces the widget in the splitter's layout at the given \a index by \a widget.
1121
1122 Returns the widget that has just been replaced if \a index is valid and \a widget
1123 is not already a child of the splitter. Otherwise, it returns null and no replacement
1124 or addition is made.
1125
1126 The geometry of the newly inserted widget will be the same as the widget it replaces.
1127 Its visible and collapsed states are also inherited.
1128
1129 \note The splitter takes ownership of \a widget and sets the parent of the
1130 replaced widget to null.
1131
1132 \note Because \a widget gets \l{QWidget::setParent()}{reparented} into the splitter,
1133 its \l{QWidget::}{geometry} may not be set right away, but only after \a widget will
1134 receive the appropriate events.
1135
1136 \sa insertWidget(), indexOf()
1137*/
1138QWidget *QSplitter::replaceWidget(int index, QWidget *widget)
1139{
1140 Q_D(QSplitter);
1141 if (!widget) {
1142 qWarning("QSplitter::replaceWidget: Widget can't be null");
1143 return nullptr;
1144 }
1145
1146 if (index < 0 || index >= d->list.size()) {
1147 qWarning("QSplitter::replaceWidget: Index %d out of range", index);
1148 return nullptr;
1149 }
1150
1151 QSplitterLayoutStruct *s = d->list.at(index);
1152 QWidget *current = s->widget;
1153 if (current == widget) {
1154 qWarning("QSplitter::replaceWidget: Trying to replace a widget with itself");
1155 return nullptr;
1156 }
1157
1158 if (widget->parentWidget() == this) {
1159 qWarning("QSplitter::replaceWidget: Trying to replace a widget with one of its siblings");
1160 return nullptr;
1161 }
1162
1163 QScopedValueRollback b(d->blockChildAdd, true);
1164
1165 const QRect geom = current->geometry();
1166 const bool wasHidden = current->isHidden();
1167
1168 s->widget = widget;
1169 current->setParent(nullptr);
1170 widget->setParent(this);
1171
1172 // The splitter layout struct's geometry is already set and
1173 // should not change. Only set the geometry on the new widget
1174 widget->setGeometry(geom);
1175 widget->lower();
1176 if (wasHidden)
1177 widget->hide();
1178 else if (d->shouldShowWidget(widget))
1179 widget->show();
1180
1181 return current;
1182}
1183
1184/*!
1185 Returns the index in the splitter's layout of the specified \a widget,
1186 or -1 if \a widget is not found. This also works for handles.
1187
1188 Handles are numbered from 0. There are as many handles as there
1189 are child widgets, but the handle at position 0 is always hidden.
1190
1191
1192 \sa count(), widget()
1193*/
1194int QSplitter::indexOf(QWidget *widget) const
1195{
1196 Q_D(const QSplitter);
1197 for (int i = 0; i < d->list.size(); ++i) {
1198 QSplitterLayoutStruct *s = d->list.at(i);
1199 if (s->widget == widget || s->handle == widget)
1200 return i;
1201 }
1202 return -1;
1203}
1204
1205/*!
1206 Returns a new splitter handle as a child widget of this splitter.
1207 This function can be reimplemented in subclasses to provide support
1208 for custom handles.
1209
1210 \sa handle(), indexOf()
1211*/
1212QSplitterHandle *QSplitter::createHandle()
1213{
1214 Q_D(QSplitter);
1215 return new QSplitterHandle(d->orient, this);
1216}
1217
1218/*!
1219 Returns the handle to the left of (or above) the item in the
1220 splitter's layout at the given \a index, or \nullptr if there is no such item.
1221 The handle at index 0 is always hidden.
1222
1223 For right-to-left languages such as Arabic and Hebrew, the layout
1224 of horizontal splitters is reversed. The handle will be to the
1225 right of the widget at \a index.
1226
1227 \sa count(), widget(), indexOf(), createHandle(), setHandleWidth()
1228*/
1229QSplitterHandle *QSplitter::handle(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(index)->handle;
1235}
1236
1237/*!
1238 Returns the widget at the given \a index in the splitter's layout,
1239 or \nullptr if there is no such widget.
1240
1241 \sa count(), handle(), indexOf(), insertWidget()
1242*/
1243QWidget *QSplitter::widget(int index) const
1244{
1245 Q_D(const QSplitter);
1246 if (index < 0 || index >= d->list.size())
1247 return nullptr;
1248 return d->list.at(index)->widget;
1249}
1250
1251/*!
1252 Returns the number of widgets contained in the splitter's layout.
1253
1254 \sa widget(), handle()
1255*/
1256int QSplitter::count() const
1257{
1258 Q_D(const QSplitter);
1259 return d->list.size();
1260}
1261
1262/*!
1263 \reimp
1264
1265 Tells the splitter that the child widget described by \a c has been
1266 inserted or removed.
1267
1268 This method is also used to handle the situation where a widget is created
1269 with the splitter as a parent but not explicitly added with insertWidget()
1270 or addWidget(). This is for compatibility and not the recommended way of
1271 putting widgets into a splitter in new code. Please use insertWidget() or
1272 addWidget() in new code.
1273
1274 \sa addWidget(), insertWidget()
1275*/
1276
1277void QSplitter::childEvent(QChildEvent *c)
1278{
1279 Q_D(QSplitter);
1280 if (c->added()) {
1281 if (!c->child()->isWidgetType()) {
1282 if (Q_UNLIKELY(qobject_cast<QLayout *>(c->child())))
1283 qWarning("Adding a QLayout to a QSplitter is not supported.");
1284 return;
1285 }
1286 QWidget *w = static_cast<QWidget*>(c->child());
1287 if (!d->blockChildAdd && !w->isWindow() && !d->findWidget(w))
1288 d->insertWidget_helper(d->list.size(), w, false);
1289 } else if (c->polished()) {
1290 if (!c->child()->isWidgetType())
1291 return;
1292 QWidget *w = static_cast<QWidget*>(c->child());
1293 if (!d->blockChildAdd && !w->isWindow() && d->shouldShowWidget(w))
1294 w->show();
1295 } else if (c->removed()) {
1296 QObject *child = c->child();
1297 for (int i = 0; i < d->list.size(); ++i) {
1298 QSplitterLayoutStruct *s = d->list.at(i);
1299 if (s->widget == child) {
1300 d->list.removeAt(i);
1301 delete s;
1302 d->recalc(isVisible());
1303 return;
1304 }
1305 }
1306 }
1307}
1308
1309
1310/*!
1311 Displays a rubber band at position \a pos. If \a pos is negative, the
1312 rubber band is removed.
1313*/
1314
1315void QSplitter::setRubberBand(int pos)
1316{
1317#if QT_CONFIG(rubberband)
1318 Q_D(QSplitter);
1319 if (pos < 0) {
1320 if (d->rubberBand)
1321 d->rubberBand->deleteLater();
1322 return;
1323 }
1324 QRect r = contentsRect();
1325 const int rBord = 3; // customizable?
1326 int hw = handleWidth();
1327 if (!d->rubberBand) {
1328 QScopedValueRollback b(d->blockChildAdd, true);
1329 d->rubberBand = new QRubberBand(QRubberBand::Line, this);
1330 // For accessibility to identify this special widget.
1331 d->rubberBand->setObjectName("qt_rubberband"_L1);
1332 }
1333
1334 const QRect newGeom = d->orient == Qt::Horizontal ? QRect(QPoint(pos + hw / 2 - rBord, r.y()), QSize(2 * rBord, r.height()))
1335 : QRect(QPoint(r.x(), pos + hw / 2 - rBord), QSize(r.width(), 2 * rBord));
1336 d->rubberBand->setGeometry(newGeom);
1337 d->rubberBand->show();
1338#else
1339 Q_UNUSED(pos);
1340#endif
1341}
1342
1343/*!
1344 \reimp
1345*/
1346
1347bool QSplitter::event(QEvent *e)
1348{
1349 Q_D(QSplitter);
1350 switch (e->type()) {
1351 case QEvent::Hide:
1352 // Reset firstShow to false here since things can be done to the splitter in between
1353 if (!d->firstShow)
1354 d->firstShow = true;
1355 break;
1356 case QEvent::Show:
1357 if (!d->firstShow)
1358 break;
1359 d->firstShow = false;
1360 Q_FALLTHROUGH();
1361 case QEvent::HideToParent:
1362 case QEvent::ShowToParent:
1363 case QEvent::LayoutRequest:
1364 d->recalc(isVisible());
1365 break;
1366 default:
1367 ;
1368 }
1369 return QFrame::event(e);
1370}
1371
1372/*!
1373 \fn void QSplitter::splitterMoved(int pos, int index)
1374
1375 This signal is emitted when the splitter handle at a particular \a
1376 index has been moved to position \a pos.
1377
1378 For right-to-left languages such as Arabic and Hebrew, the layout
1379 of horizontal splitters is reversed. \a pos is then the
1380 distance from the right edge of the widget.
1381
1382 \sa moveSplitter()
1383*/
1384
1385/*!
1386 Moves the left or top edge of the splitter handle at \a index as
1387 close as possible to position \a pos, which is the distance from the
1388 left or top edge of the widget.
1389
1390 For right-to-left languages such as Arabic and Hebrew, the layout
1391 of horizontal splitters is reversed. \a pos is then the distance
1392 from the right edge of the widget.
1393
1394 \sa splitterMoved(), closestLegalPosition(), getRange()
1395*/
1396void QSplitter::moveSplitter(int pos, int index)
1397{
1398 Q_D(QSplitter);
1399 QSplitterLayoutStruct *s = d->list.at(index);
1400 int farMin = 0;
1401 int min = 0;
1402 int max = 0;
1403 int farMax = 0;
1404
1405#ifdef QSPLITTER_DEBUG
1406 int debugp = pos;
1407#endif
1408
1409 pos = d->adjustPos(pos, index, &farMin, &min, &max, &farMax);
1410 int oldP = d->pick(s->rect.topLeft());
1411#ifdef QSPLITTER_DEBUG
1412 qDebug() << "QSplitter::moveSplitter" << debugp << index << "adjusted" << pos << "oldP" << oldP;
1413#endif
1414
1415 QVarLengthArray<int, 32> poss(d->list.size());
1416 QVarLengthArray<int, 32> ws(d->list.size());
1417 bool upLeft;
1418
1419 d->doMove(false, pos, index, +1, (d->collapsible(s) && (pos > max)), poss.data(), ws.data());
1420 d->doMove(true, pos, index - 1, +1, (d->collapsible(index - 1) && (pos < min)), poss.data(), ws.data());
1421 upLeft = (pos < oldP);
1422
1423 int wid, delta, count = d->list.size();
1424 if (upLeft) {
1425 wid = 0;
1426 delta = 1;
1427 } else {
1428 wid = count - 1;
1429 delta = -1;
1430 }
1431 for (; wid >= 0 && wid < count; wid += delta) {
1432 QSplitterLayoutStruct *sls = d->list.at( wid );
1433 if (!sls->widget->isHidden())
1434 d->setGeo(sls, poss[wid], ws[wid], true);
1435 }
1436 d->storeSizes();
1437
1438 emit splitterMoved(pos, index);
1439}
1440
1441
1442/*!
1443 Returns the valid range of the splitter at \a index in
1444 *\a{min} and *\a{max} if \a min and \a max are not 0.
1445*/
1446
1447void QSplitter::getRange(int index, int *min, int *max) const
1448{
1449 Q_D(const QSplitter);
1450 d->getRange(index, min, nullptr, nullptr, max);
1451}
1452
1453
1454/*!
1455 Returns the closest legal position to \a pos of the widget at \a index.
1456
1457 For right-to-left languages such as Arabic and Hebrew, the layout
1458 of horizontal splitters is reversed. Positions are then measured
1459 from the right edge of the widget.
1460
1461 \sa getRange()
1462*/
1463
1464int QSplitter::closestLegalPosition(int pos, int index)
1465{
1466 Q_D(QSplitter);
1467 int x = 0;
1468 int i = 0;
1469 int n = 0;
1470 int u = 0;
1471 return d->adjustPos(pos, index, &u, &n, &i, &x);
1472}
1473
1474/*!
1475 \property QSplitter::opaqueResize
1476 Returns \c true if widgets are resized dynamically (opaquely) while interactively moving the
1477 splitter. Otherwise returns \c false.
1478
1479 The default resize behavior is style dependent (determined by the
1480 SH_Splitter_OpaqueResize style hint). However, you can override it
1481 by calling setOpaqueResize()
1482
1483 \sa QStyle::StyleHint
1484*/
1485
1486bool QSplitter::opaqueResize() const
1487{
1488 Q_D(const QSplitter);
1489 return d->opaqueResizeSet ? d->opaque : style()->styleHint(QStyle::SH_Splitter_OpaqueResize, nullptr, this);
1490}
1491
1492
1493void QSplitter::setOpaqueResize(bool on)
1494{
1495 Q_D(QSplitter);
1496 d->opaqueResizeSet = true;
1497 d->opaque = on;
1498}
1499
1500
1501/*!
1502 \reimp
1503*/
1504QSize QSplitter::sizeHint() const
1505{
1506 Q_D(const QSplitter);
1507 ensurePolished();
1508 int l = 0;
1509 int t = 0;
1510 for (int i = 0; i < d->list.size(); ++i) {
1511 QWidget *w = d->list.at(i)->widget;
1512 if (w->isHidden())
1513 continue;
1514 QSize s = w->sizeHint();
1515 if (s.isValid()) {
1516 l += d->pick(s);
1517 t = qMax(t, d->trans(s));
1518 }
1519 }
1520 return orientation() == Qt::Horizontal ? QSize(l, t) : QSize(t, l);
1521}
1522
1523
1524/*!
1525 \reimp
1526*/
1527
1528QSize QSplitter::minimumSizeHint() const
1529{
1530 Q_D(const QSplitter);
1531 ensurePolished();
1532 int l = 0;
1533 int t = 0;
1534
1535 for (int i = 0; i < d->list.size(); ++i) {
1536 QSplitterLayoutStruct *s = d->list.at(i);
1537 if (!s || !s->widget)
1538 continue;
1539 if (s->widget->isHidden())
1540 continue;
1541 QSize widgetSize = qSmartMinSize(s->widget);
1542 if (widgetSize.isValid()) {
1543 l += d->pick(widgetSize);
1544 t = qMax(t, d->trans(widgetSize));
1545 }
1546 if (!s->handle || s->handle->isHidden())
1547 continue;
1548 QSize splitterSize = s->handle->sizeHint();
1549 if (splitterSize.isValid()) {
1550 l += d->pick(splitterSize);
1551 t = qMax(t, d->trans(splitterSize));
1552 }
1553 }
1554 return orientation() == Qt::Horizontal ? QSize(l, t) : QSize(t, l);
1555}
1556
1557
1558/*!
1559 Returns a list of the size parameters of all the widgets in this splitter.
1560
1561 If the splitter's orientation is horizontal, the list contains the
1562 widgets width in pixels, from left to right; if the orientation is
1563 vertical, the list contains the widgets' heights in pixels,
1564 from top to bottom.
1565
1566 Giving the values to another splitter's setSizes() function will
1567 produce a splitter with the same layout as this one.
1568
1569 Note that invisible widgets have a size of 0.
1570
1571 \sa setSizes()
1572*/
1573
1574QList<int> QSplitter::sizes() const
1575{
1576 Q_D(const QSplitter);
1577 ensurePolished();
1578
1579 const int numSizes = d->list.size();
1580 QList<int> list;
1581 list.reserve(numSizes);
1582
1583 for (int i = 0; i < numSizes; ++i) {
1584 QSplitterLayoutStruct *s = d->list.at(i);
1585 list.append(d->pick(s->rect.size()));
1586 }
1587 return list;
1588}
1589
1590/*!
1591 Sets the child widgets' respective sizes to the values given in the \a list.
1592
1593 If the splitter is horizontal, the values set the width of each
1594 widget in pixels, from left to right. If the splitter is vertical, the
1595 height of each widget is set, from top to bottom.
1596
1597 Extra values in the \a list are ignored. If \a list contains too few
1598 values, the result is undefined, but the program will still be well-behaved.
1599
1600 The overall size of the splitter widget is not affected.
1601 Instead, any additional/missing space is distributed amongst the
1602 widgets according to the relative weight of the sizes.
1603
1604 If you specify a size of 0, the widget will be invisible. The size policies
1605 of the widgets are preserved. That is, a value smaller than the minimal size
1606 hint of the respective widget will be replaced by the value of the hint.
1607
1608 \sa sizes()
1609*/
1610
1611void QSplitter::setSizes(const QList<int> &list)
1612{
1613 Q_D(QSplitter);
1614 d->setSizes_helper(list, true);
1615}
1616
1617/*!
1618 \property QSplitter::handleWidth
1619 \brief the width of the splitter handles
1620
1621 By default, this property contains a value that depends on the user's platform
1622 and style preferences.
1623
1624 If you set handleWidth to 1 or 0, the actual grab area will grow to overlap a
1625 few pixels of its respective widgets.
1626*/
1627
1628int QSplitter::handleWidth() const
1629{
1630 Q_D(const QSplitter);
1631 if (d->handleWidth >= 0) {
1632 return d->handleWidth;
1633 } else {
1634 return style()->pixelMetric(QStyle::PM_SplitterWidth, nullptr, this);
1635 }
1636}
1637
1638void QSplitter::setHandleWidth(int width)
1639{
1640 Q_D(QSplitter);
1641 d->handleWidth = width;
1642 d->updateHandles();
1643}
1644
1645/*!
1646 \reimp
1647*/
1648void QSplitter::changeEvent(QEvent *ev)
1649{
1650 Q_D(QSplitter);
1651 if (ev->type() == QEvent::StyleChange)
1652 d->updateHandles();
1653 QFrame::changeEvent(ev);
1654}
1655
1656static const qint32 SplitterMagic = 0xff;
1657
1658/*!
1659 Saves the state of the splitter's layout.
1660
1661 Typically this is used in conjunction with QSettings to remember the size
1662 for a future session. A version number is stored as part of the data.
1663 Here is an example:
1664
1665 \snippet splitter/splitter.cpp 1
1666
1667 \sa restoreState()
1668*/
1669QByteArray QSplitter::saveState() const
1670{
1671 Q_D(const QSplitter);
1672 int version = 1;
1673 QByteArray data;
1674 QDataStream stream(&data, QIODevice::WriteOnly);
1675 stream.setVersion(QDataStream::Qt_5_0);
1676
1677 stream << qint32(SplitterMagic);
1678 stream << qint32(version);
1679 const int numSizes = d->list.size();
1680 QList<int> list;
1681 list.reserve(numSizes);
1682 for (int i = 0; i < numSizes; ++i) {
1683 QSplitterLayoutStruct *s = d->list.at(i);
1684 list.append(s->sizer);
1685 }
1686 stream << list;
1687 stream << childrenCollapsible();
1688 stream << qint32(d->handleWidth);
1689 stream << opaqueResize();
1690 stream << qint32(orientation());
1691 stream << d->opaqueResizeSet;
1692 return data;
1693}
1694
1695/*!
1696 Restores the splitter's layout to the \a state specified.
1697 Returns \c true if the state is restored; otherwise returns \c false.
1698
1699 Typically this is used in conjunction with QSettings to restore the size
1700 from a past session. Here is an example:
1701
1702 Restore the splitter's state:
1703
1704 \snippet splitter/splitter.cpp 2
1705
1706 A failure to restore the splitter's layout may result from either
1707 invalid or out-of-date data in the supplied byte array.
1708
1709 \sa saveState()
1710*/
1711bool QSplitter::restoreState(const QByteArray &state)
1712{
1713 Q_D(QSplitter);
1714 int version = 1;
1715 QByteArray sd = state;
1716 QDataStream stream(&sd, QIODevice::ReadOnly);
1717 stream.setVersion(QDataStream::Qt_5_0);
1718 QList<int> list;
1719 bool b;
1720 qint32 i;
1721 qint32 marker;
1722 qint32 v;
1723
1724 stream >> marker;
1725 stream >> v;
1726 if (marker != SplitterMagic || v > version)
1727 return false;
1728
1729 stream >> list;
1730 d->setSizes_helper(list, false);
1731
1732 stream >> b;
1733 setChildrenCollapsible(b);
1734
1735 stream >> i;
1736 setHandleWidth(i);
1737
1738 stream >> b;
1739 setOpaqueResize(b);
1740
1741 stream >> i;
1742 setOrientation(Qt::Orientation(i));
1743 d->doResize();
1744
1745 if (v >= 1)
1746 stream >> d->opaqueResizeSet;
1747
1748 return true;
1749}
1750
1751/*!
1752 Updates the size policy of the widget at position \a index to
1753 have a stretch factor of \a stretch.
1754
1755 \a stretch is not the effective stretch factor; the effective
1756 stretch factor is calculated by taking the initial size of the
1757 widget and multiplying it with \a stretch.
1758
1759 This function is provided for convenience. It is equivalent to
1760
1761 \snippet code/src_gui_widgets_qsplitter.cpp 0
1762
1763 \sa setSizes(), widget()
1764*/
1765void QSplitter::setStretchFactor(int index, int stretch)
1766{
1767 Q_D(QSplitter);
1768 if (index <= -1 || index >= d->list.size())
1769 return;
1770
1771 QWidget *widget = d->list.at(index)->widget;
1772 QSizePolicy sp = widget->sizePolicy();
1773 sp.setHorizontalStretch(stretch);
1774 sp.setVerticalStretch(stretch);
1775 widget->setSizePolicy(sp);
1776}
1777
1778QT_END_NAMESPACE
1779
1780#include "moc_qsplitter.cpp"
Combined button and popup list for selecting options.
static const qint32 SplitterMagic