13 #include "Separator_p.h"
16 #include "ItemFreeContainer_p.h"
20 #include <QScopedValueRollback>
22 #include <QGuiApplication>
28 #pragma warning(disable : 4138)
29 #pragma warning(disable : 4244)
30 #pragma warning(disable : 4457)
31 #pragma warning(disable : 4702)
39 int Layouting::Item::separatorThickness = 5;
42 QSize Layouting::Item::hardcodedMinimumSize =
QSize(80, 90);
43 QSize Layouting::Item::hardcodedMaximumSize =
QSize(16777215, 16777215);
45 bool Layouting::ItemBoxContainer::s_inhibitSimplify =
false;
97 return qMax(0, length - minLength);
102 return qMax(0, minLength - length);
110 return m_parent ? m_parent->root()
111 :
const_cast<ItemBoxContainer *
>(qobject_cast<const ItemBoxContainer *>(
this));
126 return p + parentContainer()->mapToRoot(pos());
132 return mapToRoot(
QPoint(0, p)).
y();
133 return mapToRoot(
QPoint(p, 0)).x();
138 const Item *it =
this;
141 it = it->parentContainer();
165 return mapFromRoot(
QPoint(0, p)).y();
166 return mapFromRoot(
QPoint(p, 0)).x();
169 QObject *Item::guestAsQObject()
const
171 return m_guest ? m_guest->asQObject() :
nullptr;
174 void Item::setGuestWidget(
Widget *guest)
176 Q_ASSERT(!guest || !m_guest);
178 QObject *oldWidget = guestAsQObject();
182 disconnect(oldWidget,
nullptr,
this,
nullptr);
189 m_guest->setLayoutItem(
this);
200 connect(newWidget, SIGNAL(layoutInvalidated()),
this, SLOT(onWidgetLayoutRequested()));
202 if (m_sizingInfo.geometry.isEmpty()) {
204 QRect widgetGeo = m_guest->geometry();
206 widgetGeo.
size().
expandedTo(minSize()).expandedTo(Item::hardcodedMinimumSize));
207 setGeometry(mapFromRoot(widgetGeo));
209 updateWidgetGeometries();
216 void Item::updateWidgetGeometries()
219 m_guest->setGeometry(mapToRoot(rect()));
223 QVariantMap Item::toVariantMap()
const
227 result[QStringLiteral(
"sizingInfo")] = m_sizingInfo.toVariantMap();
228 result[QStringLiteral(
"isVisible")] = m_isVisible;
229 result[QStringLiteral(
"isContainer")] = isContainer();
230 result[QStringLiteral(
"objectName")] = objectName();
232 result[QStringLiteral(
"guestId")] = m_guest->id();
239 m_sizingInfo.fromVariantMap(map[QStringLiteral(
"sizingInfo")].toMap());
240 m_isVisible = map[QStringLiteral(
"isVisible")].toBool();
241 setObjectName(map[QStringLiteral(
"objectName")].toString());
243 const QString guestId = map.value(QStringLiteral(
"guestId")).toString();
246 setGuestWidget(guest);
247 m_guest->setParent(hostWidget());
248 }
else if (hostWidget()) {
249 qWarning() << Q_FUNC_INFO <<
"Couldn't find frame to restore for" <<
this;
257 auto item =
new Item(hostWidget, parent);
258 item->fillFromVariantMap(map, widgets);
269 Q_ASSERT(m_refCount > 0);
271 if (m_refCount == 0) {
273 parentContainer()->removeItem(
this);
277 int Item::refCount()
const
282 Widget *Item::hostWidget()
const
289 return m_hostWidget ? m_hostWidget->asQObject()
293 void Item::restore(
Widget *guest)
295 if (isVisible() || guestAsQObject()) {
296 qWarning() << Q_FUNC_INFO <<
"Hitting assert. visible="
297 << isVisible() <<
"; guest=" << guestAsQObject();
302 qWarning() << Q_FUNC_INFO <<
"Containers can't be restored";
304 setGuestWidget(guest);
305 parentContainer()->restore(
this);
324 const Item *it =
this;
326 if (
auto p = it->parentContainer()) {
327 const auto index = p->childItems().indexOf(
const_cast<Item *
>(it));
338 void Item::setHostWidget(
Widget *host)
340 if (m_hostWidget != host) {
344 m_guest->setVisible(
true);
345 updateWidgetGeometries();
350 void Item::setSize_recursive(
QSize newSize, ChildrenResizeStrategy)
355 QSize Item::missingSize()
const
357 QSize missing = minSize() - this->size();
364 bool Item::isBeingInserted()
const
366 return m_sizingInfo.isBeingInserted;
369 void Item::setBeingInserted(
bool is)
371 m_sizingInfo.isBeingInserted = is;
374 if (
auto parent = parentContainer()) {
376 if (!parent->hasVisibleChildren())
377 parent->setBeingInserted(
true);
379 parent->setBeingInserted(
false);
386 if (parent == m_parent)
390 disconnect(
this, &Item::minSizeChanged, m_parent, &ItemContainer::onChildMinSizeChanged);
391 disconnect(
this, &Item::visibleChanged, m_parent, &ItemContainer::onChildVisibleChanged);
392 Q_EMIT visibleChanged(
this,
false);
395 if (
auto c = asContainer()) {
396 const bool ceasingToBeRoot = !m_parent && parent;
397 if (ceasingToBeRoot && !c->hasVisibleChildren()) {
405 connectParent(parent);
413 connect(
this, &Item::minSizeChanged, parent, &ItemContainer::onChildMinSizeChanged);
414 connect(
this, &Item::visibleChanged, parent, &ItemContainer::onChildVisibleChanged);
416 setHostWidget(parent->hostWidget());
417 updateWidgetGeometries();
419 Q_EMIT visibleChanged(
this, isVisible());
430 return qobject_cast<ItemBoxContainer *>(m_parent);
435 return qobject_cast<const ItemContainer *>(
this);
440 return qobject_cast<ItemContainer *>(
this);
445 return qobject_cast<ItemBoxContainer *>(
this);
448 void Item::setMinSize(
QSize sz)
450 if (sz != m_sizingInfo.minSize) {
451 m_sizingInfo.minSize = sz;
452 Q_EMIT minSizeChanged(
this);
453 if (!m_isSettingGuest)
454 setSize_recursive(size().expandedTo(sz));
458 void Item::setMaxSizeHint(
QSize sz)
460 if (sz != m_sizingInfo.maxSizeHint) {
461 m_sizingInfo.maxSizeHint = sz;
462 Q_EMIT maxSizeChanged(
this);
466 QSize Item::minSize()
const
468 return m_sizingInfo.minSize;
471 QSize Item::maxSizeHint()
const
473 return m_sizingInfo.maxSizeHint.
boundedTo(hardcodedMaximumSize);
476 void Item::setPos(
QPoint pos)
478 QRect geo = m_sizingInfo.geometry;
486 setPos({ x(), pos });
488 setPos({ pos, y() });
499 return m_sizingInfo.geometry.x();
504 return m_sizingInfo.geometry.y();
507 int Item::width()
const
509 return m_sizingInfo.geometry.width();
512 int Item::height()
const
514 return m_sizingInfo.geometry.height();
517 QSize Item::size()
const
519 return m_sizingInfo.geometry.size();
522 void Item::setSize(
QSize sz)
524 QRect newGeo = m_sizingInfo.geometry;
531 return m_sizingInfo.geometry.topLeft();
534 QRect Item::geometry()
const
536 return isBeingInserted() ?
QRect()
537 : m_sizingInfo.geometry;
540 QRect Item::rect()
const
542 return QRect(0, 0, width(), height());
545 bool Item::isContainer()
const
547 return m_isContainer;
552 return Layouting::length(minSize(), o);
557 return Layouting::length(maxSizeHint(), o);
562 Q_ASSERT(length > 0);
564 const int w = qMax(width(), hardcodedMinimumSize.width());
565 setSize(
QSize(w, length));
567 const int h = qMax(height(), hardcodedMinimumSize.height());
568 setSize(
QSize(length, h));
574 setLength(length, o);
579 return Layouting::length(size(), o);
584 return length(o) - minLength(o);
587 bool Item::isPlaceholder()
const
592 bool Item::isVisible(
bool excludeBeingInserted)
const
594 return m_isVisible && !(excludeBeingInserted && isBeingInserted());
597 void Item::setIsVisible(
bool is)
599 if (is != m_isVisible) {
601 Q_EMIT visibleChanged(
this, is);
605 m_guest->setGeometry(mapToRoot(rect()));
606 m_guest->setVisible(
true);
612 void Item::setGeometry_recursive(
QRect rect)
618 bool Item::checkSanity()
623 if (minSize().width() > width() || minSize().height() > height()) {
624 root()->dumpLayout();
625 qWarning() << Q_FUNC_INFO <<
"Size constraints not honoured" <<
this
626 <<
"; min=" << minSize() <<
"; size=" << size();
631 if (m_guest->parent() != hostWidget()->asQObject()) {
633 root()->dumpLayout();
634 qWarning() << Q_FUNC_INFO <<
"Unexpected parent for our guest. guest.parent="
635 << m_guest->parent() <<
"; host=" << hostWidget()->
asQObject()
636 <<
"; guest.asObj=" << m_guest->asQObject()
638 <<
"; item.parentContainer=" << parentContainer()
639 <<
"; item.root.parent=" << (root() ? root()->parent() :
nullptr);
643 if (
false && !m_guest->isVisible() && (!m_guest->parent() || m_guest->parentWidget()->isVisible())) {
645 qWarning() << Q_FUNC_INFO <<
"Guest widget isn't visible" <<
this
646 << m_guest->asQObject();
650 if (m_guest->geometry() != mapToRoot(rect())) {
651 root()->dumpLayout();
653 d << Q_FUNC_INFO <<
"Guest widget doesn't have correct geometry. has"
654 <<
"guest.global=" << m_guest->geometry()
655 <<
"; item.local=" << geometry()
656 <<
"; item.global=" << mapToRoot(rect())
658 m_guest->dumpDebug(d);
667 bool Item::isMDI()
const
669 return qobject_cast<ItemFreeContainer *>(parentContainer()) !=
nullptr;
672 void Item::setGeometry(
QRect rect)
674 QRect &m_geometry = m_sizingInfo.geometry;
676 if (rect != m_geometry) {
677 const QRect oldGeo = m_geometry;
685 if (c->hasVisibleChildren()) {
691 qWarning() << Q_FUNC_INFO <<
"Empty rect";
695 const QSize minSz = minSize();
699 qWarning() << Q_FUNC_INFO <<
this <<
"Constraints not honoured."
700 <<
"sz=" << rect.
size() <<
"; min=" << minSz
701 <<
": parent=" << parentContainer();
704 Q_EMIT geometryChanged();
706 if (oldGeo.
x() != x())
708 if (oldGeo.
y() != y())
710 if (oldGeo.
width() != width())
711 Q_EMIT widthChanged();
712 if (oldGeo.
height() != height())
713 Q_EMIT heightChanged();
715 updateWidgetGeometries();
719 void Item::dumpLayout(
int level)
724 auto dbg = qDebug().noquote();
726 dbg << indent <<
"- Widget: " << objectName()
727 << m_sizingInfo.geometry
728 <<
"; min=" << minSize();
730 if (maxSizeHint() != hardcodedMaximumSize)
731 dbg <<
"; max=" << maxSizeHint();
734 dbg << QStringLiteral(
";hidden;");
736 if (m_guest && geometry() != m_guest->geometry()) {
737 dbg <<
"; guest geometry=" << m_guest->geometry();
740 if (m_sizingInfo.isBeingInserted)
741 dbg << QStringLiteral(
";beingInserted;");
743 dbg <<
this <<
"; guest=" << guestAsQObject();
748 , m_isContainer(false)
750 , m_hostWidget(hostWidget)
752 connectParent(parent);
757 , m_isContainer(isContainer)
759 , m_hostWidget(hostWidget)
761 connectParent(parent);
774 if (widget->
parent() != host) {
776 Q_ASSERT(isVisible());
777 turnIntoPlaceholder();
784 void Item::turnIntoPlaceholder()
786 Q_ASSERT(!isContainer());
791 parentContainer()->removeItem(
this,
false);
794 void Item::updateObjectName()
799 if (
auto w = guestAsQObject()) {
800 setObjectName(w->objectName().isEmpty() ? QStringLiteral(
"widget") : w->objectName());
801 }
else if (!isVisible()) {
802 setObjectName(QStringLiteral(
"hidden"));
803 }
else if (!m_guest) {
804 setObjectName(QStringLiteral(
"null"));
806 setObjectName(QStringLiteral(
"empty"));
810 void Item::onWidgetDestroyed()
815 turnIntoPlaceholder();
816 }
else if (!isRoot()) {
817 parentContainer()->removeItem(
this);
821 void Item::onWidgetLayoutRequested()
823 if (
Widget *w = guestWidget()) {
824 if (w->size() != size() && !isMDI()) {
825 qDebug() << Q_FUNC_INFO <<
"TODO: Not implemented yet. Widget can't just decide to resize yet"
828 << m_sizingInfo.geometry
829 << m_sizingInfo.isBeingInserted;
832 if (w->minSize() != minSize()) {
833 setMinSize(m_guest->minSize());
836 setMaxSizeHint(w->maxSizeHint());
840 bool Item::isRoot()
const
842 return m_parent ==
nullptr;
845 LayoutBorderLocations Item::adjacentLayoutBorders()
const
848 return LayoutBorderLocation_All;
853 return LayoutBorderLocation_None;
855 const int indexInParent = c->indexOfVisibleChild(
this);
856 const int numVisibleChildren = c->numVisibleChildren();
857 const bool isFirst = indexInParent == 0;
858 const bool isLast = indexInParent == numVisibleChildren - 1;
859 if (indexInParent == -1)
860 return LayoutBorderLocation_None;
862 LayoutBorderLocations locations = LayoutBorderLocation_None;
864 if (c->isVertical()) {
865 locations |= LayoutBorderLocation_West;
866 locations |= LayoutBorderLocation_East;
869 locations |= LayoutBorderLocation_North;
871 locations |= LayoutBorderLocation_South;
873 locations |= LayoutBorderLocation_North;
874 locations |= LayoutBorderLocation_South;
877 locations |= LayoutBorderLocation_West;
879 locations |= LayoutBorderLocation_East;
882 const LayoutBorderLocations parentBorders = c->adjacentLayoutBorders();
883 if (c->isVertical()) {
884 if (parentBorders & LayoutBorderLocation_West)
885 locations |= LayoutBorderLocation_West;
887 if (parentBorders & LayoutBorderLocation_East)
888 locations |= LayoutBorderLocation_East;
890 if (isFirst && (parentBorders & LayoutBorderLocation_North))
891 locations |= LayoutBorderLocation_North;
893 if (isLast && (parentBorders & LayoutBorderLocation_South))
894 locations |= LayoutBorderLocation_South;
897 if (parentBorders & LayoutBorderLocation_North)
898 locations |= LayoutBorderLocation_North;
900 if (parentBorders & LayoutBorderLocation_South)
901 locations |= LayoutBorderLocation_South;
903 if (isFirst && (parentBorders & LayoutBorderLocation_West))
904 locations |= LayoutBorderLocation_West;
906 if (isLast && (parentBorders & LayoutBorderLocation_East))
907 locations |= LayoutBorderLocation_East;
914 int Item::visibleCount_recursive()
const
916 return isVisible() ? 1 : 0;
919 struct ItemBoxContainer::Private
928 qDeleteAll(m_separators);
929 m_separators.clear();
932 int defaultLengthFor(Item *item,
InitialOption option)
const;
933 bool isOverflowing()
const;
934 void relayoutIfNeeded();
935 const Item *itemFromPath(
const QVector<int> &path)
const;
936 void resizeChildren(
QSize oldSize,
QSize newSize, SizingInfo::List &sizes, ChildrenResizeStrategy);
937 void honourMaxSizes(SizingInfo::List &sizes);
938 void scheduleCheckSanity()
const;
941 void updateWidgets_recursive();
944 void updateSeparators();
945 void deleteSeparators();
948 bool isDummy()
const;
949 void deleteSeparators_recursive();
950 void updateSeparators_recursive();
951 QSize minSize(
const Item::List &items)
const;
952 int excessLength()
const;
954 mutable bool m_checkSanityScheduled =
false;
956 bool m_convertingItemToContainer =
false;
957 bool m_blockUpdatePercentages =
false;
958 bool m_isDeserializing =
false;
959 bool m_isSimplifying =
false;
966 , d(new Private(this))
971 ItemBoxContainer::ItemBoxContainer(
Widget *hostWidget)
973 , d(new Private(this))
977 ItemBoxContainer::~ItemBoxContainer()
985 if (d->m_orientation == o) {
987 for (Item *child : m_children) {
989 num += container->numSideBySide_recursive(o);
990 }
else if (!child->isPlaceholder()) {
996 for (Item *child : m_children) {
998 num = qMax(num, container->numSideBySide_recursive(o));
999 }
else if (!child->isPlaceholder()) {
1008 bool ItemBoxContainer::checkSanity()
1010 d->m_checkSanityScheduled =
false;
1012 if (!hostWidget()) {
1017 if (!Item::checkSanity())
1020 if (numChildren() == 0 && !isRoot()) {
1021 qWarning() << Q_FUNC_INFO <<
"Container is empty. Should be deleted";
1026 qWarning() << Q_FUNC_INFO <<
"Invalid orientation" << d->m_orientation <<
this;
1031 int expectedPos = 0;
1032 const auto children = childItems();
1033 for (Item *item : children) {
1034 if (!item->isVisible())
1036 const int pos = Layouting::pos(item->pos(), d->m_orientation);
1037 if (expectedPos != pos) {
1038 root()->dumpLayout();
1039 qWarning() << Q_FUNC_INFO <<
"Unexpected pos" << pos <<
"; expected=" << expectedPos
1040 <<
"; for item=" << item
1041 <<
"; isContainer=" << item->isContainer();
1045 expectedPos = pos + Layouting::length(item->size(), d->m_orientation) + separatorThickness;
1049 for (Item *item : children) {
1050 if (item->parentContainer() !=
this) {
1051 qWarning() <<
"Invalid parent container for" << item
1052 <<
"; is=" << item->parentContainer() <<
"; expected=" <<
this;
1056 if (item->parent() !=
this) {
1057 qWarning() <<
"Invalid QObject parent for" << item
1058 <<
"; is=" << item->parent() <<
"; expected=" <<
this;
1062 if (item->isVisible()) {
1066 root()->dumpLayout();
1067 qWarning() << Q_FUNC_INFO <<
"Invalid size for item." << item
1068 <<
"Container.length=" << h1 <<
"; item.length=" << h2;
1072 if (!rect().contains(item->geometry())) {
1073 root()->dumpLayout();
1074 qWarning() << Q_FUNC_INFO <<
"Item geo is out of bounds. item=" << item <<
"; geo="
1075 << item->geometry() <<
"; parent.rect=" << rect();
1080 if (!item->checkSanity())
1084 const Item::List visibleChildren = this->visibleChildren();
1085 const bool isEmptyRoot = isRoot() && visibleChildren.isEmpty();
1087 auto occupied = qMax(0, Item::separatorThickness * (visibleChildren.size() - 1));
1088 for (Item *item : visibleChildren) {
1089 occupied += item->length(d->m_orientation);
1092 if (occupied != length()) {
1093 root()->dumpLayout();
1094 qWarning() << Q_FUNC_INFO <<
"Unexpected length. Expected=" << occupied
1095 <<
"; got=" << length() <<
"; this=" <<
this;
1100 const double totalPercentage = std::accumulate(percentages.
begin(), percentages.
end(), 0.0);
1101 const double expectedPercentage = visibleChildren.isEmpty() ? 0.0 : 1.0;
1102 if (!qFuzzyCompare(totalPercentage, expectedPercentage)) {
1103 root()->dumpLayout();
1104 qWarning() << Q_FUNC_INFO <<
"Percentages don't add up"
1105 << totalPercentage << percentages
1108 qWarning() << Q_FUNC_INFO << d->childPercentages();
1113 const auto numVisibleChildren = visibleChildren.size();
1114 if (d->m_separators.size() != qMax(0, numVisibleChildren - 1)) {
1115 root()->dumpLayout();
1116 qWarning() << Q_FUNC_INFO <<
"Unexpected number of separators" << d->m_separators.size()
1117 << numVisibleChildren;
1121 const QSize expectedSeparatorSize = isVertical() ?
QSize(width(), Item::separatorThickness)
1122 :
QSize(Item::separatorThickness, height());
1126 for (
int i = 0; i < d->m_separators.size(); ++i) {
1127 Separator *separator = d->m_separators.at(i);
1128 Item *item = visibleChildren.at(i);
1129 const int expectedSeparatorPos = mapToRoot(item->m_sizingInfo.edge(d->m_orientation) + 1, d->m_orientation);
1131 if (separator->host() != host()) {
1132 qWarning() << Q_FUNC_INFO <<
"Invalid host widget for separator"
1133 << separator->host() << host() <<
this;
1137 if (separator->parentContainer() !=
this) {
1138 qWarning() << Q_FUNC_INFO <<
"Invalid parent container for separator"
1139 << separator->parentContainer() << separator <<
this;
1143 if (separator->position() != expectedSeparatorPos) {
1144 root()->dumpLayout();
1145 qWarning() << Q_FUNC_INFO <<
"Unexpected separator position" << separator->position()
1146 <<
"; expected=" << expectedSeparatorPos
1147 << separator <<
"; this=" <<
this;
1151 Widget *separatorWidget = separator->asWidget();
1152 if (separatorWidget->
geometry().
size() != expectedSeparatorSize) {
1153 qWarning() << Q_FUNC_INFO <<
"Unexpected separator size" << separatorWidget->
geometry().
size()
1154 <<
"; expected=" << expectedSeparatorSize
1155 << separator <<
"; this=" <<
this;
1161 root()->dumpLayout();
1162 qWarning() << Q_FUNC_INFO <<
"Unexpected position pos2=" << separatorPos2
1163 <<
"; expected=" << pos2
1164 << separator <<
"; this=" <<
this;
1168 if (separator->host() != host()) {
1169 qWarning() << Q_FUNC_INFO <<
"Unexpected host widget in separator"
1170 << separator->host() <<
"; expected=" << host();
1175 const int separatorMinPos = minPosForSeparator_global(separator,
false);
1176 const int separatorMaxPos = maxPosForSeparator_global(separator,
false);
1177 const int separatorPos = separator->position();
1178 if (separatorPos < separatorMinPos || separatorPos > separatorMaxPos || separatorMinPos < 0 || separatorMaxPos <= 0) {
1179 root()->dumpLayout();
1180 qWarning() << Q_FUNC_INFO <<
"Invalid bounds for separator, pos="
1181 << separatorPos <<
"; min=" << separatorMinPos
1182 <<
"; max=" << separatorMaxPos
1188 #ifdef DOCKS_DEVELOPER_MODE
1191 if (!asBoxContainer()->test_suggestedRect())
1199 void ItemBoxContainer::Private::scheduleCheckSanity()
const
1201 if (!m_checkSanityScheduled) {
1202 m_checkSanityScheduled =
true;
1207 bool ItemBoxContainer::hasOrientation()
const
1209 return isVertical() || isHorizontal();
1212 int ItemBoxContainer::indexOfVisibleChild(
const Item *item)
const
1214 const Item::List items = visibleChildren();
1215 return items.indexOf(
const_cast<Item *
>(item));
1218 void ItemBoxContainer::restore(Item *child)
1220 restoreChild(child, NeighbourSqueezeStrategy::ImmediateNeighboursFirst);
1223 void ItemBoxContainer::removeItem(Item *item,
bool hardRemove)
1225 Q_ASSERT(!item->isRoot());
1227 if (!contains(item)) {
1229 item->parentContainer()->removeItem(item, hardRemove);
1233 Item *side1Item = visibleNeighbourFor(item, Side1);
1234 Item *side2Item = visibleNeighbourFor(item, Side2);
1236 const bool isContainer = item->isContainer();
1237 const bool wasVisible = !isContainer && item->isVisible();
1240 m_children.removeOne(item);
1243 Q_EMIT root()->numItemsChanged();
1245 item->setIsVisible(
false);
1246 item->setGuestWidget(
nullptr);
1248 if (!wasVisible && !isContainer) {
1255 Q_EMIT root()->numVisibleItemsChanged(root()->numVisibleChildren());
1260 if (
auto p = parentContainer())
1261 p->removeItem(
this,
true);
1262 }
else if (!hardRemove && !hasVisibleChildren()) {
1263 if (
auto p = parentContainer()) {
1264 p->removeItem(
this,
false);
1265 setGeometry(
QRect());
1269 growNeighbours(side1Item, side2Item);
1270 Q_EMIT itemsChanged();
1272 updateSizeConstraints();
1273 d->updateSeparators_recursive();
1277 void ItemBoxContainer::setGeometry_recursive(
QRect rect)
1282 setSize_recursive(rect.
size());
1289 const auto index = m_children.indexOf(leaf);
1290 Q_ASSERT(index != -1);
1292 container->setParentContainer(
nullptr);
1293 container->setParentContainer(
this);
1295 insertItem(container, index, DefaultSizeMode::NoDefaultSizeMode);
1296 m_children.removeOne(leaf);
1297 container->setGeometry(leaf->geometry());
1298 container->insertItem(leaf,
Location_OnTop, DefaultSizeMode::NoDefaultSizeMode);
1299 Q_EMIT itemsChanged();
1300 d->updateSeparators_recursive();
1306 void ItemBoxContainer::insertItemRelativeTo(Item *item, Item *relativeTo,
1309 Q_ASSERT(item != relativeTo);
1311 if (
auto asContainer = relativeTo->asBoxContainer()) {
1312 asContainer->insertItem(item, loc, option);
1317 Q_ASSERT(!(option.
startsHidden() && item->isContainer()));
1321 qWarning() << Q_FUNC_INFO <<
"This method should only be called for box containers"
1326 if (parent->hasOrientationFor(loc)) {
1328 auto indexInParent = parent->childItems().indexOf(relativeTo);
1333 if (orientation != parent->orientation()) {
1334 Q_ASSERT(parent->visibleChildren().size() == 1);
1337 parent->setOrientation(orientation);
1340 parent->insertItem(item, indexInParent, option);
1343 container->insertItem(item, loc, option);
1347 void ItemBoxContainer::insertItem(Item *item,
Location loc,
1350 Q_ASSERT(item !=
this);
1351 if (contains(item)) {
1352 qWarning() << Q_FUNC_INFO <<
"Item already exists";
1357 Q_ASSERT(!(initialOption.
startsHidden() && item->isContainer()));
1361 if (hasOrientationFor(loc)) {
1362 if (m_children.size() == 1) {
1364 d->m_orientation = locOrientation;
1368 insertItem(item, index, initialOption);
1373 container->setGeometry(rect());
1374 container->setChildren(m_children, d->m_orientation);
1377 insertItem(container, 0, DefaultSizeMode::NoDefaultSizeMode);
1380 insertItem(item, loc, initialOption);
1382 if (!container->hasVisibleChildren())
1383 container->setGeometry(
QRect());
1386 d->updateSeparators_recursive();
1387 d->scheduleCheckSanity();
1390 void ItemBoxContainer::onChildMinSizeChanged(Item *child)
1392 if (d->m_convertingItemToContainer || d->m_isDeserializing || !child->isVisible()) {
1397 updateSizeConstraints();
1399 if (child->isBeingInserted())
1402 if (numVisibleChildren() == 1 && child->isVisible()) {
1404 child->setGeometry(rect());
1405 updateChildPercentages();
1409 const QSize missingForChild = child->missingSize();
1410 if (!missingForChild.
isNull()) {
1412 growItem(child, Layouting::length(missingForChild, d->m_orientation), GrowthStrategy::BothSidesEqually, NeighbourSqueezeStrategy::AllNeighbours);
1415 updateChildPercentages();
1418 void ItemBoxContainer::updateSizeConstraints()
1420 const QSize missingSize = this->missingSize();
1421 if (!missingSize.
isNull()) {
1424 setSize_recursive(size() + missingSize);
1429 Q_EMIT minSizeChanged(
this);
1432 void ItemBoxContainer::onChildVisibleChanged(Item *,
bool visible)
1434 if (d->m_isDeserializing || isInSimplify())
1437 const int numVisible = numVisibleChildren();
1438 if (visible && numVisible == 1) {
1440 Q_EMIT visibleChanged(
this,
true);
1441 }
else if (!visible && numVisible == 0) {
1442 Q_EMIT visibleChanged(
this,
false);
1446 QRect ItemBoxContainer::suggestedDropRect(
const Item *item,
const Item *relativeTo,
Location loc)
const
1456 if (relativeTo && !relativeTo->parentContainer()) {
1457 qWarning() << Q_FUNC_INFO <<
"No parent container";
1461 if (relativeTo && relativeTo->parentContainer() !=
this) {
1462 qWarning() << Q_FUNC_INFO <<
"Called on the wrong container";
1466 if (relativeTo && !relativeTo->isVisible()) {
1467 qWarning() << Q_FUNC_INFO <<
"relative to isn't visible";
1472 qWarning() << Q_FUNC_INFO <<
"Invalid location";
1476 const QSize availableSize = root()->availableSize();
1477 const QSize minSize = item->minSize();
1478 const bool isEmpty = !root()->hasVisibleChildren();
1479 const int extraWidth = (isEmpty ||
locationIsVertical(loc)) ? 0 : Item::separatorThickness;
1480 const int extraHeight = (isEmpty || !
locationIsVertical(loc)) ? 0 : Item::separatorThickness;
1481 const bool windowNeedsGrowing = availableSize.
width() < minSize.
width() + extraWidth || availableSize.
height() < minSize.
height() + extraHeight;
1483 if (windowNeedsGrowing)
1484 return suggestedDropRectFallback(item, relativeTo, loc);
1486 const QVariantMap rootSerialized = root()->toVariantMap();
1488 rootCopy.fillFromVariantMap(rootSerialized, {});
1491 relativeTo = rootCopy.d->itemFromPath(relativeTo->pathFromRoot());
1493 const QVariantMap itemSerialized = item->toVariantMap();
1494 auto itemCopy =
new Item(
nullptr);
1495 itemCopy->fillFromVariantMap(itemSerialized, {});
1498 auto r =
const_cast<Item *
>(relativeTo);
1499 ItemBoxContainer::insertItemRelativeTo(itemCopy, r, loc, DefaultSizeMode::FairButFloor);
1501 rootCopy.insertItem(itemCopy, loc, DefaultSizeMode::FairButFloor);
1504 if (rootCopy.size() != root()->size()) {
1506 qWarning() << Q_FUNC_INFO <<
"The root copy grew ?!" << rootCopy.size() << root()->size()
1508 return suggestedDropRectFallback(item, relativeTo, loc);
1511 return itemCopy->mapToRoot(itemCopy->rect());
1514 QRect ItemBoxContainer::suggestedDropRectFallback(
const Item *item,
const Item *relativeTo,
Location loc)
const
1516 const QSize minSize = item->minSize();
1517 const int itemMin = Layouting::length(minSize, d->m_orientation);
1518 const int available = availableLength() - Item::separatorThickness;
1520 int suggestedPos = 0;
1521 const QRect relativeToGeo = relativeTo->geometry();
1525 suggestedPos = relativeToGeo.
x();
1528 suggestedPos = relativeToGeo.
y();
1531 suggestedPos = relativeToGeo.
right() - suggestedLength + 1;
1534 suggestedPos = relativeToGeo.
bottom() - suggestedLength + 1;
1543 rect.
setSize(
QSize(relativeTo->width(), suggestedLength));
1546 rect.
setSize(
QSize(suggestedLength, relativeTo->height()));
1549 return mapToRoot(rect);
1550 }
else if (isRoot()) {
1552 QRect rect = this->rect();
1553 const int oneThird = length() / 3;
1554 const int suggestedLength = qMax(qMin(available, oneThird), itemMin);
1564 rect.
adjust(rect.
width() - suggestedLength, 0, 0, 0);
1576 qWarning() << Q_FUNC_INFO <<
"Shouldn't happen";
1582 void ItemBoxContainer::positionItems()
1584 SizingInfo::List sizes = this->sizes();
1585 positionItems(sizes);
1586 applyPositions(sizes);
1588 d->updateSeparators_recursive();
1591 void ItemBoxContainer::positionItems_recursive()
1594 for (Item *item : qAsConst(m_children)) {
1595 if (item->isVisible()) {
1596 if (
auto c = item->asBoxContainer())
1597 c->positionItems_recursive();
1602 void ItemBoxContainer::applyPositions(
const SizingInfo::List &sizes)
1604 const Item::List items = visibleChildren();
1605 const auto count = items.size();
1606 Q_ASSERT(count == sizes.size());
1607 for (
int i = 0; i < count; ++i) {
1608 Item *item = items.at(i);
1609 const SizingInfo &sizing = sizes[i];
1610 if (sizing.isBeingInserted) {
1618 item->setPos(sizing.geometry.topLeft());
1624 return d->m_orientation;
1627 void ItemBoxContainer::positionItems(SizingInfo::List &sizes)
1630 const auto count = sizes.count();
1632 for (
auto i = 0; i < count; ++i) {
1633 SizingInfo &sizing = sizes[i];
1634 if (sizing.isBeingInserted) {
1635 nextPos += Item::separatorThickness;
1644 sizing.setPos(nextPos, d->m_orientation);
1645 nextPos += sizing.length(d->m_orientation) + Item::separatorThickness;
1649 void ItemBoxContainer::clear()
1651 for (Item *item : qAsConst(m_children)) {
1658 d->deleteSeparators();
1661 Item *ItemBoxContainer::itemAt(
QPoint p)
const
1663 for (Item *item : qAsConst(m_children)) {
1664 if (item->isVisible() && item->geometry().contains(p))
1671 Item *ItemBoxContainer::itemAt_recursive(
QPoint p)
const
1673 if (Item *item = itemAt(p)) {
1674 if (
auto c = item->asBoxContainer()) {
1675 return c->itemAt_recursive(c->mapFromParent(p));
1684 void ItemBoxContainer::setHostWidget(
Widget *host)
1686 Item::setHostWidget(host);
1687 d->deleteSeparators_recursive();
1688 for (Item *item : qAsConst(m_children)) {
1689 item->setHostWidget(host);
1692 d->updateSeparators_recursive();
1695 void ItemBoxContainer::setIsVisible(
bool)
1700 bool ItemBoxContainer::isVisible(
bool excludeBeingInserted)
const
1702 return hasVisibleChildren(excludeBeingInserted);
1705 void ItemBoxContainer::setLength_recursive(
int length,
Qt::Orientation o)
1714 setSize_recursive(sz);
1717 void ItemBoxContainer::insertItem(Item *item,
int index,
InitialOption option)
1719 if (option.sizeMode != DefaultSizeMode::NoDefaultSizeMode) {
1721 const int suggestedLength = d->defaultLengthFor(item, option);
1722 item->setLength_recursive(suggestedLength, d->m_orientation);
1725 m_children.insert(index, item);
1726 item->setParentContainer(
this);
1728 Q_EMIT itemsChanged();
1730 if (!d->m_convertingItemToContainer && item->isVisible())
1733 const bool shouldEmitVisibleChanged = item->isVisible();
1735 if (!d->m_convertingItemToContainer && !s_inhibitSimplify)
1738 if (shouldEmitVisibleChanged)
1739 Q_EMIT root()->numVisibleItemsChanged(root()->numVisibleChildren());
1740 Q_EMIT root()->numItemsChanged();
1743 bool ItemBoxContainer::hasOrientationFor(
Location loc)
const
1745 if (m_children.size() <= 1)
1751 int ItemBoxContainer::usableLength()
const
1753 const Item::List children = visibleChildren();
1754 const auto numVisibleChildren = children.size();
1756 if (children.size() <= 1)
1757 return Layouting::length(size(), d->m_orientation);
1759 const int separatorWaste = separatorThickness * (numVisibleChildren - 1);
1760 return length() - separatorWaste;
1763 void ItemBoxContainer::setChildren(
const List &children,
Qt::Orientation o)
1765 m_children = children;
1766 for (Item *item : children)
1767 item->setParentContainer(
this);
1774 if (o != d->m_orientation) {
1775 d->m_orientation = o;
1776 d->updateSeparators_recursive();
1780 QSize ItemBoxContainer::Private::minSize(
const Item::List &items)
const
1785 if (!q->m_children.isEmpty()) {
1786 for (Item *item : items) {
1787 if (!(item->isVisible() || item->isBeingInserted()))
1790 if (q->isVertical()) {
1791 minW = qMax(minW, item->minSize().width());
1792 minH += item->minSize().height();
1794 minH = qMax(minH, item->minSize().height());
1795 minW += item->minSize().width();
1799 const int separatorWaste = qMax(0, (numVisible - 1) * separatorThickness);
1800 if (q->isVertical())
1801 minH += separatorWaste;
1803 minW += separatorWaste;
1806 return QSize(minW, minH);
1809 QSize ItemBoxContainer::minSize()
const
1811 return d->minSize(m_children);
1814 QSize ItemBoxContainer::maxSizeHint()
const
1816 int maxW = isVertical() ? hardcodedMaximumSize.
width() : 0;
1817 int maxH = isVertical() ? 0 : hardcodedMaximumSize.height();
1819 const Item::List visibleChildren = this->visibleChildren(
false);
1820 if (!visibleChildren.isEmpty()) {
1821 for (Item *item : visibleChildren) {
1822 if (item->isBeingInserted())
1824 const QSize itemMaxSz = item->maxSizeHint();
1825 const int itemMaxWidth = itemMaxSz.
width();
1826 const int itemMaxHeight = itemMaxSz.
height();
1828 maxW = qMin(maxW, itemMaxWidth);
1829 maxH = qMin(maxH + itemMaxHeight, hardcodedMaximumSize.height());
1831 maxH = qMin(maxH, itemMaxHeight);
1832 maxW = qMin(maxW + itemMaxWidth, hardcodedMaximumSize.width());
1836 const auto separatorWaste = (visibleChildren.size() - 1) * separatorThickness;
1838 maxH = qMin(maxH + separatorWaste, hardcodedMaximumSize.height());
1840 maxW = qMin(maxW + separatorWaste, hardcodedMaximumSize.width());
1845 maxW = hardcodedMaximumSize.width();
1848 maxH = hardcodedMaximumSize.height();
1853 void ItemBoxContainer::Private::resizeChildren(
QSize oldSize,
QSize newSize, SizingInfo::List &childSizes,
1854 ChildrenResizeStrategy strategy)
1861 const auto count = childSizes.count();
1862 const bool widthChanged = oldSize.
width() != newSize.
width();
1863 const bool heightChanged = oldSize.
height() != newSize.
height();
1864 const bool lengthChanged = (q->isVertical() && heightChanged) || (q->isHorizontal() && widthChanged);
1865 const int totalNewLength = q->usableLength();
1867 if (strategy == ChildrenResizeStrategy::Percentage) {
1871 int remaining = totalNewLength;
1872 for (
int i = 0; i < count; ++i) {
1873 const bool isLast = i == count - 1;
1875 SizingInfo &itemSize = childSizes[i];
1877 const qreal childPercentage = childPercentages.
at(i);
1878 const int newItemLength = lengthChanged ? (isLast ? remaining
1879 : int(childPercentage * totalNewLength))
1880 : itemSize.length(m_orientation);
1882 if (newItemLength <= 0) {
1883 q->root()->dumpLayout();
1884 qWarning() << Q_FUNC_INFO <<
"Invalid resize newItemLength=" << newItemLength;
1889 remaining = remaining - newItemLength;
1891 if (q->isVertical()) {
1892 itemSize.geometry.setSize({ q->width(), newItemLength });
1894 itemSize.geometry.setSize({ newItemLength, q->height() });
1897 }
else if (strategy == ChildrenResizeStrategy::Side1SeparatorMove || strategy == ChildrenResizeStrategy::Side2SeparatorMove) {
1898 int remaining = Layouting::length(newSize - oldSize, m_orientation);
1899 const bool isGrowing = remaining > 0;
1900 remaining = qAbs(remaining);
1907 const bool isSide1SeparatorMove = strategy == ChildrenResizeStrategy::Side1SeparatorMove;
1908 bool resizeHeadFirst =
false;
1909 if (isGrowing && isSide1SeparatorMove) {
1910 resizeHeadFirst =
true;
1911 }
else if (isGrowing && !isSide1SeparatorMove) {
1912 resizeHeadFirst =
false;
1913 }
else if (!isGrowing && isSide1SeparatorMove) {
1914 resizeHeadFirst =
false;
1915 }
else if (!isGrowing && !isSide1SeparatorMove) {
1916 resizeHeadFirst =
true;
1919 for (
int i = 0; i < count; i++) {
1920 const auto index = resizeHeadFirst ? i : count - 1 - i;
1922 SizingInfo &size = childSizes[index];
1926 size.incrementLength(remaining, m_orientation);
1929 const int availableToGive = size.availableLength(m_orientation);
1930 const int took = qMin(availableToGive, remaining);
1931 size.incrementLength(-took, m_orientation);
1939 honourMaxSizes(childSizes);
1942 void ItemBoxContainer::Private::honourMaxSizes(SizingInfo::List &sizes)
1947 int amountNeededToShrink = 0;
1948 int amountAvailableToGrow = 0;
1952 for (
int i = 0; i < sizes.count(); ++i) {
1953 SizingInfo &info = sizes[i];
1954 const int neededToShrink = info.neededToShrink(m_orientation);
1955 const int availableToGrow = info.availableToGrow(m_orientation);
1957 if (neededToShrink > 0) {
1958 amountNeededToShrink += neededToShrink;
1960 }
else if (availableToGrow > 0) {
1961 amountAvailableToGrow = qMin(amountAvailableToGrow + availableToGrow, q->length());
1967 amountAvailableToGrow = qMin(amountNeededToShrink, amountAvailableToGrow);
1970 amountNeededToShrink = qMin(amountAvailableToGrow, amountNeededToShrink);
1972 if (amountNeededToShrink == 0 || amountAvailableToGrow == 0)
1979 while (amountAvailableToGrow > 0) {
1981 auto toGrow = qMax(1, amountAvailableToGrow / indexesOfGrowers.
size());
1983 for (
auto it = indexesOfGrowers.
begin(); it != indexesOfGrowers.
end();) {
1984 const int index = *it;
1985 SizingInfo &sizing = sizes[index];
1986 const auto grew = qMin(sizing.availableToGrow(m_orientation), toGrow);
1987 sizing.incrementLength(grew, m_orientation);
1988 amountAvailableToGrow -= grew;
1990 if (amountAvailableToGrow == 0) {
1995 if (sizing.availableToGrow(m_orientation) == 0) {
1997 it = indexesOfGrowers.
erase(it);
2005 while (amountNeededToShrink > 0) {
2007 auto toShrink = qMax(1, amountNeededToShrink / indexesOfShrinkers.
size());
2009 for (
auto it = indexesOfShrinkers.
begin(); it != indexesOfShrinkers.
end();) {
2010 const int index = *it;
2011 SizingInfo &sizing = sizes[index];
2012 const auto shrunk = qMin(sizing.neededToShrink(m_orientation), toShrink);
2013 sizing.incrementLength(-shrunk, m_orientation);
2014 amountNeededToShrink -= shrunk;
2016 if (amountNeededToShrink == 0) {
2021 if (sizing.neededToShrink(m_orientation) == 0) {
2023 it = indexesOfShrinkers.
erase(it);
2031 void ItemBoxContainer::setSize_recursive(
QSize newSize, ChildrenResizeStrategy strategy)
2035 const QSize minSize = this->minSize();
2037 root()->dumpLayout();
2038 qWarning() << Q_FUNC_INFO <<
"New size doesn't respect size constraints"
2039 <<
"; new=" << newSize
2040 <<
"; min=" << minSize
2044 if (newSize == size())
2047 const QSize oldSize = size();
2050 const Item::List children = visibleChildren();
2051 const auto count = children.size();
2052 SizingInfo::List childSizes = sizes();
2062 d->resizeChildren(oldSize, newSize, childSizes, strategy);
2065 positionItems( childSizes);
2068 for (
int i = 0; i < count; ++i) {
2069 SizingInfo &size = childSizes[i];
2070 const int missing = size.missingLength(d->m_orientation);
2072 growItem(i, childSizes, missing, GrowthStrategy::BothSidesEqually, NeighbourSqueezeStrategy::AllNeighbours);
2076 applyGeometries(childSizes, strategy);
2079 int ItemBoxContainer::length()
const
2081 return isVertical() ? height() : width();
2084 void ItemBoxContainer::dumpLayout(
int level)
2086 if (level == 0 && hostWidget()) {
2088 const auto screens = qApp->screens();
2089 for (
auto screen : screens) {
2090 qDebug().noquote() <<
"Screen" << screen->geometry() << screen->availableGeometry()
2091 <<
"; drp=" << screen->devicePixelRatio();
2094 hostWidget()->
dumpDebug(qDebug().noquote());
2099 const QString beingInserted = m_sizingInfo.isBeingInserted ? QStringLiteral(
"; beingInserted;")
2101 const QString visible = !isVisible() ? QStringLiteral(
";hidden;")
2104 const QString typeStr = isRoot() ? QStringLiteral(
"* Root: ")
2105 : QStringLiteral(
"* Layout: ");
2108 auto dbg = qDebug().noquote();
2109 dbg << indent << typeStr << d->m_orientation
2110 << m_sizingInfo.geometry
2111 <<
"; min=" << minSize()
2112 <<
"; this=" <<
this << beingInserted << visible
2113 <<
"; %=" << d->childPercentages();
2115 if (maxSizeHint() != Item::hardcodedMaximumSize)
2116 dbg <<
"; max=" << maxSizeHint();
2120 for (Item *item : qAsConst(m_children)) {
2121 item->dumpLayout(level + 1);
2122 if (item->isVisible()) {
2123 if (i < d->m_separators.size()) {
2124 auto separator = d->m_separators.at(i);
2125 qDebug().noquote() << indent <<
" - Separator: "
2126 <<
"local.geo=" << mapFromRoot(separator->asWidget()->geometry())
2127 <<
"global.geo=" << separator->asWidget()->geometry()
2135 void ItemBoxContainer::updateChildPercentages()
2137 if (root()->d->m_blockUpdatePercentages)
2140 const int usable = usableLength();
2141 for (Item *item : qAsConst(m_children)) {
2142 if (item->isVisible() && !item->isBeingInserted()) {
2143 item->m_sizingInfo.percentageWithinParent = (1.0 * item->length(d->m_orientation)) / usable;
2145 item->m_sizingInfo.percentageWithinParent = 0.0;
2150 void ItemBoxContainer::updateChildPercentages_recursive()
2152 updateChildPercentages();
2153 for (Item *item : qAsConst(m_children)) {
2154 if (
auto c = item->asBoxContainer())
2155 c->updateChildPercentages_recursive();
2162 percentages.
reserve(q->m_children.size());
2164 for (Item *item : qAsConst(q->m_children)) {
2165 if (item->isVisible() && !item->isBeingInserted())
2166 percentages << item->m_sizingInfo.percentageWithinParent;
2172 void ItemBoxContainer::restoreChild(Item *item, NeighbourSqueezeStrategy neighbourSqueezeStrategy)
2174 Q_ASSERT(contains(item));
2176 const bool hadVisibleChildren = hasVisibleChildren(
true);
2178 item->setIsVisible(
true);
2179 item->setBeingInserted(
true);
2181 const int excessLength = d->excessLength();
2183 if (!hadVisibleChildren) {
2185 if (
auto c = parentBoxContainer()) {
2186 setSize(item->size());
2187 c->restoreChild(
this, neighbourSqueezeStrategy);
2192 updateSizeConstraints();
2194 item->setBeingInserted(
false);
2196 if (numVisibleChildren() == 1) {
2198 item->setGeometry_recursive(rect());
2199 d->updateSeparators_recursive();
2203 const int available = availableToSqueezeOnSide(item, Side1) + availableToSqueezeOnSide(item, Side2) - Item::separatorThickness;
2205 const int max = qMin(available, item->maxLengthHint(d->m_orientation));
2206 const int min = item->minLength(d->m_orientation);
2214 const int proposed = qMax(Layouting::length(item->size(), d->m_orientation), excessLength - Item::separatorThickness);
2215 const int newLength = qBound(min, proposed, max);
2217 Q_ASSERT(item->isVisible());
2221 item->m_sizingInfo.geometry.setHeight(0);
2223 item->m_sizingInfo.geometry.setWidth(0);
2226 growItem(item, newLength, GrowthStrategy::BothSidesEqually, neighbourSqueezeStrategy,
true);
2227 d->updateSeparators_recursive();
2230 void ItemBoxContainer::updateWidgetGeometries()
2232 for (Item *item : qAsConst(m_children))
2233 item->updateWidgetGeometries();
2236 int ItemBoxContainer::oppositeLength()
const
2238 return isVertical() ? width()
2242 void ItemBoxContainer::requestSeparatorMove(
Separator *separator,
int delta)
2244 const auto separatorIndex = d->m_separators.indexOf(separator);
2245 if (separatorIndex == -1) {
2247 qWarning() << Q_FUNC_INFO <<
"Unknown separator" << separator <<
this;
2248 root()->dumpLayout();
2255 const int min = minPosForSeparator_global(separator);
2256 const int pos = separator->position();
2257 const int max = maxPosForSeparator_global(separator);
2259 if ((pos + delta < min && delta < 0) ||
2260 (pos + delta > max && delta > 0)) {
2261 root()->dumpLayout();
2262 qWarning() <<
"Separator would have gone out of bounds"
2263 <<
"; separators=" << separator
2264 <<
"; min=" << min <<
"; pos=" << pos
2265 <<
"; max=" << max <<
"; delta=" << delta;
2269 const Side moveDirection = delta < 0 ? Side1 : Side2;
2270 const Item::List children = visibleChildren();
2271 if (children.size() <= separatorIndex) {
2273 qWarning() << Q_FUNC_INFO <<
"Not enough children for separator index" << separator
2274 <<
this << separatorIndex;
2275 root()->dumpLayout();
2279 int remainingToTake = qAbs(delta);
2280 int tookLocally = 0;
2282 Item *side1Neighbour = children[separatorIndex];
2283 Item *side2Neighbour = children[separatorIndex + 1];
2285 Side nextSeparatorDirection = moveDirection;
2287 if (moveDirection == Side1) {
2289 const int availableSqueeze1 = availableToSqueezeOnSide(side2Neighbour, Side1);
2290 const int availableGrow2 = availableToGrowOnSide(side1Neighbour, Side2);
2293 tookLocally = qMin(availableSqueeze1, remainingToTake);
2294 tookLocally = qMin(tookLocally, availableGrow2);
2296 if (tookLocally != 0) {
2297 growItem(side2Neighbour, tookLocally, GrowthStrategy::Side1Only,
2298 NeighbourSqueezeStrategy::ImmediateNeighboursFirst,
false,
2299 ChildrenResizeStrategy::Side1SeparatorMove);
2302 if (availableGrow2 == tookLocally)
2303 nextSeparatorDirection = Side2;
2307 const int availableSqueeze2 = availableToSqueezeOnSide(side1Neighbour, Side2);
2308 const int availableGrow1 = availableToGrowOnSide(side2Neighbour, Side1);
2311 tookLocally = qMin(availableSqueeze2, remainingToTake);
2312 tookLocally = qMin(tookLocally, availableGrow1);
2314 if (tookLocally != 0) {
2315 growItem(side1Neighbour, tookLocally, GrowthStrategy::Side2Only,
2316 NeighbourSqueezeStrategy::ImmediateNeighboursFirst,
false,
2317 ChildrenResizeStrategy::Side2SeparatorMove);
2320 if (availableGrow1 == tookLocally)
2321 nextSeparatorDirection = Side1;
2324 remainingToTake -= tookLocally;
2326 if (remainingToTake > 0) {
2328 if (Q_UNLIKELY(isRoot())) {
2330 qWarning() << Q_FUNC_INFO <<
"Not enough space to move separator"
2333 Separator *nextSeparator = parentBoxContainer()->d->neighbourSeparator_recursive(
this, nextSeparatorDirection, d->m_orientation);
2334 if (!nextSeparator) {
2336 qWarning() << Q_FUNC_INFO <<
"nextSeparator is null, report a bug";
2341 const int remainingDelta = moveDirection == Side1 ? -remainingToTake : remainingToTake;
2342 nextSeparator->parentContainer()->requestSeparatorMove(nextSeparator, remainingDelta);
2347 void ItemBoxContainer::requestEqualSize(
Separator *separator)
2349 const auto separatorIndex = d->m_separators.indexOf(separator);
2350 if (separatorIndex == -1) {
2352 qWarning() << Q_FUNC_INFO <<
"Separator not found" << separator;
2356 const Item::List children = visibleChildren();
2357 Item *side1Item = children.at(separatorIndex);
2358 Item *side2Item = children.at(separatorIndex + 1);
2360 const int length1 = side1Item->length(d->m_orientation);
2361 const int length2 = side2Item->length(d->m_orientation);
2363 if (qAbs(length1 - length2) <= 1) {
2368 if (!(side1Item->m_sizingInfo.isPastMax(d->m_orientation) || side2Item->m_sizingInfo.isPastMax(d->m_orientation))) {
2373 const int newLength = (length1 + length2) / 2;
2376 if (length1 < newLength) {
2378 delta = newLength - length1;
2379 }
else if (length2 < newLength) {
2381 delta = -(newLength - length2);
2385 const int min = minPosForSeparator_global(separator,
true);
2386 const int max = maxPosForSeparator_global(separator,
true);
2387 const int newPos = qBound(min, separator->position() + delta, max);
2390 delta = newPos - separator->position();
2393 requestSeparatorMove(separator, delta);
2396 void ItemBoxContainer::layoutEqually()
2398 SizingInfo::List childSizes = sizes();
2399 if (!childSizes.isEmpty()) {
2400 layoutEqually(childSizes);
2401 applyGeometries(childSizes);
2405 void ItemBoxContainer::layoutEqually(SizingInfo::List &sizes)
2407 const auto numItems = sizes.count();
2409 satisfiedIndexes.
reserve(numItems);
2411 auto lengthToGive = length() - (d->m_separators.size() * Item::separatorThickness);
2414 for (SizingInfo &size : sizes) {
2415 size.setLength(0, d->m_orientation);
2418 while (satisfiedIndexes.
count() < sizes.count()) {
2419 const auto remainingItems = sizes.count() - satisfiedIndexes.
count();
2420 auto suggestedToGive = qMax(1, lengthToGive / remainingItems);
2421 const auto oldLengthToGive = lengthToGive;
2423 for (
int i = 0; i < numItems; ++i) {
2427 SizingInfo &size = sizes[i];
2428 if (size.availableToGrow(d->m_orientation) <= 0) {
2438 const auto othersMissing =
2440 std::accumulate(sizes.constBegin(), sizes.constEnd(), 0,
2441 [
this](
size_t sum,
const SizingInfo &sz) {
2442 return int(sum) + sz.missingLength(d->m_orientation);
2444 - size.missingLength(d->m_orientation);
2446 const auto maxLength =
2447 qMin(size.length(d->m_orientation) + lengthToGive - othersMissing,
2448 size.maxLengthHint(d->m_orientation));
2450 const auto newItemLenght =
2451 qBound(size.minLength(d->m_orientation),
2452 size.length(d->m_orientation) + suggestedToGive, maxLength);
2453 const auto toGive = newItemLenght - size.length(d->m_orientation);
2459 lengthToGive -= toGive;
2460 size.incrementLength(toGive, d->m_orientation);
2461 if (size.availableToGrow(d->m_orientation) <= 0) {
2464 if (lengthToGive == 0)
2467 if (lengthToGive < 0) {
2468 qWarning() << Q_FUNC_INFO <<
"Breaking infinite loop";
2474 if (oldLengthToGive == lengthToGive) {
2481 void ItemBoxContainer::layoutEqually_recursive()
2484 for (Item *item : qAsConst(m_children)) {
2485 if (item->isVisible()) {
2486 if (
auto c = item->asBoxContainer())
2487 c->layoutEqually_recursive();
2492 Item *ItemBoxContainer::visibleNeighbourFor(
const Item *item, Side side)
const
2495 const auto index = m_children.indexOf(
const_cast<Item *
>(item));
2497 if (side == Side1) {
2498 for (
auto i = index - 1; i >= 0; --i) {
2499 Item *child = m_children.at(i);
2500 if (child->isVisible())
2504 for (
auto i = index + 1; i < m_children.size(); ++i) {
2505 Item *child = m_children.at(i);
2506 if (child->isVisible())
2514 QSize ItemBoxContainer::availableSize()
const
2516 return size() - this->minSize();
2519 int ItemBoxContainer::availableLength()
const
2521 return isVertical() ? availableSize().
height()
2522 : availableSize().
width();
2525 LengthOnSide ItemBoxContainer::lengthOnSide(
const SizingInfo::List &sizes,
int fromIndex,
2531 const auto count = sizes.count();
2532 if (fromIndex >= count)
2537 if (side == Side1) {
2545 LengthOnSide result;
2546 for (
int i = start; i <= end; ++i) {
2547 const SizingInfo &size = sizes.at(i);
2548 result.length += size.length(o);
2549 result.minLength += size.minLength(o);
2555 int ItemBoxContainer::neighboursLengthFor(
const Item *item, Side side,
Qt::Orientation o)
const
2557 const Item::List children = visibleChildren();
2558 const auto index = children.indexOf(
const_cast<Item *
>(item));
2560 qWarning() << Q_FUNC_INFO <<
"Couldn't find item" << item;
2564 if (o == d->m_orientation) {
2565 int neighbourLength = 0;
2568 if (side == Side1) {
2573 end = children.size() - 1;
2576 for (
int i = start; i <= end; ++i)
2577 neighbourLength += children.at(i)->length(d->m_orientation);
2579 return neighbourLength;
2586 int ItemBoxContainer::neighboursLengthFor_recursive(
const Item *item, Side side,
Qt::Orientation o)
const
2588 return neighboursLengthFor(item, side, o) + (isRoot() ? 0 : parentBoxContainer()->neighboursLengthFor_recursive(
this, side, o));
2591 int ItemBoxContainer::neighboursMinLengthFor(
const Item *item, Side side,
Qt::Orientation o)
const
2593 const Item::List children = visibleChildren();
2594 const auto index = children.indexOf(
const_cast<Item *
>(item));
2596 qWarning() << Q_FUNC_INFO <<
"Couldn't find item" << item;
2600 if (o == d->m_orientation) {
2601 int neighbourMinLength = 0;
2604 if (side == Side1) {
2609 end = children.size() - 1;
2612 for (
int i = start; i <= end; ++i)
2613 neighbourMinLength += children.at(i)->minLength(d->m_orientation);
2615 return neighbourMinLength;
2622 int ItemBoxContainer::neighboursMaxLengthFor(
const Item *item, Side side,
Qt::Orientation o)
const
2624 const Item::List children = visibleChildren();
2625 const auto index = children.indexOf(
const_cast<Item *
>(item));
2627 qWarning() << Q_FUNC_INFO <<
"Couldn't find item" << item;
2631 if (o == d->m_orientation) {
2632 int neighbourMaxLength = 0;
2635 if (side == Side1) {
2640 end = children.size() - 1;
2643 for (
int i = start; i <= end; ++i)
2644 neighbourMaxLength = qMin(Layouting::length(root()->size(), d->m_orientation), neighbourMaxLength + children.at(i)->maxLengthHint(d->m_orientation));
2646 return neighbourMaxLength;
2653 int ItemBoxContainer::availableToSqueezeOnSide(
const Item *child, Side side)
const
2655 const int length = neighboursLengthFor(child, side, d->m_orientation);
2656 const int min = neighboursMinLengthFor(child, side, d->m_orientation);
2658 const int available = length - min;
2659 if (available < 0) {
2660 root()->dumpLayout();
2666 int ItemBoxContainer::availableToGrowOnSide(
const Item *child, Side side)
const
2668 const int length = neighboursLengthFor(child, side, d->m_orientation);
2669 const int max = neighboursMaxLengthFor(child, side, d->m_orientation);
2671 return max - length;
2674 int ItemBoxContainer::availableToSqueezeOnSide_recursive(
const Item *child, Side side,
Qt::Orientation orientation)
const
2676 if (orientation == d->m_orientation) {
2677 const int available = availableToSqueezeOnSide(child, side);
2678 return isRoot() ? available
2679 : (available + parentBoxContainer()->availableToSqueezeOnSide_recursive(
this, side, orientation));
2682 : parentBoxContainer()->availableToSqueezeOnSide_recursive(
this, side, orientation);
2686 int ItemBoxContainer::availableToGrowOnSide_recursive(
const Item *child, Side side,
Qt::Orientation orientation)
const
2688 if (orientation == d->m_orientation) {
2689 const int available = availableToGrowOnSide(child, side);
2690 return isRoot() ? available
2691 : (available + parentBoxContainer()->availableToGrowOnSide_recursive(
this, side, orientation));
2694 : parentBoxContainer()->availableToGrowOnSide_recursive(
this, side, orientation);
2698 void ItemBoxContainer::growNeighbours(Item *side1Neighbour, Item *side2Neighbour)
2700 if (!side1Neighbour && !side2Neighbour)
2703 SizingInfo::List childSizes = sizes();
2705 if (side1Neighbour && side2Neighbour) {
2706 const int index1 = indexOfVisibleChild(side1Neighbour);
2707 const int index2 = indexOfVisibleChild(side2Neighbour);
2709 if (index1 == -1 || index2 == -1 || index1 >= childSizes.count() || index2 >= childSizes.count()) {
2711 qWarning() << Q_FUNC_INFO <<
"Invalid indexes" << index1 << index2 << childSizes.count();
2716 QRect &geo1 = childSizes[index1].geometry;
2717 QRect &geo2 = childSizes[index2].geometry;
2720 const int available = geo2.
y() - geo1.
bottom() - separatorThickness;
2724 const int available = geo2.
x() - geo1.
right() - separatorThickness;
2729 }
else if (side1Neighbour) {
2730 const int index1 = indexOfVisibleChild(side1Neighbour);
2731 if (index1 == -1 || index1 >= childSizes.count()) {
2733 qWarning() << Q_FUNC_INFO <<
"Invalid indexes" << index1 << childSizes.count();
2738 QRect &geo = childSizes[index1].geometry;
2744 }
else if (side2Neighbour) {
2745 const int index2 = indexOfVisibleChild(side2Neighbour);
2746 if (index2 == -1 || index2 >= childSizes.count()) {
2748 qWarning() << Q_FUNC_INFO <<
"Invalid indexes" << index2 << childSizes.count();
2753 QRect &geo = childSizes[index2].geometry;
2761 d->honourMaxSizes(childSizes);
2762 positionItems( childSizes);
2763 applyGeometries(childSizes);
2766 void ItemBoxContainer::growItem(
int index, SizingInfo::List &sizes,
int missing,
2767 GrowthStrategy growthStrategy,
2768 NeighbourSqueezeStrategy neighbourSqueezeStrategy,
2769 bool accountForNewSeparator)
2771 int toSteal = missing;
2772 if (accountForNewSeparator)
2773 toSteal += Item::separatorThickness;
2775 Q_ASSERT(index != -1);
2780 SizingInfo &sizingInfo = sizes[index];
2781 sizingInfo.setOppositeLength(oppositeLength(), d->m_orientation);
2782 const bool isFirst = index == 0;
2783 const bool isLast = index == sizes.count() - 1;
2785 int side1Growth = 0;
2786 int side2Growth = 0;
2788 if (growthStrategy == GrowthStrategy::BothSidesEqually) {
2789 sizingInfo.setLength(sizingInfo.length(d->m_orientation) + missing, d->m_orientation);
2790 const auto count = sizes.count();
2793 sizingInfo.incrementLength(missing, d->m_orientation);
2798 const LengthOnSide side1Length = lengthOnSide(sizes, index - 1, Side1, d->m_orientation);
2799 const LengthOnSide side2Length = lengthOnSide(sizes, index + 1, Side2, d->m_orientation);
2801 int available1 = side1Length.available();
2802 int available2 = side2Length.available();
2804 if (toSteal > available1 + available2) {
2805 root()->dumpLayout();
2809 while (toSteal > 0) {
2810 if (available1 == 0) {
2811 Q_ASSERT(available2 >= toSteal);
2812 side2Growth += toSteal;
2814 }
else if (available2 == 0) {
2815 Q_ASSERT(available1 >= toSteal);
2816 side1Growth += toSteal;
2820 const int toTake = qMax(1, toSteal / 2);
2821 const int took1 = qMin(toTake, available1);
2823 available1 -= took1;
2824 side1Growth += took1;
2828 const int took2 = qMin(toTake, available2);
2830 side2Growth += took2;
2831 available2 -= took2;
2833 shrinkNeighbours(index, sizes, side1Growth, side2Growth, neighbourSqueezeStrategy);
2834 }
else if (growthStrategy == GrowthStrategy::Side1Only) {
2835 side1Growth = qMin(missing, sizingInfo.availableToGrow(d->m_orientation));
2836 sizingInfo.setLength(sizingInfo.length(d->m_orientation) + side1Growth, d->m_orientation);
2837 if (side1Growth > 0)
2838 shrinkNeighbours(index, sizes, side1Growth, 0, neighbourSqueezeStrategy);
2839 if (side1Growth < missing) {
2840 missing = missing - side1Growth;
2844 qWarning() << Q_FUNC_INFO <<
"No more items to grow";
2846 growItem(index + 1, sizes, missing, growthStrategy, neighbourSqueezeStrategy, accountForNewSeparator);
2850 }
else if (growthStrategy == GrowthStrategy::Side2Only) {
2851 side2Growth = qMin(missing, sizingInfo.availableToGrow(d->m_orientation));
2852 sizingInfo.setLength(sizingInfo.length(d->m_orientation) + side2Growth, d->m_orientation);
2854 if (side2Growth > 0)
2855 shrinkNeighbours(index, sizes, 0, side2Growth, neighbourSqueezeStrategy);
2856 if (side2Growth < missing) {
2857 missing = missing - side2Growth;
2861 qWarning() << Q_FUNC_INFO <<
"No more items to grow";
2863 growItem(index - 1, sizes, missing, growthStrategy, neighbourSqueezeStrategy, accountForNewSeparator);
2869 void ItemBoxContainer::growItem(Item *item,
int amount, GrowthStrategy growthStrategy,
2870 NeighbourSqueezeStrategy neighbourSqueezeStrategy,
2871 bool accountForNewSeparator,
2872 ChildrenResizeStrategy childResizeStrategy)
2874 const Item::List items = visibleChildren();
2875 const auto index = items.indexOf(item);
2876 SizingInfo::List sizes = this->sizes();
2878 growItem(index, sizes, amount, growthStrategy, neighbourSqueezeStrategy, accountForNewSeparator);
2880 applyGeometries(sizes, childResizeStrategy);
2883 void ItemBoxContainer::applyGeometries(
const SizingInfo::List &sizes, ChildrenResizeStrategy strategy)
2885 const Item::List items = visibleChildren();
2886 const auto count = items.size();
2887 Q_ASSERT(count == sizes.size());
2889 for (
int i = 0; i < count; ++i) {
2890 Item *item = items.at(i);
2891 item->setSize_recursive(sizes[i].geometry.size(), strategy);
2897 SizingInfo::List ItemBoxContainer::sizes(
bool ignoreBeingInserted)
const
2899 const Item::List children = visibleChildren(ignoreBeingInserted);
2900 SizingInfo::List result;
2901 result.reserve(children.count());
2902 for (Item *item : children) {
2903 if (item->isContainer()) {
2906 item->m_sizingInfo.minSize = item->minSize();
2907 item->m_sizingInfo.maxSizeHint = item->maxSizeHint();
2909 result << item->m_sizingInfo;
2915 QVector<int> ItemBoxContainer::calculateSqueezes(SizingInfo::List::ConstIterator begin,
2916 SizingInfo::List::ConstIterator end,
int needed,
2917 NeighbourSqueezeStrategy strategy,
bool reversed)
const
2920 for (
auto it = begin; it < end; ++it) {
2921 availabilities << it->availableLength(d->m_orientation);
2924 const auto count = availabilities.
count();
2928 std::fill(squeezes.
begin(), squeezes.
end(), 0);
2930 int missing = needed;
2932 if (strategy == NeighbourSqueezeStrategy::AllNeighbours) {
2933 while (missing > 0) {
2934 const int numDonors = std::count_if(availabilities.
cbegin(), availabilities.
cend(), [](
int num) {
2938 if (numDonors == 0) {
2939 root()->dumpLayout();
2944 int toTake = missing / numDonors;
2948 for (
int i = 0; i < count; ++i) {
2949 const int available = availabilities.
at(i);
2952 const int took = qMin(missing, qMin(toTake, available));
2953 availabilities[i] -= took;
2955 squeezes[i] += took;
2960 }
else if (strategy == NeighbourSqueezeStrategy::ImmediateNeighboursFirst) {
2961 for (
int i = 0; i < count; i++) {
2962 const auto index = reversed ? count - 1 - i : i;
2964 const int available = availabilities.
at(index);
2965 if (available > 0) {
2966 const int took = qMin(missing, available);
2968 squeezes[index] += took;
2978 qWarning() << Q_FUNC_INFO <<
"Missing is negative" << missing
2985 void ItemBoxContainer::shrinkNeighbours(
int index, SizingInfo::List &sizes,
int side1Amount,
2986 int side2Amount, NeighbourSqueezeStrategy strategy)
2988 Q_ASSERT(side1Amount > 0 || side2Amount > 0);
2989 Q_ASSERT(side1Amount >= 0 && side2Amount >= 0);
2991 if (side1Amount > 0) {
2992 auto begin = sizes.cbegin();
2993 auto end = sizes.cbegin() + index;
2994 const bool reversed = strategy == NeighbourSqueezeStrategy::ImmediateNeighboursFirst;
2995 const QVector<int> squeezes = calculateSqueezes(begin, end, side1Amount, strategy, reversed);
2996 for (
int i = 0; i < squeezes.
size(); ++i) {
2997 const int squeeze = squeezes.
at(i);
2998 SizingInfo &sizing = sizes[i];
3000 sizing.setSize(
adjustedRect(sizing.geometry, d->m_orientation, 0, -squeeze).
size());
3004 if (side2Amount > 0) {
3005 auto begin = sizes.cbegin() + index + 1;
3006 auto end = sizes.cend();
3008 const QVector<int> squeezes = calculateSqueezes(begin, end, side2Amount, strategy);
3009 for (
int i = 0; i < squeezes.
size(); ++i) {
3010 const int squeeze = squeezes.
at(i);
3011 SizingInfo &sizing = sizes[i + index + 1];
3012 sizing.setSize(
adjustedRect(sizing.geometry, d->m_orientation, squeeze, 0).
size());
3017 QVector<int> ItemBoxContainer::Private::requiredSeparatorPositions()
const
3019 const int numSeparators = qMax(0, q->numVisibleChildren() - 1);
3021 positions.
reserve(numSeparators);
3023 for (Item *item : qAsConst(q->m_children)) {
3024 if (positions.
size() == numSeparators)
3027 if (item->isVisible()) {
3028 const int localPos = item->m_sizingInfo.edge(m_orientation) + 1;
3029 positions << q->mapToRoot(localPos, m_orientation);
3036 void ItemBoxContainer::Private::updateSeparators()
3038 if (!q->hostWidget())
3041 const QVector<int> positions = requiredSeparatorPositions();
3042 const auto requiredNumSeparators = positions.
size();
3044 const bool numSeparatorsChanged = requiredNumSeparators != m_separators.size();
3045 if (numSeparatorsChanged) {
3048 Separator::List newSeparators;
3049 newSeparators.reserve(requiredNumSeparators);
3051 for (
int position : positions) {
3052 Separator *separator = separatorAt(position);
3055 newSeparators.push_back(separator);
3056 m_separators.removeOne(separator);
3058 separator = Config::self().createSeparator(q->hostWidget());
3059 separator->init(q, m_orientation);
3060 newSeparators.push_back(separator);
3067 m_separators = newSeparators;
3071 const int pos2 = q->isVertical() ? q->mapToRoot(
QPoint(0, 0)).x()
3072 : q->mapToRoot(
QPoint(0, 0)).y();
3075 for (
int position : positions) {
3076 m_separators.at(i)->setGeometry(position, pos2, q->oppositeLength());
3080 q->updateChildPercentages();
3083 void ItemBoxContainer::Private::deleteSeparators()
3085 qDeleteAll(m_separators);
3086 m_separators.clear();
3089 void ItemBoxContainer::Private::deleteSeparators_recursive()
3094 for (Item *item : qAsConst(q->m_children)) {
3095 if (
auto c = item->asBoxContainer())
3096 c->d->deleteSeparators_recursive();
3100 void ItemBoxContainer::Private::updateSeparators_recursive()
3105 const Item::List items = q->visibleChildren();
3106 for (Item *item : items) {
3107 if (
auto c = item->asBoxContainer())
3108 c->d->updateSeparators_recursive();
3112 int ItemBoxContainer::Private::excessLength()
const
3115 return qMax(0, Layouting::length(q->size(), m_orientation) - q->maxLengthHint(m_orientation));
3118 void ItemBoxContainer::simplify()
3125 Item::List newChildren;
3126 newChildren.reserve(m_children.size() + 20);
3128 for (Item *child : qAsConst(m_children)) {
3130 childContainer->simplify();
3132 if (childContainer->orientation() == d->m_orientation || childContainer->m_children.size() == 1) {
3135 for (Item *child2 : childContainer->childItems()) {
3136 child2->setParentContainer(
this);
3137 newChildren.push_back(child2);
3140 delete childContainer;
3142 newChildren.push_back(child);
3145 newChildren.push_back(child);
3149 if (m_children != newChildren) {
3150 m_children = newChildren;
3152 updateChildPercentages();
3156 Separator *ItemBoxContainer::Private::separatorAt(
int p)
const
3158 for (
Separator *separator : m_separators) {
3159 if (separator->position() == p)
3166 bool ItemBoxContainer::isVertical()
const
3171 bool ItemBoxContainer::isHorizontal()
const
3176 int ItemBoxContainer::indexOf(
Separator *separator)
const
3178 return d->m_separators.indexOf(separator);
3181 bool ItemBoxContainer::isInSimplify()
const
3183 if (d->m_isSimplifying)
3186 auto p = parentBoxContainer();
3187 return p && p->isInSimplify();
3190 int ItemBoxContainer::minPosForSeparator(
Separator *separator,
bool honourMax)
const
3192 const int globalMin = minPosForSeparator_global(separator, honourMax);
3193 return mapFromRoot(globalMin, d->m_orientation);
3196 int ItemBoxContainer::maxPosForSeparator(
Separator *separator,
bool honourMax)
const
3198 const int globalMax = maxPosForSeparator_global(separator, honourMax);
3199 return mapFromRoot(globalMax, d->m_orientation);
3202 int ItemBoxContainer::minPosForSeparator_global(
Separator *separator,
bool honourMax)
const
3204 const int separatorIndex = indexOf(separator);
3205 Q_ASSERT(separatorIndex != -1);
3207 const Item::List children = visibleChildren();
3208 Q_ASSERT(separatorIndex + 1 < children.size());
3209 Item *item2 = children.at(separatorIndex + 1);
3211 const int availableToSqueeze = availableToSqueezeOnSide_recursive(item2, Side1, d->m_orientation);
3215 Item *item1 = children.at(separatorIndex);
3216 const int availabletoGrow = availableToGrowOnSide_recursive(item1, Side2, d->m_orientation);
3217 return separator->position() - qMin(availabletoGrow, availableToSqueeze);
3220 return separator->position() - availableToSqueeze;
3223 int ItemBoxContainer::maxPosForSeparator_global(
Separator *separator,
bool honourMax)
const
3225 const int separatorIndex = indexOf(separator);
3226 Q_ASSERT(separatorIndex != -1);
3228 const Item::List children = visibleChildren();
3229 Item *item1 = children.at(separatorIndex);
3231 const int availableToSqueeze = availableToSqueezeOnSide_recursive(item1, Side2, d->m_orientation);
3235 Item *item2 = children.at(separatorIndex + 1);
3236 const int availabletoGrow = availableToGrowOnSide_recursive(item2, Side1, d->m_orientation);
3237 return separator->position() + qMin(availabletoGrow, availableToSqueeze);
3240 return separator->position() + availableToSqueeze;
3243 QVariantMap ItemBoxContainer::toVariantMap()
const
3245 QVariantMap result = Item::toVariantMap();
3247 QVariantList childrenV;
3248 childrenV.reserve(m_children.size());
3249 for (Item *child : qAsConst(m_children)) {
3250 childrenV.push_back(child->toVariantMap());
3253 result[QStringLiteral(
"children")] = childrenV;
3254 result[QStringLiteral(
"orientation")] = d->m_orientation;
3259 void ItemBoxContainer::fillFromVariantMap(
const QVariantMap &map,
3264 Item::fillFromVariantMap(map, widgets);
3265 const QVariantList childrenV = map[QStringLiteral(
"children")].toList();
3266 d->m_orientation =
Qt::Orientation(map[QStringLiteral(
"orientation")].toInt());
3268 for (
const QVariant &childV : childrenV) {
3269 const QVariantMap childMap = childV.toMap();
3270 const bool isContainer = childMap.value(QStringLiteral(
"isContainer")).toBool();
3272 : new Item(hostWidget(), this);
3273 child->fillFromVariantMap(childMap, widgets);
3274 m_children.push_back(child);
3278 updateChildPercentages_recursive();
3280 d->updateSeparators_recursive();
3281 d->updateWidgets_recursive();
3284 d->relayoutIfNeeded();
3285 positionItems_recursive();
3287 Q_EMIT minSizeChanged(
this);
3288 #ifdef DOCKS_DEVELOPER_MODE
3290 qWarning() << Q_FUNC_INFO <<
"Resulting layout is invalid";
3295 bool ItemBoxContainer::Private::isDummy()
const
3297 return q->hostWidget() ==
nullptr;
3300 #ifdef DOCKS_DEVELOPER_MODE
3301 bool ItemBoxContainer::test_suggestedRect()
3303 auto itemToDrop =
new Item(hostWidget());
3305 const Item::List children = visibleChildren();
3306 for (Item *relativeTo : children) {
3307 if (
auto c = relativeTo->asBoxContainer()) {
3308 c->test_suggestedRect();
3312 const QRect rect = suggestedDropRect(itemToDrop, relativeTo, loc);
3315 qWarning() << Q_FUNC_INFO <<
"Empty rect";
3317 }
else if (!root()->rect().contains(rect)) {
3318 root()->dumpLayout();
3319 qWarning() << Q_FUNC_INFO <<
"Suggested rect is out of bounds" << rect
3320 <<
"; loc=" << loc <<
"; relativeTo=" << relativeTo;
3325 root()->dumpLayout();
3326 qWarning() << Q_FUNC_INFO <<
"Invalid suggested rects" << rects
3327 <<
this <<
"; relativeTo=" << relativeTo;
3340 Layouting::Separator::List separators = d->m_separators;
3342 for (Item *item : qAsConst(m_children)) {
3343 if (
auto c = item->asBoxContainer())
3344 separators << c->separators_recursive();
3352 return d->m_separators;
3355 bool ItemBoxContainer::Private::isOverflowing()
const
3360 int contentsLength = 0;
3362 for (Item *item : qAsConst(q->m_children)) {
3363 if (item->isVisible()) {
3364 contentsLength += item->
length(m_orientation);
3369 contentsLength += qMax(0, Item::separatorThickness * (numVisible - 1));
3370 return contentsLength > q->length();
3373 void ItemBoxContainer::Private::relayoutIfNeeded()
3379 if (!q->missingSize().isNull())
3380 q->setSize_recursive(q->minSize());
3382 if (isOverflowing()) {
3383 const QSize size = q->size();
3384 q->m_sizingInfo.setSize(size +
QSize(1, 1));
3385 q->setSize_recursive(size);
3386 q->updateChildPercentages();
3390 for (Item *item : qAsConst(q->m_children)) {
3391 if (item->isVisible()) {
3392 if (
auto c = item->asBoxContainer())
3393 c->d->relayoutIfNeeded();
3398 const Item *ItemBoxContainer::Private::itemFromPath(
const QVector<int> &path)
const
3402 for (
int i = 0; i < path.
size(); ++i) {
3403 const int index = path[i];
3404 const bool isLast = i == path.
size() - 1;
3405 if (index < 0 || index >= container->m_children.size()) {
3407 q->root()->dumpLayout();
3408 qWarning() << Q_FUNC_INFO <<
"Invalid index" << index
3409 <<
this << path << q->isRoot();
3414 return container->m_children.
at(index);
3416 container = container->m_children.at(index)->asBoxContainer();
3418 qWarning() << Q_FUNC_INFO <<
"Invalid index" << path;
3427 Separator *ItemBoxContainer::Private::neighbourSeparator(
const Item *item, Side side,
Qt::Orientation orientation)
const
3429 Item::List children = q->visibleChildren();
3430 const auto itemIndex = children.indexOf(
const_cast<Item *
>(item));
3431 if (itemIndex == -1) {
3432 qWarning() << Q_FUNC_INFO <<
"Item not found" << item
3434 q->root()->dumpLayout();
3438 if (orientation != q->orientation()) {
3443 return q->parentBoxContainer()->d->neighbourSeparator(q, side, orientation);
3447 const auto separatorIndex = side == Side1 ? itemIndex - 1
3450 if (separatorIndex < 0 || separatorIndex >= m_separators.size())
3453 return m_separators[separatorIndex];
3456 Separator *ItemBoxContainer::Private::neighbourSeparator_recursive(
const Item *item, Side side,
3459 Separator *separator = neighbourSeparator(item, side, orientation);
3463 if (!q->parentContainer())
3466 return q->parentBoxContainer()->d->neighbourSeparator_recursive(q, side, orientation);
3469 void ItemBoxContainer::Private::updateWidgets_recursive()
3471 for (Item *item : qAsConst(q->m_children)) {
3472 if (
auto c = item->asBoxContainer()) {
3473 c->d->updateWidgets_recursive();
3475 if (item->isVisible()) {
3476 if (
Widget *widget = item->guestWidget()) {
3477 widget->setGeometry(q->mapToRoot(item->geometry()));
3478 widget->setVisible(
true);
3480 qWarning() << Q_FUNC_INFO <<
"visible item doesn't have a guest"
3488 SizingInfo::SizingInfo()
3489 : minSize(
Layouting::Item::hardcodedMinimumSize)
3490 , maxSizeHint(
Layouting::Item::hardcodedMaximumSize)
3499 QVariantMap SizingInfo::toVariantMap()
const
3502 result[QStringLiteral(
"geometry")] = rectToMap(geometry);
3503 result[QStringLiteral(
"minSize")] = sizeToMap(minSize);
3504 result[QStringLiteral(
"maxSize")] = sizeToMap(maxSizeHint);
3508 void SizingInfo::fromVariantMap(
const QVariantMap &map)
3510 *
this = SizingInfo();
3511 geometry = mapToRect(map[QStringLiteral(
"geometry")].toMap());
3512 minSize = mapToSize(map[QStringLiteral(
"minSize")].toMap());
3513 maxSizeHint = mapToSize(map[QStringLiteral(
"maxSize")].toMap());
3516 int ItemBoxContainer::Private::defaultLengthFor(Item *item,
InitialOption option)
const
3520 if (option.
hasPreferredLength(m_orientation) && option.sizeMode != DefaultSizeMode::NoDefaultSizeMode) {
3523 switch (option.sizeMode) {
3524 case DefaultSizeMode::NoDefaultSizeMode:
3526 case DefaultSizeMode::Fair: {
3527 const int numVisibleChildren = q->numVisibleChildren() + 1;
3528 const int usableLength = q->length() - (Item::separatorThickness * (numVisibleChildren - 1));
3529 result = usableLength / numVisibleChildren;
3532 case DefaultSizeMode::FairButFloor: {
3533 int length = defaultLengthFor(item, DefaultSizeMode::Fair);
3534 result = qMin(length, item->length(m_orientation));
3537 case DefaultSizeMode::ItemSize:
3538 result = item->length(m_orientation);
3543 result = qMax(item->minLength(m_orientation), result);
3547 struct ItemContainer::Private
3552 ( void )Config::self();
3562 : Item(true, hostWidget, parent)
3563 , d(new Private(this))
3565 connect(
this, &Item::xChanged,
this, [
this] {
3566 for (Item *item : qAsConst(m_children)) {
3567 Q_EMIT item->xChanged();
3571 connect(
this, &Item::yChanged,
this, [
this] {
3572 for (Item *item : qAsConst(m_children)) {
3573 Q_EMIT item->yChanged();
3578 ItemContainer::ItemContainer(
Widget *hostWidget)
3579 : Item(true, hostWidget, nullptr)
3580 , d(new Private(this))
3584 ItemContainer::~ItemContainer()
3589 const Item::List ItemContainer::childItems()
const
3594 bool ItemContainer::hasChildren()
const
3596 return !m_children.isEmpty();
3599 bool ItemContainer::hasVisibleChildren(
bool excludeBeingInserted)
const
3601 for (Item *item : qAsConst(m_children)) {
3602 if (item->isVisible(excludeBeingInserted))
3609 int ItemContainer::numChildren()
const
3611 return m_children.size();
3614 int ItemContainer::numVisibleChildren()
const
3617 for (Item *child : qAsConst(m_children)) {
3618 if (child->isVisible())
3624 bool ItemContainer::isEmpty()
const
3626 return m_children.isEmpty();
3629 bool ItemContainer::hasSingleVisibleItem()
const
3631 return numVisibleChildren() == 1;
3634 bool ItemContainer::contains(
const Item *item)
const
3636 return m_children.contains(
const_cast<Item *
>(item));
3639 Item *ItemContainer::itemForObject(
const QObject *o)
const
3641 for (Item *item : qAsConst(m_children)) {
3642 if (item->isContainer()) {
3643 if (Item *result = item->asContainer()->itemForObject(o))
3645 }
else if (
auto guest = item->guestWidget()) {
3654 Item *ItemContainer::itemForWidget(
const Widget *w)
const
3656 for (Item *item : qAsConst(m_children)) {
3657 if (item->isContainer()) {
3658 if (Item *result = item->asContainer()->itemForWidget(w))
3660 }
else if (item->guestWidget() == w) {
3668 Item::List ItemContainer::visibleChildren(
bool includeBeingInserted)
const
3671 items.reserve(m_children.size());
3672 for (Item *item : qAsConst(m_children)) {
3673 if (includeBeingInserted) {
3674 if (item->isVisible() || item->isBeingInserted())
3677 if (item->isVisible() && !item->isBeingInserted())
3685 Item::List ItemContainer::items_recursive()
const
3689 for (Item *item : qAsConst(m_children)) {
3690 if (
auto c = item->asContainer()) {
3691 items << c->items_recursive();
3700 bool ItemContainer::contains_recursive(
const Item *item)
const
3702 for (Item *it : qAsConst(m_children)) {
3705 }
else if (it->isContainer()) {
3706 if (it->asContainer()->contains_recursive(item))
3715 int ItemContainer::visibleCount_recursive()
const
3718 for (Item *item : qAsConst(m_children)) {
3719 count += item->visibleCount_recursive();
3725 int ItemContainer::count_recursive()
const
3728 for (Item *item : qAsConst(m_children)) {
3729 if (
auto c = item->asContainer()) {
3730 count += c->count_recursive();
3740 #pragma warning(pop)