86QSplitterHandle::QSplitterHandle(Qt::Orientation orientation, QSplitter *parent)
87 : QWidget(*
new QSplitterHandlePrivate, parent, { })
91 setOrientation(orientation);
161void QSplitterHandle::moveSplitter(
int pos)
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));
177int QSplitterHandle::closestLegalPosition(
int pos)
179 Q_D(QSplitterHandle);
181 if (s->isRightToLeft() && d->orient == Qt::Horizontal) {
182 int w = s->contentsRect().width();
183 return w - s->closestLegalPosition(w - pos, s->indexOf(
this));
185 return s->closestLegalPosition(pos, s->indexOf(
this));
191QSize QSplitterHandle::sizeHint()
const
193 Q_D(
const QSplitterHandle);
194 int hw = d->s->handleWidth();
197 opt.state = QStyle::State_None;
198 return parentWidget()->style()->sizeFromContents(QStyle::CT_Splitter, &opt, QSize(hw, hw), d->s);
204void QSplitterHandle::resizeEvent(QResizeEvent *event)
206 Q_D(
const QSplitterHandle);
209 const int handleMargin = (5 - d->s->handleWidth()) / 2;
215 const bool useTinyMode = handleMargin > 0;
216 setAttribute(Qt::WA_MouseNoMask, useTinyMode);
218 if (orientation() == Qt::Horizontal)
219 setContentsMargins(handleMargin, 0, handleMargin, 0);
221 setContentsMargins(0, handleMargin, 0, handleMargin);
222 setMask(QRegion(contentsRect()));
224 setContentsMargins(0, 0, 0, 0);
228 QWidget::resizeEvent(event);
306void QSplitterHandle::paintEvent(QPaintEvent *)
308 Q_D(QSplitterHandle);
311 opt.rect = contentsRect();
312 opt.palette = palette();
313 if (orientation() == Qt::Horizontal)
314 opt.state = QStyle::State_Horizontal;
316 opt.state = QStyle::State_None;
318 opt.state |= QStyle::State_MouseOver;
320 opt.state |= QStyle::State_Sunken;
322 opt.state |= QStyle::State_Enabled;
323 parentWidget()->style()->drawControl(QStyle::CE_Splitter, &opt, &p, d->s);
327int QSplitterLayoutStruct::getWidgetSize(Qt::Orientation orient)
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);
338 QSizePolicy p = widget->sizePolicy();
339 int sf = (orient == Qt::Horizontal) ? p.horizontalStretch() : p.verticalStretch();
361void QSplitterPrivate::recalc(
bool update)
366
367
368
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);
382 for (
int i = 0; i < n ; ++i) {
383 QSplitterLayoutStruct *s = list.at(i);
384 if (!s->widget->isHidden()) {
385 s->collapsed =
false;
390 int fi = 2 * q->frameWidth();
393 int maxt = QWIDGETSIZE_MAX;
396
397
399 for (
int j = 0; j < n; j++) {
400 QSplitterLayoutStruct *s = list.at(j);
402 if (!s->widget->isHidden()) {
404 if (!s->handle->isHidden()) {
405 minl += s->getHandleSize(orient);
406 maxl += s->getHandleSize(orient);
409 QSize minS = qSmartMinSize(s->widget);
411 maxl += pick(qSmartMaxSize(s->widget));
412 mint = qMax(mint, trans(minS));
413 int tm = trans(qSmartMaxSize(s->widget));
415 maxt = qMin(maxt, tm);
420 if (qobject_cast<QSplitter *>(parent)) {
425 maxl = QWIDGETSIZE_MAX;
428 maxl = qMin<
int>(maxl, QWIDGETSIZE_MAX);
434 if (orient == Qt::Horizontal) {
435 q->setMaximumSize(maxl, maxt);
437 q->setMinimumSize(minl,mint);
439 q->setMaximumSize(maxt, maxl);
441 q->setMinimumSize(mint,minl);
450void QSplitterPrivate::doResize()
453 QRect r = q->contentsRect();
455 QList<QLayoutStruct> a(n * 2);
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();
463 noStretchFactorsSet =
false;
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());
477 if (s->handle->isHidden()) {
478 a[j].maximumSize = 0;
480 a[j].sizeHint = a[j].minimumSize = a[j].maximumSize = s->getHandleSize(orient);
486 if (s->widget->isHidden() || s->collapsed) {
487 a[j].maximumSize = 0;
489 a[j].minimumSize = pick(qSmartMinSize(s->widget));
490 a[j].maximumSize = pick(qSmartMaxSize(s->widget));
493 bool stretch = noStretchFactorsSet;
495 QSizePolicy p = s->widget->sizePolicy();
496 int sf = orient == Qt::Horizontal ? p.horizontalStretch() : p.verticalStretch();
500 a[j].stretch = s->getWidgetSize(orient);
501 a[j].sizeHint = a[j].minimumSize;
502 a[j].expansive =
true;
504 a[j].sizeHint = qMax(s->getWidgetSize(orient), a[j].minimumSize);
510 qGeomCalc(a, 0, n*2, pick(r.topLeft()), pick(r.size()), 0);
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",
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);
541void QSplitterPrivate::addContribution(
int index,
int *min,
int *max,
bool mayCollapse)
const
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);
549 if (mayCollapse || !s->collapsed)
550 *min += pick(qSmartMinSize(s->widget));
552 *max += pick(qSmartMaxSize(s->widget));
556int QSplitterPrivate::findWidgetJustBeforeOrJustAfter(
int index,
int delta,
int &collapsibleSize)
const
561 QWidget *w = list.at(index)->widget;
562 if (!w->isHidden()) {
563 if (collapsible(list.at(index)))
564 collapsibleSize = pick(qSmartMinSize(w));
568 }
while (index >= 0 && index < list.size());
577void QSplitterPrivate::getRange(
int index,
int *farMin,
int *min,
int *max,
int *farMax)
const
579 Q_Q(
const QSplitter);
581 if (index <= 0 || index >= n)
584 int collapsibleSizeBefore = 0;
585 int idJustBefore = findWidgetJustBeforeOrJustAfter(index, -1, collapsibleSizeBefore);
587 int collapsibleSizeAfter = 0;
588 int idJustAfter = findWidgetJustBeforeOrJustAfter(index, +1, collapsibleSizeAfter);
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);
601 QRect r = q->contentsRect();
607 int smartMinBefore = qMax(minBefore, pick(r.size()) - maxAfter);
608 int smartMaxBefore = qMin(maxBefore, pick(r.size()) - minAfter);
610 minVal = pick(r.topLeft()) + smartMinBefore;
611 maxVal = pick(r.topLeft()) + smartMaxBefore;
614 if (minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter)
615 farMinVal -= collapsibleSizeBefore;
617 if (pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore)
618 farMaxVal += collapsibleSizeAfter;
630int QSplitterPrivate::adjustPos(
int pos,
int index,
int *farMin,
int *min,
int *max,
int *farMax)
const
632 const int Threshold = 40;
634 getRange(index, farMin, min, max, farMax);
640 int delta = pos - *max;
641 int width = *farMax - *max;
643 if (delta > width / 2 && delta >= qMin(Threshold, width)) {
650 int delta = *min - pos;
651 int width = *min - *farMin;
653 if (delta > width / 2 && delta >= qMin(Threshold, width)) {
676void QSplitterPrivate::setSizes_helper(
const QList<
int> &sizes,
bool clampNegativeSize)
680 for (
int i = 0; i < list.size(); ++i) {
681 QSplitterLayoutStruct *s = list.at(i);
683 s->collapsed =
false;
684 s->sizer = sizes.value(j++);
685 if (clampNegativeSize && s->sizer < 0)
687 int smartMinSize = pick(qSmartMinSize(s->widget));
691 if (collapsible(s) && smartMinSize > 0) {
694 s->sizer = smartMinSize;
697 if (s->sizer < smartMinSize)
698 s->sizer = smartMinSize;
715void QSplitterPrivate::setGeo(QSplitterLayoutStruct *sls,
int p,
int s,
bool allowCollapse)
718 QWidget *w = sls->widget;
720 QRect contents = q->contentsRect();
721 if (orient == Qt::Horizontal) {
722 r.setRect(p, contents.y(), s, contents.height());
724 r.setRect(contents.x(), p, contents.width(), s);
728 int minSize = pick(qSmartMinSize(w));
730 if (orient == Qt::Horizontal && q->isRightToLeft())
731 r.moveRight(contents.width() - r.left());
734 sls->collapsed = s <= 0 && minSize > 0 && !w->isHidden();
739 r.moveTopLeft(QPoint(-r.width()-1, -r.height()-1));
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());
752 h->setGeometry(contents.x(), p-hs.height() - m.top(), contents.width(), hs.height() + m.top() + m.bottom());
757void QSplitterPrivate::doMove(
bool backwards,
int hPos,
int index,
int delta,
bool mayCollapse,
758 int *positions,
int *widths)
760 if (index < 0 || index >= list.size())
763#ifdef QSPLITTER_DEBUG
764 qDebug() <<
"QSplitterPrivate::doMove" << backwards << hPos << index << delta << mayCollapse;
767 QSplitterLayoutStruct *s = list.at(index);
768 QWidget *w = s->widget;
770 int nextId = backwards ? index - delta : index + delta;
773 doMove(backwards, hPos, nextId, delta, collapsible(nextId), positions, widths);
775 int hs =s->handle->isHidden() ? 0 : s->getHandleSize(orient);
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)));
785 positions[index] = backwards ? hPos - ws : hPos + hs;
787 doMove(backwards, backwards ? hPos - ws - hs : hPos + hs + ws, nextId, delta,
788 collapsible(nextId), positions, widths);
806void QSplitterPrivate::insertWidget_helper(
int index, QWidget *widget,
bool show)
809 QScopedValueRollback b(blockChildAdd,
true);
810 const bool needShow = show && shouldShowWidget(widget);
811 if (widget->parentWidget() != q)
812 widget->setParent(q);
815 insertWidget(index, widget);
816 recalc(q->isVisible());
825QSplitterLayoutStruct *QSplitterPrivate::insertWidget(
int index, QWidget *w)
828 QSplitterLayoutStruct *sls =
nullptr;
830 int last = list.size();
831 for (i = 0; i < list.size(); ++i) {
832 QSplitterLayoutStruct *s = list.at(i);
833 if (s->widget == w) {
839 if (index < 0 || index > last)
845 sls =
new QSplitterLayoutStruct;
846 QSplitterHandle *newHandle = q->createHandle();
847 newHandle->setObjectName(
"qt_splithandle_"_L1 + w->objectName());
848 sls->handle = newHandle;
851 list.insert(index,sls);
853 if (newHandle && q->isVisible())
974void QSplitter::setOrientation(Qt::Orientation orientation)
977 if (d->orient == orientation)
980 if (!testAttribute(Qt::WA_WState_OwnSizePolicy)) {
981 setSizePolicy(sizePolicy().transposed());
982 setAttribute(Qt::WA_WState_OwnSizePolicy,
false);
985 d->orient = orientation;
987 for (
int i = 0; i < d->list.size(); ++i) {
988 QSplitterLayoutStruct *s = d->list.at(i);
989 s->handle->setOrientation(orientation);
991 d->recalc(isVisible());
1124QWidget *QSplitter::replaceWidget(
int index, QWidget *widget)
1128 qWarning(
"QSplitter::replaceWidget: Widget can't be null");
1132 if (index < 0 || index >= d->list.size()) {
1133 qWarning(
"QSplitter::replaceWidget: Index %d out of range", index);
1137 QSplitterLayoutStruct *s = d->list.at(index);
1138 QWidget *current = s->widget;
1139 if (current == widget) {
1140 qWarning(
"QSplitter::replaceWidget: Trying to replace a widget with itself");
1144 if (widget->parentWidget() ==
this) {
1145 qWarning(
"QSplitter::replaceWidget: Trying to replace a widget with one of its siblings");
1149 QScopedValueRollback b(d->blockChildAdd,
true);
1151 const QRect geom = current->geometry();
1152 const bool wasHidden = current->isHidden();
1155 current->setParent(
nullptr);
1156 widget->setParent(
this);
1160 widget->setGeometry(geom);
1164 else if (d->shouldShowWidget(widget))
1263void QSplitter::childEvent(QChildEvent *c)
1267 if (!c->child()->isWidgetType()) {
1268 if (Q_UNLIKELY(qobject_cast<QLayout *>(c->child())))
1269 qWarning(
"Adding a QLayout to a QSplitter is not supported.");
1272 QWidget *w =
static_cast<QWidget*>(c->child());
1273 if (!d->blockChildAdd && !w->isWindow() && !d->findWidget(w))
1274 d->insertWidget_helper(d->list.size(), w,
false);
1275 }
else if (c->polished()) {
1276 if (!c->child()->isWidgetType())
1278 QWidget *w =
static_cast<QWidget*>(c->child());
1279 if (!d->blockChildAdd && !w->isWindow() && d->shouldShowWidget(w))
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);
1288 d->recalc(isVisible());
1301void QSplitter::setRubberBand(
int pos)
1303#if QT_CONFIG(rubberband)
1307 d->rubberBand->deleteLater();
1310 QRect r = contentsRect();
1311 const int rBord = 3;
1312 int hw = handleWidth();
1313 if (!d->rubberBand) {
1314 QScopedValueRollback b(d->blockChildAdd,
true);
1315 d->rubberBand =
new QRubberBand(QRubberBand::Line,
this);
1317 d->rubberBand->setObjectName(
"qt_rubberband"_L1);
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();
1382void QSplitter::moveSplitter(
int pos,
int index)
1385 QSplitterLayoutStruct *s = d->list.at(index);
1391#ifdef QSPLITTER_DEBUG
1395 pos = d->adjustPos(pos, index, &farMin, &min, &max, &farMax);
1396 int oldP = d->pick(s->rect.topLeft());
1397#ifdef QSPLITTER_DEBUG
1398 qDebug() <<
"QSplitter::moveSplitter" << debugp << index <<
"adjusted" << pos <<
"oldP" << oldP;
1401 QVarLengthArray<
int, 32> poss(d->list.size());
1402 QVarLengthArray<
int, 32> ws(d->list.size());
1405 d->doMove(
false, pos, index, +1, (d->collapsible(s) && (pos > max)), poss.data(), ws.data());
1406 d->doMove(
true, pos, index - 1, +1, (d->collapsible(index - 1) && (pos < min)), poss.data(), ws.data());
1407 upLeft = (pos < oldP);
1409 int wid, delta, count = d->list.size();
1417 for (; wid >= 0 && wid < count; wid += delta) {
1418 QSplitterLayoutStruct *sls = d->list.at( wid );
1419 if (!sls->widget->isHidden())
1420 d->setGeo(sls, poss[wid], ws[wid],
true);
1424 emit splitterMoved(pos, index);
1490QSize QSplitter::sizeHint()
const
1492 Q_D(
const QSplitter);
1496 for (
int i = 0; i < d->list.size(); ++i) {
1497 QWidget *w = d->list.at(i)->widget;
1500 QSize s = w->sizeHint();
1503 t = qMax(t, d->trans(s));
1506 return orientation() == Qt::Horizontal ? QSize(l, t) : QSize(t, l);
1514QSize QSplitter::minimumSizeHint()
const
1516 Q_D(
const QSplitter);
1521 for (
int i = 0; i < d->list.size(); ++i) {
1522 QSplitterLayoutStruct *s = d->list.at(i);
1523 if (!s || !s->widget)
1525 if (s->widget->isHidden())
1527 QSize widgetSize = qSmartMinSize(s->widget);
1528 if (widgetSize.isValid()) {
1529 l += d->pick(widgetSize);
1530 t = qMax(t, d->trans(widgetSize));
1532 if (!s->handle || s->handle->isHidden())
1534 QSize splitterSize = s->handle->sizeHint();
1535 if (splitterSize.isValid()) {
1536 l += d->pick(splitterSize);
1537 t = qMax(t, d->trans(splitterSize));
1540 return orientation() == Qt::Horizontal ? QSize(l, t) : QSize(t, l);
1655QByteArray QSplitter::saveState()
const
1657 Q_D(
const QSplitter);
1660 QDataStream stream(&data, QIODevice::WriteOnly);
1661 stream.setVersion(QDataStream::Qt_5_0);
1663 stream << qint32(SplitterMagic);
1664 stream << qint32(version);
1665 const int numSizes = d->list.size();
1667 list.reserve(numSizes);
1668 for (
int i = 0; i < numSizes; ++i) {
1669 QSplitterLayoutStruct *s = d->list.at(i);
1670 list.append(s->sizer);
1673 stream << childrenCollapsible();
1674 stream << qint32(d->handleWidth);
1675 stream << opaqueResize();
1676 stream << qint32(orientation());
1677 stream << d->opaqueResizeSet;
1697bool QSplitter::restoreState(
const QByteArray &state)
1701 QByteArray sd = state;
1702 QDataStream stream(&sd, QIODevice::ReadOnly);
1703 stream.setVersion(QDataStream::Qt_5_0);
1712 if (marker != SplitterMagic || v > version)
1716 d->setSizes_helper(list,
false);
1719 setChildrenCollapsible(b);
1728 setOrientation(Qt::Orientation(i));
1732 stream >> d->opaqueResizeSet;
1751void QSplitter::setStretchFactor(
int index,
int stretch)
1754 if (index <= -1 || index >= d->list.size())
1757 QWidget *widget = d->list.at(index)->widget;
1758 QSizePolicy sp = widget->sizePolicy();
1759 sp.setHorizontalStretch(stretch);
1760 sp.setVerticalStretch(stretch);
1761 widget->setSizePolicy(sp);