13 #include "Separator_p.h"
16 #include "ItemFreeContainer_p.h"
20 #include <QScopedValueRollback>
22 #include <QGuiApplication>
27 #pragma warning(disable : 4138)
28 #pragma warning(disable : 4244)
29 #pragma warning(disable : 4457)
30 #pragma warning(disable : 4702)
38 int Layouting::Item::separatorThickness = 5;
41 QSize Layouting::Item::hardcodedMinimumSize =
QSize(80, 90);
42 QSize Layouting::Item::hardcodedMaximumSize =
QSize(16777215, 16777215);
44 bool Layouting::ItemBoxContainer::s_inhibitSimplify =
false;
96 return qMax(0, length - minLength);
101 return qMax(0, minLength - length);
109 return m_parent ? m_parent->root()
110 :
const_cast<ItemBoxContainer *
>(qobject_cast<const ItemBoxContainer *>(
this));
125 return p + parentContainer()->mapToRoot(pos());
131 return mapToRoot(
QPoint(0, p)).
y();
132 return mapToRoot(
QPoint(p, 0)).x();
137 const Item *it =
this;
140 it = it->parentContainer();
164 return mapFromRoot(
QPoint(0, p)).y();
165 return mapFromRoot(
QPoint(p, 0)).x();
168 QObject *Item::guestAsQObject()
const
170 return m_guest ? m_guest->asQObject() :
nullptr;
173 void Item::setGuestWidget(
Widget *guest)
175 Q_ASSERT(!guest || !m_guest);
177 QObject *oldWidget = guestAsQObject();
181 disconnect(oldWidget,
nullptr,
this,
nullptr);
188 m_guest->setLayoutItem(
this);
199 connect(newWidget, SIGNAL(layoutInvalidated()),
this, SLOT(onWidgetLayoutRequested()));
201 if (m_sizingInfo.geometry.isEmpty()) {
203 QRect widgetGeo = m_guest->geometry();
205 widgetGeo.
size().
expandedTo(minSize()).expandedTo(Item::hardcodedMinimumSize));
206 setGeometry(mapFromRoot(widgetGeo));
208 updateWidgetGeometries();
215 void Item::updateWidgetGeometries()
218 m_guest->setGeometry(mapToRoot(rect()));
222 QVariantMap Item::toVariantMap()
const
226 result[QStringLiteral(
"sizingInfo")] = m_sizingInfo.toVariantMap();
227 result[QStringLiteral(
"isVisible")] = m_isVisible;
228 result[QStringLiteral(
"isContainer")] = isContainer();
229 result[QStringLiteral(
"objectName")] = objectName();
231 result[QStringLiteral(
"guestId")] = m_guest->id();
238 m_sizingInfo.fromVariantMap(map[QStringLiteral(
"sizingInfo")].toMap());
239 m_isVisible = map[QStringLiteral(
"isVisible")].toBool();
240 setObjectName(map[QStringLiteral(
"objectName")].toString());
242 const QString guestId = map.value(QStringLiteral(
"guestId")).toString();
245 setGuestWidget(guest);
246 m_guest->setParent(hostWidget());
247 }
else if (hostWidget()) {
248 qWarning() << Q_FUNC_INFO <<
"Couldn't find frame to restore for" <<
this;
256 auto item =
new Item(hostWidget, parent);
257 item->fillFromVariantMap(map, widgets);
268 Q_ASSERT(m_refCount > 0);
270 if (m_refCount == 0) {
272 parentContainer()->removeItem(
this);
276 int Item::refCount()
const
281 Widget *Item::hostWidget()
const
288 return m_hostWidget ? m_hostWidget->asQObject()
292 void Item::restore(
Widget *guest)
294 if (isVisible() || guestAsQObject()) {
295 qWarning() << Q_FUNC_INFO <<
"Hitting assert. visible="
296 << isVisible() <<
"; guest=" << guestAsQObject();
301 qWarning() << Q_FUNC_INFO <<
"Containers can't be restored";
303 setGuestWidget(guest);
304 parentContainer()->restore(
this);
323 const Item *it =
this;
325 if (
auto p = it->parentContainer()) {
326 const auto index = p->childItems().indexOf(
const_cast<Item *
>(it));
337 void Item::setHostWidget(
Widget *host)
339 if (m_hostWidget != host) {
343 m_guest->setVisible(
true);
344 updateWidgetGeometries();
349 void Item::setSize_recursive(
QSize newSize, ChildrenResizeStrategy)
354 QSize Item::missingSize()
const
356 QSize missing = minSize() - this->size();
363 bool Item::isBeingInserted()
const
365 return m_sizingInfo.isBeingInserted;
368 void Item::setBeingInserted(
bool is)
370 m_sizingInfo.isBeingInserted = is;
373 if (
auto parent = parentContainer()) {
375 if (!parent->hasVisibleChildren())
376 parent->setBeingInserted(
true);
378 parent->setBeingInserted(
false);
385 if (parent == m_parent)
389 disconnect(
this, &Item::minSizeChanged, m_parent, &ItemContainer::onChildMinSizeChanged);
390 disconnect(
this, &Item::visibleChanged, m_parent, &ItemContainer::onChildVisibleChanged);
391 Q_EMIT visibleChanged(
this,
false);
394 if (
auto c = asContainer()) {
395 const bool ceasingToBeRoot = !m_parent && parent;
396 if (ceasingToBeRoot && !c->hasVisibleChildren()) {
404 connectParent(parent);
412 connect(
this, &Item::minSizeChanged, parent, &ItemContainer::onChildMinSizeChanged);
413 connect(
this, &Item::visibleChanged, parent, &ItemContainer::onChildVisibleChanged);
415 setHostWidget(parent->hostWidget());
416 updateWidgetGeometries();
418 Q_EMIT visibleChanged(
this, isVisible());
429 return qobject_cast<ItemBoxContainer *>(m_parent);
434 return qobject_cast<const ItemContainer *>(
this);
439 return qobject_cast<ItemContainer *>(
this);
444 return qobject_cast<ItemBoxContainer *>(
this);
447 void Item::setMinSize(
QSize sz)
449 if (sz != m_sizingInfo.minSize) {
450 m_sizingInfo.minSize = sz;
451 Q_EMIT minSizeChanged(
this);
452 if (!m_isSettingGuest)
453 setSize_recursive(size().expandedTo(sz));
457 void Item::setMaxSizeHint(
QSize sz)
459 if (sz != m_sizingInfo.maxSizeHint) {
460 m_sizingInfo.maxSizeHint = sz;
461 Q_EMIT maxSizeChanged(
this);
465 QSize Item::minSize()
const
467 return m_sizingInfo.minSize;
470 QSize Item::maxSizeHint()
const
472 return m_sizingInfo.maxSizeHint.
boundedTo(hardcodedMaximumSize);
475 void Item::setPos(
QPoint pos)
477 QRect geo = m_sizingInfo.geometry;
485 setPos({ x(), pos });
487 setPos({ pos, y() });
498 return m_sizingInfo.geometry.x();
503 return m_sizingInfo.geometry.y();
506 int Item::width()
const
508 return m_sizingInfo.geometry.width();
511 int Item::height()
const
513 return m_sizingInfo.geometry.height();
516 QSize Item::size()
const
518 return m_sizingInfo.geometry.size();
521 void Item::setSize(
QSize sz)
523 QRect newGeo = m_sizingInfo.geometry;
530 return m_sizingInfo.geometry.topLeft();
533 QRect Item::geometry()
const
535 return isBeingInserted() ?
QRect()
536 : m_sizingInfo.geometry;
539 QRect Item::rect()
const
541 return QRect(0, 0, width(), height());
544 bool Item::isContainer()
const
546 return m_isContainer;
551 return Layouting::length(minSize(), o);
556 return Layouting::length(maxSizeHint(), o);
561 Q_ASSERT(length > 0);
563 const int w = qMax(width(), hardcodedMinimumSize.width());
564 setSize(
QSize(w, length));
566 const int h = qMax(height(), hardcodedMinimumSize.height());
567 setSize(
QSize(length, h));
573 setLength(length, o);
578 return Layouting::length(size(), o);
583 return length(o) - minLength(o);
586 bool Item::isPlaceholder()
const
591 bool Item::isVisible(
bool excludeBeingInserted)
const
593 return m_isVisible && !(excludeBeingInserted && isBeingInserted());
596 void Item::setIsVisible(
bool is)
598 if (is != m_isVisible) {
600 Q_EMIT visibleChanged(
this, is);
604 m_guest->setGeometry(mapToRoot(rect()));
605 m_guest->setVisible(
true);
611 void Item::setGeometry_recursive(
QRect rect)
617 bool Item::checkSanity()
622 if (minSize().width() > width() || minSize().height() > height()) {
623 root()->dumpLayout();
624 qWarning() << Q_FUNC_INFO <<
"Size constraints not honoured" <<
this
625 <<
"; min=" << minSize() <<
"; size=" << size();
630 if (m_guest->parent() != hostWidget()->asQObject()) {
632 root()->dumpLayout();
633 qWarning() << Q_FUNC_INFO <<
"Unexpected parent for our guest. guest.parent="
634 << m_guest->parent() <<
"; host=" << hostWidget()->
asQObject()
635 <<
"; guest.asObj=" << m_guest->asQObject()
637 <<
"; item.parentContainer=" << parentContainer()
638 <<
"; item.root.parent=" << (root() ? root()->parent() :
nullptr);
642 if (
false && !m_guest->isVisible() && (!m_guest->parent() || m_guest->parentWidget()->isVisible())) {
644 qWarning() << Q_FUNC_INFO <<
"Guest widget isn't visible" <<
this
645 << m_guest->asQObject();
649 if (m_guest->geometry() != mapToRoot(rect())) {
650 root()->dumpLayout();
652 d << Q_FUNC_INFO <<
"Guest widget doesn't have correct geometry. has"
653 <<
"guest.global=" << m_guest->geometry()
654 <<
"; item.local=" << geometry()
655 <<
"; item.global=" << mapToRoot(rect())
657 m_guest->dumpDebug(d);
666 bool Item::isMDI()
const
668 return qobject_cast<ItemFreeContainer *>(parentContainer()) !=
nullptr;
671 void Item::setGeometry(
QRect rect)
673 QRect &m_geometry = m_sizingInfo.geometry;
675 if (rect != m_geometry) {
676 const QRect oldGeo = m_geometry;
684 if (c->hasVisibleChildren()) {
690 qWarning() << Q_FUNC_INFO <<
"Empty rect";
694 const QSize minSz = minSize();
698 qWarning() << Q_FUNC_INFO <<
this <<
"Constraints not honoured."
699 <<
"sz=" << rect.
size() <<
"; min=" << minSz
700 <<
": parent=" << parentContainer();
703 Q_EMIT geometryChanged();
705 if (oldGeo.
x() != x())
707 if (oldGeo.
y() != y())
709 if (oldGeo.
width() != width())
710 Q_EMIT widthChanged();
711 if (oldGeo.
height() != height())
712 Q_EMIT heightChanged();
714 updateWidgetGeometries();
718 void Item::dumpLayout(
int level)
723 auto dbg = qDebug().noquote();
725 dbg << indent <<
"- Widget: " << objectName()
726 << m_sizingInfo.geometry
727 <<
"; min=" << minSize();
729 if (maxSizeHint() != hardcodedMaximumSize)
730 dbg <<
"; max=" << maxSizeHint();
733 dbg << QStringLiteral(
";hidden;");
735 if (m_guest && geometry() != m_guest->geometry()) {
736 dbg <<
"; guest geometry=" << m_guest->geometry();
739 if (m_sizingInfo.isBeingInserted)
740 dbg << QStringLiteral(
";beingInserted;");
742 dbg <<
this <<
"; guest=" << guestAsQObject();
747 , m_isContainer(false)
749 , m_hostWidget(hostWidget)
751 connectParent(parent);
756 , m_isContainer(isContainer)
758 , m_hostWidget(hostWidget)
760 connectParent(parent);
773 if (widget->
parent() != host) {
775 Q_ASSERT(isVisible());
776 turnIntoPlaceholder();
783 void Item::turnIntoPlaceholder()
785 Q_ASSERT(!isContainer());
790 parentContainer()->removeItem(
this,
false);
793 void Item::updateObjectName()
798 if (
auto w = guestAsQObject()) {
799 setObjectName(w->objectName().isEmpty() ? QStringLiteral(
"widget") : w->objectName());
800 }
else if (!isVisible()) {
801 setObjectName(QStringLiteral(
"hidden"));
802 }
else if (!m_guest) {
803 setObjectName(QStringLiteral(
"null"));
805 setObjectName(QStringLiteral(
"empty"));
809 void Item::onWidgetDestroyed()
814 turnIntoPlaceholder();
815 }
else if (!isRoot()) {
816 parentContainer()->removeItem(
this);
820 void Item::onWidgetLayoutRequested()
822 if (
Widget *w = guestWidget()) {
823 if (w->size() != size() && !isMDI()) {
824 qDebug() << Q_FUNC_INFO <<
"TODO: Not implemented yet. Widget can't just decide to resize yet"
827 << m_sizingInfo.geometry
828 << m_sizingInfo.isBeingInserted;
831 if (w->minSize() != minSize()) {
832 setMinSize(m_guest->minSize());
835 setMaxSizeHint(w->maxSizeHint());
839 bool Item::isRoot()
const
841 return m_parent ==
nullptr;
844 LayoutBorderLocations Item::adjacentLayoutBorders()
const
847 return LayoutBorderLocation_All;
852 return LayoutBorderLocation_None;
854 const int indexInParent = c->indexOfVisibleChild(
this);
855 const int numVisibleChildren = c->numVisibleChildren();
856 const bool isFirst = indexInParent == 0;
857 const bool isLast = indexInParent == numVisibleChildren - 1;
858 if (indexInParent == -1)
859 return LayoutBorderLocation_None;
861 LayoutBorderLocations locations = LayoutBorderLocation_None;
863 if (c->isVertical()) {
864 locations |= LayoutBorderLocation_West;
865 locations |= LayoutBorderLocation_East;
868 locations |= LayoutBorderLocation_North;
870 locations |= LayoutBorderLocation_South;
872 locations |= LayoutBorderLocation_North;
873 locations |= LayoutBorderLocation_South;
876 locations |= LayoutBorderLocation_West;
878 locations |= LayoutBorderLocation_East;
881 const LayoutBorderLocations parentBorders = c->adjacentLayoutBorders();
882 if (c->isVertical()) {
883 if (parentBorders & LayoutBorderLocation_West)
884 locations |= LayoutBorderLocation_West;
886 if (parentBorders & LayoutBorderLocation_East)
887 locations |= LayoutBorderLocation_East;
889 if (isFirst && (parentBorders & LayoutBorderLocation_North))
890 locations |= LayoutBorderLocation_North;
892 if (isLast && (parentBorders & LayoutBorderLocation_South))
893 locations |= LayoutBorderLocation_South;
896 if (parentBorders & LayoutBorderLocation_North)
897 locations |= LayoutBorderLocation_North;
899 if (parentBorders & LayoutBorderLocation_South)
900 locations |= LayoutBorderLocation_South;
902 if (isFirst && (parentBorders & LayoutBorderLocation_West))
903 locations |= LayoutBorderLocation_West;
905 if (isLast && (parentBorders & LayoutBorderLocation_East))
906 locations |= LayoutBorderLocation_East;
913 int Item::visibleCount_recursive()
const
915 return isVisible() ? 1 : 0;
918 struct ItemBoxContainer::Private
927 qDeleteAll(m_separators);
928 m_separators.clear();
931 int defaultLengthFor(Item *item,
InitialOption option)
const;
932 bool isOverflowing()
const;
933 void relayoutIfNeeded();
934 const Item *itemFromPath(
const QVector<int> &path)
const;
935 void resizeChildren(
QSize oldSize,
QSize newSize, SizingInfo::List &sizes, ChildrenResizeStrategy);
936 void honourMaxSizes(SizingInfo::List &sizes);
937 void scheduleCheckSanity()
const;
940 void updateWidgets_recursive();
943 void updateSeparators();
944 void deleteSeparators();
947 bool isDummy()
const;
948 void deleteSeparators_recursive();
949 void updateSeparators_recursive();
950 QSize minSize(
const Item::List &items)
const;
951 int excessLength()
const;
953 mutable bool m_checkSanityScheduled =
false;
955 bool m_convertingItemToContainer =
false;
956 bool m_blockUpdatePercentages =
false;
957 bool m_isDeserializing =
false;
958 bool m_isSimplifying =
false;
965 , d(new Private(this))
970 ItemBoxContainer::ItemBoxContainer(
Widget *hostWidget)
972 , d(new Private(this))
976 ItemBoxContainer::~ItemBoxContainer()
981 bool ItemBoxContainer::checkSanity()
983 d->m_checkSanityScheduled =
false;
990 if (!Item::checkSanity())
993 if (numChildren() == 0 && !isRoot()) {
994 qWarning() << Q_FUNC_INFO <<
"Container is empty. Should be deleted";
999 qWarning() << Q_FUNC_INFO <<
"Invalid orientation" << d->m_orientation <<
this;
1004 int expectedPos = 0;
1005 const auto children = childItems();
1006 for (Item *item : children) {
1007 if (!item->isVisible())
1009 const int pos = Layouting::pos(item->pos(), d->m_orientation);
1010 if (expectedPos != pos) {
1011 root()->dumpLayout();
1012 qWarning() << Q_FUNC_INFO <<
"Unexpected pos" << pos <<
"; expected=" << expectedPos
1013 <<
"; for item=" << item
1014 <<
"; isContainer=" << item->isContainer();
1018 expectedPos = pos + Layouting::length(item->size(), d->m_orientation) + separatorThickness;
1022 for (Item *item : children) {
1023 if (item->parentContainer() !=
this) {
1024 qWarning() <<
"Invalid parent container for" << item
1025 <<
"; is=" << item->parentContainer() <<
"; expected=" <<
this;
1029 if (item->parent() !=
this) {
1030 qWarning() <<
"Invalid QObject parent for" << item
1031 <<
"; is=" << item->parent() <<
"; expected=" <<
this;
1035 if (item->isVisible()) {
1039 root()->dumpLayout();
1040 qWarning() << Q_FUNC_INFO <<
"Invalid size for item." << item
1041 <<
"Container.length=" << h1 <<
"; item.length=" << h2;
1045 if (!rect().contains(item->geometry())) {
1046 root()->dumpLayout();
1047 qWarning() << Q_FUNC_INFO <<
"Item geo is out of bounds. item=" << item <<
"; geo="
1048 << item->geometry() <<
"; parent.rect=" << rect();
1053 if (!item->checkSanity())
1057 const Item::List visibleChildren = this->visibleChildren();
1058 const bool isEmptyRoot = isRoot() && visibleChildren.isEmpty();
1060 auto occupied = qMax(0, Item::separatorThickness * (visibleChildren.size() - 1));
1061 for (Item *item : visibleChildren) {
1062 occupied += item->length(d->m_orientation);
1065 if (occupied != length()) {
1066 root()->dumpLayout();
1067 qWarning() << Q_FUNC_INFO <<
"Unexpected length. Expected=" << occupied
1068 <<
"; got=" << length() <<
"; this=" <<
this;
1073 const double totalPercentage = std::accumulate(percentages.
begin(), percentages.
end(), 0.0);
1074 const double expectedPercentage = visibleChildren.isEmpty() ? 0.0 : 1.0;
1075 if (!qFuzzyCompare(totalPercentage, expectedPercentage)) {
1076 root()->dumpLayout();
1077 qWarning() << Q_FUNC_INFO <<
"Percentages don't add up"
1078 << totalPercentage << percentages
1081 qWarning() << Q_FUNC_INFO << d->childPercentages();
1086 const auto numVisibleChildren = visibleChildren.size();
1087 if (d->m_separators.size() != qMax(0, numVisibleChildren - 1)) {
1088 root()->dumpLayout();
1089 qWarning() << Q_FUNC_INFO <<
"Unexpected number of separators" << d->m_separators.size()
1090 << numVisibleChildren;
1094 const QSize expectedSeparatorSize = isVertical() ?
QSize(width(), Item::separatorThickness)
1095 :
QSize(Item::separatorThickness, height());
1099 for (
int i = 0; i < d->m_separators.size(); ++i) {
1100 Separator *separator = d->m_separators.at(i);
1101 Item *item = visibleChildren.at(i);
1102 const int expectedSeparatorPos = mapToRoot(item->m_sizingInfo.edge(d->m_orientation) + 1, d->m_orientation);
1104 if (separator->host() != host()) {
1105 qWarning() << Q_FUNC_INFO <<
"Invalid host widget for separator"
1106 << separator->host() << host() <<
this;
1110 if (separator->parentContainer() !=
this) {
1111 qWarning() << Q_FUNC_INFO <<
"Invalid parent container for separator"
1112 << separator->parentContainer() << separator <<
this;
1116 if (separator->position() != expectedSeparatorPos) {
1117 root()->dumpLayout();
1118 qWarning() << Q_FUNC_INFO <<
"Unexpected separator position" << separator->position()
1119 <<
"; expected=" << expectedSeparatorPos
1120 << separator <<
"; this=" <<
this;
1124 Widget *separatorWidget = separator->asWidget();
1125 if (separatorWidget->
geometry().
size() != expectedSeparatorSize) {
1126 qWarning() << Q_FUNC_INFO <<
"Unexpected separator size" << separatorWidget->
geometry().
size()
1127 <<
"; expected=" << expectedSeparatorSize
1128 << separator <<
"; this=" <<
this;
1134 root()->dumpLayout();
1135 qWarning() << Q_FUNC_INFO <<
"Unexpected position pos2=" << separatorPos2
1136 <<
"; expected=" << pos2
1137 << separator <<
"; this=" <<
this;
1141 if (separator->host() != host()) {
1142 qWarning() << Q_FUNC_INFO <<
"Unexpected host widget in separator"
1143 << separator->host() <<
"; expected=" << host();
1148 const int separatorMinPos = minPosForSeparator_global(separator,
false);
1149 const int separatorMaxPos = maxPosForSeparator_global(separator,
false);
1150 const int separatorPos = separator->position();
1151 if (separatorPos < separatorMinPos || separatorPos > separatorMaxPos || separatorMinPos < 0 || separatorMaxPos <= 0) {
1152 root()->dumpLayout();
1153 qWarning() << Q_FUNC_INFO <<
"Invalid bounds for separator, pos="
1154 << separatorPos <<
"; min=" << separatorMinPos
1155 <<
"; max=" << separatorMaxPos
1161 #ifdef DOCKS_DEVELOPER_MODE
1164 if (!asBoxContainer()->test_suggestedRect())
1172 void ItemBoxContainer::Private::scheduleCheckSanity()
const
1174 if (!m_checkSanityScheduled) {
1175 m_checkSanityScheduled =
true;
1180 bool ItemBoxContainer::hasOrientation()
const
1182 return isVertical() || isHorizontal();
1185 int ItemBoxContainer::indexOfVisibleChild(
const Item *item)
const
1187 const Item::List items = visibleChildren();
1188 return items.indexOf(
const_cast<Item *
>(item));
1191 void ItemBoxContainer::restore(Item *child)
1193 restoreChild(child, NeighbourSqueezeStrategy::ImmediateNeighboursFirst);
1196 void ItemBoxContainer::removeItem(Item *item,
bool hardRemove)
1198 Q_ASSERT(!item->isRoot());
1200 if (!contains(item)) {
1202 item->parentContainer()->removeItem(item, hardRemove);
1206 Item *side1Item = visibleNeighbourFor(item, Side1);
1207 Item *side2Item = visibleNeighbourFor(item, Side2);
1209 const bool isContainer = item->isContainer();
1210 const bool wasVisible = !isContainer && item->isVisible();
1213 m_children.removeOne(item);
1216 Q_EMIT root()->numItemsChanged();
1218 item->setIsVisible(
false);
1219 item->setGuestWidget(
nullptr);
1221 if (!wasVisible && !isContainer) {
1228 Q_EMIT root()->numVisibleItemsChanged(root()->numVisibleChildren());
1233 if (
auto p = parentContainer())
1234 p->removeItem(
this,
true);
1235 }
else if (!hardRemove && !hasVisibleChildren()) {
1236 if (
auto p = parentContainer()) {
1237 p->removeItem(
this,
false);
1238 setGeometry(
QRect());
1242 growNeighbours(side1Item, side2Item);
1243 Q_EMIT itemsChanged();
1245 updateSizeConstraints();
1246 d->updateSeparators_recursive();
1250 void ItemBoxContainer::setGeometry_recursive(
QRect rect)
1255 setSize_recursive(rect.
size());
1262 const auto index = m_children.indexOf(leaf);
1263 Q_ASSERT(index != -1);
1265 container->setParentContainer(
nullptr);
1266 container->setParentContainer(
this);
1268 insertItem(container, index, DefaultSizeMode::NoDefaultSizeMode);
1269 m_children.removeOne(leaf);
1270 container->setGeometry(leaf->geometry());
1271 container->insertItem(leaf,
Location_OnTop, DefaultSizeMode::NoDefaultSizeMode);
1272 Q_EMIT itemsChanged();
1273 d->updateSeparators_recursive();
1279 void ItemBoxContainer::insertItemRelativeTo(Item *item, Item *relativeTo,
1282 Q_ASSERT(item != relativeTo);
1284 if (
auto asContainer = relativeTo->asBoxContainer()) {
1285 asContainer->insertItem(item, loc, option);
1290 Q_ASSERT(!(option.
startsHidden() && item->isContainer()));
1294 qWarning() << Q_FUNC_INFO <<
"This method should only be called for box containers"
1299 if (parent->hasOrientationFor(loc)) {
1301 auto indexInParent = parent->childItems().indexOf(relativeTo);
1306 if (orientation != parent->orientation()) {
1307 Q_ASSERT(parent->visibleChildren().size() == 1);
1310 parent->setOrientation(orientation);
1313 parent->insertItem(item, indexInParent, option);
1316 container->insertItem(item, loc, option);
1320 void ItemBoxContainer::insertItem(Item *item,
Location loc,
1323 Q_ASSERT(item !=
this);
1324 if (contains(item)) {
1325 qWarning() << Q_FUNC_INFO <<
"Item already exists";
1330 Q_ASSERT(!(initialOption.
startsHidden() && item->isContainer()));
1334 if (hasOrientationFor(loc)) {
1335 if (m_children.size() == 1) {
1337 d->m_orientation = locOrientation;
1341 insertItem(item, index, initialOption);
1346 container->setGeometry(rect());
1347 container->setChildren(m_children, d->m_orientation);
1350 insertItem(container, 0, DefaultSizeMode::NoDefaultSizeMode);
1353 insertItem(item, loc, initialOption);
1355 if (!container->hasVisibleChildren())
1356 container->setGeometry(
QRect());
1359 d->updateSeparators_recursive();
1360 d->scheduleCheckSanity();
1363 void ItemBoxContainer::onChildMinSizeChanged(Item *child)
1365 if (d->m_convertingItemToContainer || d->m_isDeserializing || !child->isVisible()) {
1370 updateSizeConstraints();
1372 if (child->isBeingInserted())
1375 if (numVisibleChildren() == 1 && child->isVisible()) {
1377 child->setGeometry(rect());
1378 updateChildPercentages();
1382 const QSize missingForChild = child->missingSize();
1383 if (!missingForChild.
isNull()) {
1385 growItem(child, Layouting::length(missingForChild, d->m_orientation), GrowthStrategy::BothSidesEqually, NeighbourSqueezeStrategy::AllNeighbours);
1388 updateChildPercentages();
1391 void ItemBoxContainer::updateSizeConstraints()
1393 const QSize missingSize = this->missingSize();
1394 if (!missingSize.
isNull()) {
1397 setSize_recursive(size() + missingSize);
1402 Q_EMIT minSizeChanged(
this);
1405 void ItemBoxContainer::onChildVisibleChanged(Item *,
bool visible)
1407 if (d->m_isDeserializing || isInSimplify())
1410 const int numVisible = numVisibleChildren();
1411 if (visible && numVisible == 1) {
1413 Q_EMIT visibleChanged(
this,
true);
1414 }
else if (!visible && numVisible == 0) {
1415 Q_EMIT visibleChanged(
this,
false);
1419 QRect ItemBoxContainer::suggestedDropRect(
const Item *item,
const Item *relativeTo,
Location loc)
const
1429 if (relativeTo && !relativeTo->parentContainer()) {
1430 qWarning() << Q_FUNC_INFO <<
"No parent container";
1434 if (relativeTo && relativeTo->parentContainer() !=
this) {
1435 qWarning() << Q_FUNC_INFO <<
"Called on the wrong container";
1439 if (relativeTo && !relativeTo->isVisible()) {
1440 qWarning() << Q_FUNC_INFO <<
"relative to isn't visible";
1445 qWarning() << Q_FUNC_INFO <<
"Invalid location";
1449 const QSize availableSize = root()->availableSize();
1450 const QSize minSize = item->minSize();
1451 const bool isEmpty = !root()->hasVisibleChildren();
1452 const int extraWidth = (isEmpty ||
locationIsVertical(loc)) ? 0 : Item::separatorThickness;
1453 const int extraHeight = (isEmpty || !
locationIsVertical(loc)) ? 0 : Item::separatorThickness;
1454 const bool windowNeedsGrowing = availableSize.
width() < minSize.
width() + extraWidth || availableSize.
height() < minSize.
height() + extraHeight;
1456 if (windowNeedsGrowing)
1457 return suggestedDropRectFallback(item, relativeTo, loc);
1459 const QVariantMap rootSerialized = root()->toVariantMap();
1461 rootCopy.fillFromVariantMap(rootSerialized, {});
1464 relativeTo = rootCopy.d->itemFromPath(relativeTo->pathFromRoot());
1466 const QVariantMap itemSerialized = item->toVariantMap();
1467 auto itemCopy =
new Item(
nullptr);
1468 itemCopy->fillFromVariantMap(itemSerialized, {});
1471 auto r =
const_cast<Item *
>(relativeTo);
1472 ItemBoxContainer::insertItemRelativeTo(itemCopy, r, loc, DefaultSizeMode::FairButFloor);
1474 rootCopy.insertItem(itemCopy, loc, DefaultSizeMode::FairButFloor);
1477 if (rootCopy.size() != root()->size()) {
1479 qWarning() << Q_FUNC_INFO <<
"The root copy grew ?!" << rootCopy.size() << root()->size()
1481 return suggestedDropRectFallback(item, relativeTo, loc);
1484 return itemCopy->mapToRoot(itemCopy->rect());
1487 QRect ItemBoxContainer::suggestedDropRectFallback(
const Item *item,
const Item *relativeTo,
Location loc)
const
1489 const QSize minSize = item->minSize();
1490 const int itemMin = Layouting::length(minSize, d->m_orientation);
1491 const int available = availableLength() - Item::separatorThickness;
1493 int suggestedPos = 0;
1494 const QRect relativeToGeo = relativeTo->geometry();
1498 suggestedPos = relativeToGeo.
x();
1501 suggestedPos = relativeToGeo.
y();
1504 suggestedPos = relativeToGeo.
right() - suggestedLength + 1;
1507 suggestedPos = relativeToGeo.
bottom() - suggestedLength + 1;
1516 rect.
setSize(
QSize(relativeTo->width(), suggestedLength));
1519 rect.
setSize(
QSize(suggestedLength, relativeTo->height()));
1522 return mapToRoot(rect);
1523 }
else if (isRoot()) {
1525 QRect rect = this->rect();
1526 const int oneThird = length() / 3;
1527 const int suggestedLength = qMax(qMin(available, oneThird), itemMin);
1537 rect.
adjust(rect.
width() - suggestedLength, 0, 0, 0);
1549 qWarning() << Q_FUNC_INFO <<
"Shouldn't happen";
1555 void ItemBoxContainer::positionItems()
1557 SizingInfo::List sizes = this->sizes();
1558 positionItems(sizes);
1559 applyPositions(sizes);
1561 d->updateSeparators_recursive();
1564 void ItemBoxContainer::positionItems_recursive()
1567 for (Item *item : qAsConst(m_children)) {
1568 if (item->isVisible()) {
1569 if (
auto c = item->asBoxContainer())
1570 c->positionItems_recursive();
1575 void ItemBoxContainer::applyPositions(
const SizingInfo::List &sizes)
1577 const Item::List items = visibleChildren();
1578 const auto count = items.size();
1579 Q_ASSERT(count == sizes.size());
1580 for (
int i = 0; i < count; ++i) {
1581 Item *item = items.at(i);
1582 const SizingInfo &sizing = sizes[i];
1583 if (sizing.isBeingInserted) {
1591 item->setPos(sizing.geometry.topLeft());
1597 return d->m_orientation;
1600 void ItemBoxContainer::positionItems(SizingInfo::List &sizes)
1603 const auto count = sizes.count();
1605 for (
auto i = 0; i < count; ++i) {
1606 SizingInfo &sizing = sizes[i];
1607 if (sizing.isBeingInserted) {
1608 nextPos += Item::separatorThickness;
1617 sizing.setPos(nextPos, d->m_orientation);
1618 nextPos += sizing.length(d->m_orientation) + Item::separatorThickness;
1622 void ItemBoxContainer::clear()
1624 for (Item *item : qAsConst(m_children)) {
1631 d->deleteSeparators();
1634 Item *ItemBoxContainer::itemAt(
QPoint p)
const
1636 for (Item *item : qAsConst(m_children)) {
1637 if (item->isVisible() && item->geometry().contains(p))
1644 Item *ItemBoxContainer::itemAt_recursive(
QPoint p)
const
1646 if (Item *item = itemAt(p)) {
1647 if (
auto c = item->asBoxContainer()) {
1648 return c->itemAt_recursive(c->mapFromParent(p));
1657 void ItemBoxContainer::setHostWidget(
Widget *host)
1659 Item::setHostWidget(host);
1660 d->deleteSeparators_recursive();
1661 for (Item *item : qAsConst(m_children)) {
1662 item->setHostWidget(host);
1665 d->updateSeparators_recursive();
1668 void ItemBoxContainer::setIsVisible(
bool)
1673 bool ItemBoxContainer::isVisible(
bool excludeBeingInserted)
const
1675 return hasVisibleChildren(excludeBeingInserted);
1678 void ItemBoxContainer::setLength_recursive(
int length,
Qt::Orientation o)
1687 setSize_recursive(sz);
1690 void ItemBoxContainer::insertItem(Item *item,
int index,
InitialOption option)
1692 if (option.sizeMode != DefaultSizeMode::NoDefaultSizeMode) {
1694 const int suggestedLength = d->defaultLengthFor(item, option);
1695 item->setLength_recursive(suggestedLength, d->m_orientation);
1698 m_children.insert(index, item);
1699 item->setParentContainer(
this);
1701 Q_EMIT itemsChanged();
1703 if (!d->m_convertingItemToContainer && item->isVisible())
1706 const bool shouldEmitVisibleChanged = item->isVisible();
1708 if (!d->m_convertingItemToContainer && !s_inhibitSimplify)
1711 if (shouldEmitVisibleChanged)
1712 Q_EMIT root()->numVisibleItemsChanged(root()->numVisibleChildren());
1713 Q_EMIT root()->numItemsChanged();
1716 bool ItemBoxContainer::hasOrientationFor(
Location loc)
const
1718 if (m_children.size() <= 1)
1724 int ItemBoxContainer::usableLength()
const
1726 const Item::List children = visibleChildren();
1727 const auto numVisibleChildren = children.size();
1729 if (children.size() <= 1)
1730 return Layouting::length(size(), d->m_orientation);
1732 const int separatorWaste = separatorThickness * (numVisibleChildren - 1);
1733 return length() - separatorWaste;
1736 void ItemBoxContainer::setChildren(
const List &children,
Qt::Orientation o)
1738 m_children = children;
1739 for (Item *item : children)
1740 item->setParentContainer(
this);
1747 if (o != d->m_orientation) {
1748 d->m_orientation = o;
1749 d->updateSeparators_recursive();
1753 QSize ItemBoxContainer::Private::minSize(
const Item::List &items)
const
1758 if (!q->m_children.isEmpty()) {
1759 for (Item *item : items) {
1760 if (!(item->isVisible() || item->isBeingInserted()))
1763 if (q->isVertical()) {
1764 minW = qMax(minW, item->minSize().width());
1765 minH += item->minSize().height();
1767 minH = qMax(minH, item->minSize().height());
1768 minW += item->minSize().width();
1772 const int separatorWaste = qMax(0, (numVisible - 1) * separatorThickness);
1773 if (q->isVertical())
1774 minH += separatorWaste;
1776 minW += separatorWaste;
1779 return QSize(minW, minH);
1782 QSize ItemBoxContainer::minSize()
const
1784 return d->minSize(m_children);
1787 QSize ItemBoxContainer::maxSizeHint()
const
1789 int maxW = isVertical() ? hardcodedMaximumSize.
width() : 0;
1790 int maxH = isVertical() ? 0 : hardcodedMaximumSize.height();
1792 const Item::List visibleChildren = this->visibleChildren(
false);
1793 if (!visibleChildren.isEmpty()) {
1794 for (Item *item : visibleChildren) {
1795 if (item->isBeingInserted())
1797 const QSize itemMaxSz = item->maxSizeHint();
1798 const int itemMaxWidth = itemMaxSz.
width();
1799 const int itemMaxHeight = itemMaxSz.
height();
1801 maxW = qMin(maxW, itemMaxWidth);
1802 maxH = qMin(maxH + itemMaxHeight, hardcodedMaximumSize.height());
1804 maxH = qMin(maxH, itemMaxHeight);
1805 maxW = qMin(maxW + itemMaxWidth, hardcodedMaximumSize.width());
1809 const auto separatorWaste = (visibleChildren.size() - 1) * separatorThickness;
1811 maxH = qMin(maxH + separatorWaste, hardcodedMaximumSize.height());
1813 maxW = qMin(maxW + separatorWaste, hardcodedMaximumSize.width());
1818 maxW = hardcodedMaximumSize.width();
1821 maxH = hardcodedMaximumSize.height();
1826 void ItemBoxContainer::Private::resizeChildren(
QSize oldSize,
QSize newSize, SizingInfo::List &childSizes,
1827 ChildrenResizeStrategy strategy)
1834 const auto count = childSizes.count();
1835 const bool widthChanged = oldSize.
width() != newSize.
width();
1836 const bool heightChanged = oldSize.
height() != newSize.
height();
1837 const bool lengthChanged = (q->isVertical() && heightChanged) || (q->isHorizontal() && widthChanged);
1838 const int totalNewLength = q->usableLength();
1840 if (strategy == ChildrenResizeStrategy::Percentage) {
1844 int remaining = totalNewLength;
1845 for (
int i = 0; i < count; ++i) {
1846 const bool isLast = i == count - 1;
1848 SizingInfo &itemSize = childSizes[i];
1850 const qreal childPercentage = childPercentages.
at(i);
1851 const int newItemLength = lengthChanged ? (isLast ? remaining
1852 : int(childPercentage * totalNewLength))
1853 : itemSize.length(m_orientation);
1855 if (newItemLength <= 0) {
1856 q->root()->dumpLayout();
1857 qWarning() << Q_FUNC_INFO <<
"Invalid resize newItemLength=" << newItemLength;
1862 remaining = remaining - newItemLength;
1864 if (q->isVertical()) {
1865 itemSize.geometry.setSize({ q->width(), newItemLength });
1867 itemSize.geometry.setSize({ newItemLength, q->height() });
1870 }
else if (strategy == ChildrenResizeStrategy::Side1SeparatorMove || strategy == ChildrenResizeStrategy::Side2SeparatorMove) {
1871 int remaining = Layouting::length(newSize - oldSize, m_orientation);
1872 const bool isGrowing = remaining > 0;
1873 remaining = qAbs(remaining);
1880 const bool isSide1SeparatorMove = strategy == ChildrenResizeStrategy::Side1SeparatorMove;
1881 bool resizeHeadFirst =
false;
1882 if (isGrowing && isSide1SeparatorMove) {
1883 resizeHeadFirst =
true;
1884 }
else if (isGrowing && !isSide1SeparatorMove) {
1885 resizeHeadFirst =
false;
1886 }
else if (!isGrowing && isSide1SeparatorMove) {
1887 resizeHeadFirst =
false;
1888 }
else if (!isGrowing && !isSide1SeparatorMove) {
1889 resizeHeadFirst =
true;
1892 for (
int i = 0; i < count; i++) {
1893 const auto index = resizeHeadFirst ? i : count - 1 - i;
1895 SizingInfo &size = childSizes[index];
1899 size.incrementLength(remaining, m_orientation);
1902 const int availableToGive = size.availableLength(m_orientation);
1903 const int took = qMin(availableToGive, remaining);
1904 size.incrementLength(-took, m_orientation);
1912 honourMaxSizes(childSizes);
1915 void ItemBoxContainer::Private::honourMaxSizes(SizingInfo::List &sizes)
1920 int amountNeededToShrink = 0;
1921 int amountAvailableToGrow = 0;
1925 for (
int i = 0; i < sizes.count(); ++i) {
1926 SizingInfo &info = sizes[i];
1927 const int neededToShrink = info.neededToShrink(m_orientation);
1928 const int availableToGrow = info.availableToGrow(m_orientation);
1930 if (neededToShrink > 0) {
1931 amountNeededToShrink += neededToShrink;
1933 }
else if (availableToGrow > 0) {
1934 amountAvailableToGrow = qMin(amountAvailableToGrow + availableToGrow, q->length());
1940 amountAvailableToGrow = qMin(amountNeededToShrink, amountAvailableToGrow);
1943 amountNeededToShrink = qMin(amountAvailableToGrow, amountNeededToShrink);
1945 if (amountNeededToShrink == 0 || amountAvailableToGrow == 0)
1952 while (amountAvailableToGrow > 0) {
1954 auto toGrow = qMax(1, amountAvailableToGrow / indexesOfGrowers.
size());
1956 for (
auto it = indexesOfGrowers.
begin(); it != indexesOfGrowers.
end();) {
1957 const int index = *it;
1958 SizingInfo &sizing = sizes[index];
1959 const auto grew = qMin(sizing.availableToGrow(m_orientation), toGrow);
1960 sizing.incrementLength(grew, m_orientation);
1961 amountAvailableToGrow -= grew;
1963 if (amountAvailableToGrow == 0) {
1968 if (sizing.availableToGrow(m_orientation) == 0) {
1970 it = indexesOfGrowers.
erase(it);
1978 while (amountNeededToShrink > 0) {
1980 auto toShrink = qMax(1, amountNeededToShrink / indexesOfShrinkers.
size());
1982 for (
auto it = indexesOfShrinkers.
begin(); it != indexesOfShrinkers.
end();) {
1983 const int index = *it;
1984 SizingInfo &sizing = sizes[index];
1985 const auto shrunk = qMin(sizing.neededToShrink(m_orientation), toShrink);
1986 sizing.incrementLength(-shrunk, m_orientation);
1987 amountNeededToShrink -= shrunk;
1989 if (amountNeededToShrink == 0) {
1994 if (sizing.neededToShrink(m_orientation) == 0) {
1996 it = indexesOfShrinkers.
erase(it);
2004 void ItemBoxContainer::setSize_recursive(
QSize newSize, ChildrenResizeStrategy strategy)
2008 const QSize minSize = this->minSize();
2010 root()->dumpLayout();
2011 qWarning() << Q_FUNC_INFO <<
"New size doesn't respect size constraints"
2012 <<
"; new=" << newSize
2013 <<
"; min=" << minSize
2017 if (newSize == size())
2020 const QSize oldSize = size();
2023 const Item::List children = visibleChildren();
2024 const auto count = children.size();
2025 SizingInfo::List childSizes = sizes();
2035 d->resizeChildren(oldSize, newSize, childSizes, strategy);
2038 positionItems( childSizes);
2041 for (
int i = 0; i < count; ++i) {
2042 SizingInfo &size = childSizes[i];
2043 const int missing = size.missingLength(d->m_orientation);
2045 growItem(i, childSizes, missing, GrowthStrategy::BothSidesEqually, NeighbourSqueezeStrategy::AllNeighbours);
2049 applyGeometries(childSizes, strategy);
2052 int ItemBoxContainer::length()
const
2054 return isVertical() ? height() : width();
2057 void ItemBoxContainer::dumpLayout(
int level)
2059 if (level == 0 && hostWidget()) {
2061 const auto screens = qApp->screens();
2062 for (
auto screen : screens) {
2063 qDebug().noquote() <<
"Screen" << screen->geometry() << screen->availableGeometry()
2064 <<
"; drp=" << screen->devicePixelRatio();
2067 hostWidget()->
dumpDebug(qDebug().noquote());
2072 const QString beingInserted = m_sizingInfo.isBeingInserted ? QStringLiteral(
"; beingInserted;")
2074 const QString visible = !isVisible() ? QStringLiteral(
";hidden;")
2077 const QString typeStr = isRoot() ? QStringLiteral(
"* Root: ")
2078 : QStringLiteral(
"* Layout: ");
2081 auto dbg = qDebug().noquote();
2082 dbg << indent << typeStr << d->m_orientation
2083 << m_sizingInfo.geometry
2084 <<
"; min=" << minSize()
2085 <<
"; this=" <<
this << beingInserted << visible
2086 <<
"; %=" << d->childPercentages();
2088 if (maxSizeHint() != Item::hardcodedMaximumSize)
2089 dbg <<
"; max=" << maxSizeHint();
2093 for (Item *item : qAsConst(m_children)) {
2094 item->dumpLayout(level + 1);
2095 if (item->isVisible()) {
2096 if (i < d->m_separators.size()) {
2097 auto separator = d->m_separators.at(i);
2098 qDebug().noquote() << indent <<
" - Separator: "
2099 <<
"local.geo=" << mapFromRoot(separator->asWidget()->geometry())
2100 <<
"global.geo=" << separator->asWidget()->geometry()
2108 void ItemBoxContainer::updateChildPercentages()
2110 if (root()->d->m_blockUpdatePercentages)
2113 const int usable = usableLength();
2114 for (Item *item : qAsConst(m_children)) {
2115 if (item->isVisible() && !item->isBeingInserted()) {
2116 item->m_sizingInfo.percentageWithinParent = (1.0 * item->length(d->m_orientation)) / usable;
2118 item->m_sizingInfo.percentageWithinParent = 0.0;
2123 void ItemBoxContainer::updateChildPercentages_recursive()
2125 updateChildPercentages();
2126 for (Item *item : qAsConst(m_children)) {
2127 if (
auto c = item->asBoxContainer())
2128 c->updateChildPercentages_recursive();
2135 percentages.
reserve(q->m_children.size());
2137 for (Item *item : qAsConst(q->m_children)) {
2138 if (item->isVisible() && !item->isBeingInserted())
2139 percentages << item->m_sizingInfo.percentageWithinParent;
2145 void ItemBoxContainer::restoreChild(Item *item, NeighbourSqueezeStrategy neighbourSqueezeStrategy)
2147 Q_ASSERT(contains(item));
2149 const bool hadVisibleChildren = hasVisibleChildren(
true);
2151 item->setIsVisible(
true);
2152 item->setBeingInserted(
true);
2154 const int excessLength = d->excessLength();
2156 if (!hadVisibleChildren) {
2158 if (
auto c = parentBoxContainer()) {
2159 setSize(item->size());
2160 c->restoreChild(
this, neighbourSqueezeStrategy);
2165 updateSizeConstraints();
2167 item->setBeingInserted(
false);
2169 if (numVisibleChildren() == 1) {
2171 item->setGeometry_recursive(rect());
2172 d->updateSeparators_recursive();
2176 const int available = availableToSqueezeOnSide(item, Side1) + availableToSqueezeOnSide(item, Side2) - Item::separatorThickness;
2178 const int max = qMin(available, item->maxLengthHint(d->m_orientation));
2179 const int min = item->minLength(d->m_orientation);
2187 const int proposed = qMax(Layouting::length(item->size(), d->m_orientation), excessLength - Item::separatorThickness);
2188 const int newLength = qBound(min, proposed, max);
2190 Q_ASSERT(item->isVisible());
2194 item->m_sizingInfo.geometry.setHeight(0);
2196 item->m_sizingInfo.geometry.setWidth(0);
2199 growItem(item, newLength, GrowthStrategy::BothSidesEqually, neighbourSqueezeStrategy,
true);
2200 d->updateSeparators_recursive();
2203 void ItemBoxContainer::updateWidgetGeometries()
2205 for (Item *item : qAsConst(m_children))
2206 item->updateWidgetGeometries();
2209 int ItemBoxContainer::oppositeLength()
const
2211 return isVertical() ? width()
2215 void ItemBoxContainer::requestSeparatorMove(
Separator *separator,
int delta)
2217 const auto separatorIndex = d->m_separators.indexOf(separator);
2218 if (separatorIndex == -1) {
2220 qWarning() << Q_FUNC_INFO <<
"Unknown separator" << separator <<
this;
2221 root()->dumpLayout();
2228 const int min = minPosForSeparator_global(separator);
2229 const int pos = separator->position();
2230 const int max = maxPosForSeparator_global(separator);
2232 if ((pos + delta < min && delta < 0) ||
2233 (pos + delta > max && delta > 0)) {
2234 root()->dumpLayout();
2235 qWarning() <<
"Separator would have gone out of bounds"
2236 <<
"; separators=" << separator
2237 <<
"; min=" << min <<
"; pos=" << pos
2238 <<
"; max=" << max <<
"; delta=" << delta;
2242 const Side moveDirection = delta < 0 ? Side1 : Side2;
2243 const Item::List children = visibleChildren();
2244 if (children.size() <= separatorIndex) {
2246 qWarning() << Q_FUNC_INFO <<
"Not enough children for separator index" << separator
2247 <<
this << separatorIndex;
2248 root()->dumpLayout();
2252 int remainingToTake = qAbs(delta);
2253 int tookLocally = 0;
2255 Item *side1Neighbour = children[separatorIndex];
2256 Item *side2Neighbour = children[separatorIndex + 1];
2258 Side nextSeparatorDirection = moveDirection;
2260 if (moveDirection == Side1) {
2262 const int availableSqueeze1 = availableToSqueezeOnSide(side2Neighbour, Side1);
2263 const int availableGrow2 = availableToGrowOnSide(side1Neighbour, Side2);
2266 tookLocally = qMin(availableSqueeze1, remainingToTake);
2267 tookLocally = qMin(tookLocally, availableGrow2);
2269 if (tookLocally != 0) {
2270 growItem(side2Neighbour, tookLocally, GrowthStrategy::Side1Only,
2271 NeighbourSqueezeStrategy::ImmediateNeighboursFirst,
false,
2272 ChildrenResizeStrategy::Side1SeparatorMove);
2275 if (availableGrow2 == tookLocally)
2276 nextSeparatorDirection = Side2;
2280 const int availableSqueeze2 = availableToSqueezeOnSide(side1Neighbour, Side2);
2281 const int availableGrow1 = availableToGrowOnSide(side2Neighbour, Side1);
2284 tookLocally = qMin(availableSqueeze2, remainingToTake);
2285 tookLocally = qMin(tookLocally, availableGrow1);
2287 if (tookLocally != 0) {
2288 growItem(side1Neighbour, tookLocally, GrowthStrategy::Side2Only,
2289 NeighbourSqueezeStrategy::ImmediateNeighboursFirst,
false,
2290 ChildrenResizeStrategy::Side2SeparatorMove);
2293 if (availableGrow1 == tookLocally)
2294 nextSeparatorDirection = Side1;
2297 remainingToTake -= tookLocally;
2299 if (remainingToTake > 0) {
2301 if (Q_UNLIKELY(isRoot())) {
2303 qWarning() << Q_FUNC_INFO <<
"Not enough space to move separator"
2306 Separator *nextSeparator = parentBoxContainer()->d->neighbourSeparator_recursive(
this, nextSeparatorDirection, d->m_orientation);
2307 if (!nextSeparator) {
2309 qWarning() << Q_FUNC_INFO <<
"nextSeparator is null, report a bug";
2314 const int remainingDelta = moveDirection == Side1 ? -remainingToTake : remainingToTake;
2315 nextSeparator->parentContainer()->requestSeparatorMove(nextSeparator, remainingDelta);
2320 void ItemBoxContainer::requestEqualSize(
Separator *separator)
2322 const auto separatorIndex = d->m_separators.indexOf(separator);
2323 if (separatorIndex == -1) {
2325 qWarning() << Q_FUNC_INFO <<
"Separator not found" << separator;
2329 const Item::List children = visibleChildren();
2330 Item *side1Item = children.at(separatorIndex);
2331 Item *side2Item = children.at(separatorIndex + 1);
2333 const int length1 = side1Item->length(d->m_orientation);
2334 const int length2 = side2Item->length(d->m_orientation);
2336 if (qAbs(length1 - length2) <= 1) {
2341 if (!(side1Item->m_sizingInfo.isPastMax(d->m_orientation) || side2Item->m_sizingInfo.isPastMax(d->m_orientation))) {
2346 const int newLength = (length1 + length2) / 2;
2349 if (length1 < newLength) {
2351 delta = newLength - length1;
2352 }
else if (length2 < newLength) {
2354 delta = -(newLength - length2);
2358 const int min = minPosForSeparator_global(separator,
true);
2359 const int max = maxPosForSeparator_global(separator,
true);
2360 const int newPos = qBound(min, separator->position() + delta, max);
2363 delta = newPos - separator->position();
2366 requestSeparatorMove(separator, delta);
2369 void ItemBoxContainer::layoutEqually()
2371 SizingInfo::List childSizes = sizes();
2372 if (!childSizes.isEmpty()) {
2373 layoutEqually(childSizes);
2374 applyGeometries(childSizes);
2378 void ItemBoxContainer::layoutEqually(SizingInfo::List &sizes)
2380 const auto numItems = sizes.count();
2382 satisfiedIndexes.
reserve(numItems);
2384 auto lengthToGive = length() - (d->m_separators.size() * Item::separatorThickness);
2387 for (SizingInfo &size : sizes) {
2388 size.setLength(0, d->m_orientation);
2391 while (satisfiedIndexes.
count() < sizes.count()) {
2392 const auto remainingItems = sizes.count() - satisfiedIndexes.
count();
2393 auto suggestedToGive = qMax(1, lengthToGive / remainingItems);
2394 const auto oldLengthToGive = lengthToGive;
2396 for (
int i = 0; i < numItems; ++i) {
2400 SizingInfo &size = sizes[i];
2401 if (size.availableToGrow(d->m_orientation) <= 0) {
2411 const auto othersMissing =
2413 std::accumulate(sizes.constBegin(), sizes.constEnd(), 0,
2414 [
this](
size_t sum,
const SizingInfo &sz) {
2415 return int(sum) + sz.missingLength(d->m_orientation);
2417 - size.missingLength(d->m_orientation);
2419 const auto maxLength =
2420 qMin(size.length(d->m_orientation) + lengthToGive - othersMissing,
2421 size.maxLengthHint(d->m_orientation));
2423 const auto newItemLenght =
2424 qBound(size.minLength(d->m_orientation),
2425 size.length(d->m_orientation) + suggestedToGive, maxLength);
2426 const auto toGive = newItemLenght - size.length(d->m_orientation);
2432 lengthToGive -= toGive;
2433 size.incrementLength(toGive, d->m_orientation);
2434 if (size.availableToGrow(d->m_orientation) <= 0) {
2437 if (lengthToGive == 0)
2440 if (lengthToGive < 0) {
2441 qWarning() << Q_FUNC_INFO <<
"Breaking infinite loop";
2447 if (oldLengthToGive == lengthToGive) {
2454 void ItemBoxContainer::layoutEqually_recursive()
2457 for (Item *item : qAsConst(m_children)) {
2458 if (item->isVisible()) {
2459 if (
auto c = item->asBoxContainer())
2460 c->layoutEqually_recursive();
2465 Item *ItemBoxContainer::visibleNeighbourFor(
const Item *item, Side side)
const
2468 const auto index = m_children.indexOf(
const_cast<Item *
>(item));
2470 if (side == Side1) {
2471 for (
auto i = index - 1; i >= 0; --i) {
2472 Item *child = m_children.at(i);
2473 if (child->isVisible())
2477 for (
auto i = index + 1; i < m_children.size(); ++i) {
2478 Item *child = m_children.at(i);
2479 if (child->isVisible())
2487 QSize ItemBoxContainer::availableSize()
const
2489 return size() - this->minSize();
2492 int ItemBoxContainer::availableLength()
const
2494 return isVertical() ? availableSize().
height()
2495 : availableSize().
width();
2498 LengthOnSide ItemBoxContainer::lengthOnSide(
const SizingInfo::List &sizes,
int fromIndex,
2504 const auto count = sizes.count();
2505 if (fromIndex >= count)
2510 if (side == Side1) {
2518 LengthOnSide result;
2519 for (
int i = start; i <= end; ++i) {
2520 const SizingInfo &size = sizes.at(i);
2521 result.length += size.length(o);
2522 result.minLength += size.minLength(o);
2528 int ItemBoxContainer::neighboursLengthFor(
const Item *item, Side side,
Qt::Orientation o)
const
2530 const Item::List children = visibleChildren();
2531 const auto index = children.indexOf(
const_cast<Item *
>(item));
2533 qWarning() << Q_FUNC_INFO <<
"Couldn't find item" << item;
2537 if (o == d->m_orientation) {
2538 int neighbourLength = 0;
2541 if (side == Side1) {
2546 end = children.size() - 1;
2549 for (
int i = start; i <= end; ++i)
2550 neighbourLength += children.at(i)->length(d->m_orientation);
2552 return neighbourLength;
2559 int ItemBoxContainer::neighboursLengthFor_recursive(
const Item *item, Side side,
Qt::Orientation o)
const
2561 return neighboursLengthFor(item, side, o) + (isRoot() ? 0 : parentBoxContainer()->neighboursLengthFor_recursive(
this, side, o));
2564 int ItemBoxContainer::neighboursMinLengthFor(
const Item *item, Side side,
Qt::Orientation o)
const
2566 const Item::List children = visibleChildren();
2567 const auto index = children.indexOf(
const_cast<Item *
>(item));
2569 qWarning() << Q_FUNC_INFO <<
"Couldn't find item" << item;
2573 if (o == d->m_orientation) {
2574 int neighbourMinLength = 0;
2577 if (side == Side1) {
2582 end = children.size() - 1;
2585 for (
int i = start; i <= end; ++i)
2586 neighbourMinLength += children.at(i)->minLength(d->m_orientation);
2588 return neighbourMinLength;
2595 int ItemBoxContainer::neighboursMaxLengthFor(
const Item *item, Side side,
Qt::Orientation o)
const
2597 const Item::List children = visibleChildren();
2598 const auto index = children.indexOf(
const_cast<Item *
>(item));
2600 qWarning() << Q_FUNC_INFO <<
"Couldn't find item" << item;
2604 if (o == d->m_orientation) {
2605 int neighbourMaxLength = 0;
2608 if (side == Side1) {
2613 end = children.size() - 1;
2616 for (
int i = start; i <= end; ++i)
2617 neighbourMaxLength = qMin(Layouting::length(root()->size(), d->m_orientation), neighbourMaxLength + children.at(i)->maxLengthHint(d->m_orientation));
2619 return neighbourMaxLength;
2626 int ItemBoxContainer::availableToSqueezeOnSide(
const Item *child, Side side)
const
2628 const int length = neighboursLengthFor(child, side, d->m_orientation);
2629 const int min = neighboursMinLengthFor(child, side, d->m_orientation);
2631 const int available = length - min;
2632 if (available < 0) {
2633 root()->dumpLayout();
2639 int ItemBoxContainer::availableToGrowOnSide(
const Item *child, Side side)
const
2641 const int length = neighboursLengthFor(child, side, d->m_orientation);
2642 const int max = neighboursMaxLengthFor(child, side, d->m_orientation);
2644 return max - length;
2647 int ItemBoxContainer::availableToSqueezeOnSide_recursive(
const Item *child, Side side,
Qt::Orientation orientation)
const
2649 if (orientation == d->m_orientation) {
2650 const int available = availableToSqueezeOnSide(child, side);
2651 return isRoot() ? available
2652 : (available + parentBoxContainer()->availableToSqueezeOnSide_recursive(
this, side, orientation));
2655 : parentBoxContainer()->availableToSqueezeOnSide_recursive(
this, side, orientation);
2659 int ItemBoxContainer::availableToGrowOnSide_recursive(
const Item *child, Side side,
Qt::Orientation orientation)
const
2661 if (orientation == d->m_orientation) {
2662 const int available = availableToGrowOnSide(child, side);
2663 return isRoot() ? available
2664 : (available + parentBoxContainer()->availableToGrowOnSide_recursive(
this, side, orientation));
2667 : parentBoxContainer()->availableToGrowOnSide_recursive(
this, side, orientation);
2671 void ItemBoxContainer::growNeighbours(Item *side1Neighbour, Item *side2Neighbour)
2673 if (!side1Neighbour && !side2Neighbour)
2676 SizingInfo::List childSizes = sizes();
2678 if (side1Neighbour && side2Neighbour) {
2679 const int index1 = indexOfVisibleChild(side1Neighbour);
2680 const int index2 = indexOfVisibleChild(side2Neighbour);
2682 if (index1 == -1 || index2 == -1 || index1 >= childSizes.count() || index2 >= childSizes.count()) {
2684 qWarning() << Q_FUNC_INFO <<
"Invalid indexes" << index1 << index2 << childSizes.count();
2689 QRect &geo1 = childSizes[index1].geometry;
2690 QRect &geo2 = childSizes[index2].geometry;
2693 const int available = geo2.
y() - geo1.
bottom() - separatorThickness;
2697 const int available = geo2.
x() - geo1.
right() - separatorThickness;
2702 }
else if (side1Neighbour) {
2703 const int index1 = indexOfVisibleChild(side1Neighbour);
2704 if (index1 == -1 || index1 >= childSizes.count()) {
2706 qWarning() << Q_FUNC_INFO <<
"Invalid indexes" << index1 << childSizes.count();
2711 QRect &geo = childSizes[index1].geometry;
2717 }
else if (side2Neighbour) {
2718 const int index2 = indexOfVisibleChild(side2Neighbour);
2719 if (index2 == -1 || index2 >= childSizes.count()) {
2721 qWarning() << Q_FUNC_INFO <<
"Invalid indexes" << index2 << childSizes.count();
2726 QRect &geo = childSizes[index2].geometry;
2734 d->honourMaxSizes(childSizes);
2735 positionItems( childSizes);
2736 applyGeometries(childSizes);
2739 void ItemBoxContainer::growItem(
int index, SizingInfo::List &sizes,
int missing,
2740 GrowthStrategy growthStrategy,
2741 NeighbourSqueezeStrategy neighbourSqueezeStrategy,
2742 bool accountForNewSeparator)
2744 int toSteal = missing;
2745 if (accountForNewSeparator)
2746 toSteal += Item::separatorThickness;
2748 Q_ASSERT(index != -1);
2753 SizingInfo &sizingInfo = sizes[index];
2754 sizingInfo.setOppositeLength(oppositeLength(), d->m_orientation);
2755 const bool isFirst = index == 0;
2756 const bool isLast = index == sizes.count() - 1;
2758 int side1Growth = 0;
2759 int side2Growth = 0;
2761 if (growthStrategy == GrowthStrategy::BothSidesEqually) {
2762 sizingInfo.setLength(sizingInfo.length(d->m_orientation) + missing, d->m_orientation);
2763 const auto count = sizes.count();
2766 sizingInfo.incrementLength(missing, d->m_orientation);
2771 const LengthOnSide side1Length = lengthOnSide(sizes, index - 1, Side1, d->m_orientation);
2772 const LengthOnSide side2Length = lengthOnSide(sizes, index + 1, Side2, d->m_orientation);
2774 int available1 = side1Length.available();
2775 int available2 = side2Length.available();
2777 if (toSteal > available1 + available2) {
2778 root()->dumpLayout();
2782 while (toSteal > 0) {
2783 if (available1 == 0) {
2784 Q_ASSERT(available2 >= toSteal);
2785 side2Growth += toSteal;
2787 }
else if (available2 == 0) {
2788 Q_ASSERT(available1 >= toSteal);
2789 side1Growth += toSteal;
2793 const int toTake = qMax(1, toSteal / 2);
2794 const int took1 = qMin(toTake, available1);
2796 available1 -= took1;
2797 side1Growth += took1;
2801 const int took2 = qMin(toTake, available2);
2803 side2Growth += took2;
2804 available2 -= took2;
2806 shrinkNeighbours(index, sizes, side1Growth, side2Growth, neighbourSqueezeStrategy);
2807 }
else if (growthStrategy == GrowthStrategy::Side1Only) {
2808 side1Growth = qMin(missing, sizingInfo.availableToGrow(d->m_orientation));
2809 sizingInfo.setLength(sizingInfo.length(d->m_orientation) + side1Growth, d->m_orientation);
2810 if (side1Growth > 0)
2811 shrinkNeighbours(index, sizes, side1Growth, 0, neighbourSqueezeStrategy);
2812 if (side1Growth < missing) {
2813 missing = missing - side1Growth;
2817 qWarning() << Q_FUNC_INFO <<
"No more items to grow";
2819 growItem(index + 1, sizes, missing, growthStrategy, neighbourSqueezeStrategy, accountForNewSeparator);
2823 }
else if (growthStrategy == GrowthStrategy::Side2Only) {
2824 side2Growth = qMin(missing, sizingInfo.availableToGrow(d->m_orientation));
2825 sizingInfo.setLength(sizingInfo.length(d->m_orientation) + side2Growth, d->m_orientation);
2827 if (side2Growth > 0)
2828 shrinkNeighbours(index, sizes, 0, side2Growth, neighbourSqueezeStrategy);
2829 if (side2Growth < missing) {
2830 missing = missing - side2Growth;
2834 qWarning() << Q_FUNC_INFO <<
"No more items to grow";
2836 growItem(index - 1, sizes, missing, growthStrategy, neighbourSqueezeStrategy, accountForNewSeparator);
2842 void ItemBoxContainer::growItem(Item *item,
int amount, GrowthStrategy growthStrategy,
2843 NeighbourSqueezeStrategy neighbourSqueezeStrategy,
2844 bool accountForNewSeparator,
2845 ChildrenResizeStrategy childResizeStrategy)
2847 const Item::List items = visibleChildren();
2848 const auto index = items.indexOf(item);
2849 SizingInfo::List sizes = this->sizes();
2851 growItem(index, sizes, amount, growthStrategy, neighbourSqueezeStrategy, accountForNewSeparator);
2853 applyGeometries(sizes, childResizeStrategy);
2856 void ItemBoxContainer::applyGeometries(
const SizingInfo::List &sizes, ChildrenResizeStrategy strategy)
2858 const Item::List items = visibleChildren();
2859 const auto count = items.size();
2860 Q_ASSERT(count == sizes.size());
2862 for (
int i = 0; i < count; ++i) {
2863 Item *item = items.at(i);
2864 item->setSize_recursive(sizes[i].geometry.size(), strategy);
2870 SizingInfo::List ItemBoxContainer::sizes(
bool ignoreBeingInserted)
const
2872 const Item::List children = visibleChildren(ignoreBeingInserted);
2873 SizingInfo::List result;
2874 result.reserve(children.count());
2875 for (Item *item : children) {
2876 if (item->isContainer()) {
2879 item->m_sizingInfo.minSize = item->minSize();
2880 item->m_sizingInfo.maxSizeHint = item->maxSizeHint();
2882 result << item->m_sizingInfo;
2888 QVector<int> ItemBoxContainer::calculateSqueezes(SizingInfo::List::ConstIterator begin,
2889 SizingInfo::List::ConstIterator end,
int needed,
2890 NeighbourSqueezeStrategy strategy,
bool reversed)
const
2893 for (
auto it = begin; it < end; ++it) {
2894 availabilities << it->availableLength(d->m_orientation);
2897 const auto count = availabilities.
count();
2900 int missing = needed;
2902 if (strategy == NeighbourSqueezeStrategy::AllNeighbours) {
2903 while (missing > 0) {
2904 const int numDonors = std::count_if(availabilities.
cbegin(), availabilities.
cend(), [](
int num) {
2908 if (numDonors == 0) {
2909 root()->dumpLayout();
2914 int toTake = missing / numDonors;
2918 for (
int i = 0; i < count; ++i) {
2919 const int available = availabilities.
at(i);
2922 const int took = qMin(missing, qMin(toTake, available));
2923 availabilities[i] -= took;
2925 squeezes[i] += took;
2930 }
else if (strategy == NeighbourSqueezeStrategy::ImmediateNeighboursFirst) {
2931 for (
int i = 0; i < count; i++) {
2932 const auto index = reversed ? count - 1 - i : i;
2934 const int available = availabilities.
at(index);
2935 if (available > 0) {
2936 const int took = qMin(missing, available);
2938 squeezes[index] += took;
2948 qWarning() << Q_FUNC_INFO <<
"Missing is negative" << missing
2955 void ItemBoxContainer::shrinkNeighbours(
int index, SizingInfo::List &sizes,
int side1Amount,
2956 int side2Amount, NeighbourSqueezeStrategy strategy)
2958 Q_ASSERT(side1Amount > 0 || side2Amount > 0);
2959 Q_ASSERT(side1Amount >= 0 && side2Amount >= 0);
2961 if (side1Amount > 0) {
2962 auto begin = sizes.cbegin();
2963 auto end = sizes.cbegin() + index;
2964 const bool reversed = strategy == NeighbourSqueezeStrategy::ImmediateNeighboursFirst;
2965 const QVector<int> squeezes = calculateSqueezes(begin, end, side1Amount, strategy, reversed);
2966 for (
int i = 0; i < squeezes.
size(); ++i) {
2967 const int squeeze = squeezes.
at(i);
2968 SizingInfo &sizing = sizes[i];
2970 sizing.setSize(
adjustedRect(sizing.geometry, d->m_orientation, 0, -squeeze).
size());
2974 if (side2Amount > 0) {
2975 auto begin = sizes.cbegin() + index + 1;
2976 auto end = sizes.cend();
2978 const QVector<int> squeezes = calculateSqueezes(begin, end, side2Amount, strategy);
2979 for (
int i = 0; i < squeezes.
size(); ++i) {
2980 const int squeeze = squeezes.
at(i);
2981 SizingInfo &sizing = sizes[i + index + 1];
2982 sizing.setSize(
adjustedRect(sizing.geometry, d->m_orientation, squeeze, 0).
size());
2987 QVector<int> ItemBoxContainer::Private::requiredSeparatorPositions()
const
2989 const int numSeparators = qMax(0, q->numVisibleChildren() - 1);
2991 positions.
reserve(numSeparators);
2993 for (Item *item : qAsConst(q->m_children)) {
2994 if (positions.
size() == numSeparators)
2997 if (item->isVisible()) {
2998 const int localPos = item->m_sizingInfo.edge(m_orientation) + 1;
2999 positions << q->mapToRoot(localPos, m_orientation);
3006 void ItemBoxContainer::Private::updateSeparators()
3008 if (!q->hostWidget())
3011 const QVector<int> positions = requiredSeparatorPositions();
3012 const auto requiredNumSeparators = positions.
size();
3014 const bool numSeparatorsChanged = requiredNumSeparators != m_separators.size();
3015 if (numSeparatorsChanged) {
3018 Separator::List newSeparators;
3019 newSeparators.reserve(requiredNumSeparators);
3021 for (
int position : positions) {
3022 Separator *separator = separatorAt(position);
3025 newSeparators.push_back(separator);
3026 m_separators.removeOne(separator);
3028 separator = Config::self().createSeparator(q->hostWidget());
3029 separator->init(q, m_orientation);
3030 newSeparators.push_back(separator);
3037 m_separators = newSeparators;
3041 const int pos2 = q->isVertical() ? q->mapToRoot(
QPoint(0, 0)).x()
3042 : q->mapToRoot(
QPoint(0, 0)).y();
3045 for (
int position : positions) {
3046 m_separators.at(i)->setGeometry(position, pos2, q->oppositeLength());
3050 q->updateChildPercentages();
3053 void ItemBoxContainer::Private::deleteSeparators()
3055 qDeleteAll(m_separators);
3056 m_separators.clear();
3059 void ItemBoxContainer::Private::deleteSeparators_recursive()
3064 for (Item *item : qAsConst(q->m_children)) {
3065 if (
auto c = item->asBoxContainer())
3066 c->d->deleteSeparators_recursive();
3070 void ItemBoxContainer::Private::updateSeparators_recursive()
3075 const Item::List items = q->visibleChildren();
3076 for (Item *item : items) {
3077 if (
auto c = item->asBoxContainer())
3078 c->d->updateSeparators_recursive();
3082 int ItemBoxContainer::Private::excessLength()
const
3085 return qMax(0, Layouting::length(q->size(), m_orientation) - q->maxLengthHint(m_orientation));
3088 void ItemBoxContainer::simplify()
3095 Item::List newChildren;
3096 newChildren.reserve(m_children.size() + 20);
3098 for (Item *child : qAsConst(m_children)) {
3100 childContainer->simplify();
3102 if (childContainer->orientation() == d->m_orientation || childContainer->m_children.size() == 1) {
3105 for (Item *child2 : childContainer->childItems()) {
3106 child2->setParentContainer(
this);
3107 newChildren.push_back(child2);
3110 delete childContainer;
3112 newChildren.push_back(child);
3115 newChildren.push_back(child);
3119 if (m_children != newChildren) {
3120 m_children = newChildren;
3122 updateChildPercentages();
3126 Separator *ItemBoxContainer::Private::separatorAt(
int p)
const
3128 for (
Separator *separator : m_separators) {
3129 if (separator->position() == p)
3136 bool ItemBoxContainer::isVertical()
const
3141 bool ItemBoxContainer::isHorizontal()
const
3146 int ItemBoxContainer::indexOf(
Separator *separator)
const
3148 return d->m_separators.indexOf(separator);
3151 bool ItemBoxContainer::isInSimplify()
const
3153 if (d->m_isSimplifying)
3156 auto p = parentBoxContainer();
3157 return p && p->isInSimplify();
3160 int ItemBoxContainer::minPosForSeparator(
Separator *separator,
bool honourMax)
const
3162 const int globalMin = minPosForSeparator_global(separator, honourMax);
3163 return mapFromRoot(globalMin, d->m_orientation);
3166 int ItemBoxContainer::maxPosForSeparator(
Separator *separator,
bool honourMax)
const
3168 const int globalMax = maxPosForSeparator_global(separator, honourMax);
3169 return mapFromRoot(globalMax, d->m_orientation);
3172 int ItemBoxContainer::minPosForSeparator_global(
Separator *separator,
bool honourMax)
const
3174 const int separatorIndex = indexOf(separator);
3175 Q_ASSERT(separatorIndex != -1);
3177 const Item::List children = visibleChildren();
3178 Q_ASSERT(separatorIndex + 1 < children.size());
3179 Item *item2 = children.at(separatorIndex + 1);
3181 const int availableToSqueeze = availableToSqueezeOnSide_recursive(item2, Side1, d->m_orientation);
3185 Item *item1 = children.at(separatorIndex);
3186 const int availabletoGrow = availableToGrowOnSide_recursive(item1, Side2, d->m_orientation);
3187 return separator->position() - qMin(availabletoGrow, availableToSqueeze);
3190 return separator->position() - availableToSqueeze;
3193 int ItemBoxContainer::maxPosForSeparator_global(
Separator *separator,
bool honourMax)
const
3195 const int separatorIndex = indexOf(separator);
3196 Q_ASSERT(separatorIndex != -1);
3198 const Item::List children = visibleChildren();
3199 Item *item1 = children.at(separatorIndex);
3201 const int availableToSqueeze = availableToSqueezeOnSide_recursive(item1, Side2, d->m_orientation);
3205 Item *item2 = children.at(separatorIndex + 1);
3206 const int availabletoGrow = availableToGrowOnSide_recursive(item2, Side1, d->m_orientation);
3207 return separator->position() + qMin(availabletoGrow, availableToSqueeze);
3210 return separator->position() + availableToSqueeze;
3213 QVariantMap ItemBoxContainer::toVariantMap()
const
3215 QVariantMap result = Item::toVariantMap();
3217 QVariantList childrenV;
3218 childrenV.reserve(m_children.size());
3219 for (Item *child : qAsConst(m_children)) {
3220 childrenV.push_back(child->toVariantMap());
3223 result[QStringLiteral(
"children")] = childrenV;
3224 result[QStringLiteral(
"orientation")] = d->m_orientation;
3229 void ItemBoxContainer::fillFromVariantMap(
const QVariantMap &map,
3234 Item::fillFromVariantMap(map, widgets);
3235 const QVariantList childrenV = map[QStringLiteral(
"children")].toList();
3236 d->m_orientation =
Qt::Orientation(map[QStringLiteral(
"orientation")].toInt());
3238 for (
const QVariant &childV : childrenV) {
3239 const QVariantMap childMap = childV.toMap();
3240 const bool isContainer = childMap.value(QStringLiteral(
"isContainer")).toBool();
3242 : new Item(hostWidget(), this);
3243 child->fillFromVariantMap(childMap, widgets);
3244 m_children.push_back(child);
3248 updateChildPercentages_recursive();
3250 d->updateSeparators_recursive();
3251 d->updateWidgets_recursive();
3254 d->relayoutIfNeeded();
3255 positionItems_recursive();
3257 Q_EMIT minSizeChanged(
this);
3258 #ifdef DOCKS_DEVELOPER_MODE
3260 qWarning() << Q_FUNC_INFO <<
"Resulting layout is invalid";
3265 bool ItemBoxContainer::Private::isDummy()
const
3267 return q->hostWidget() ==
nullptr;
3270 #ifdef DOCKS_DEVELOPER_MODE
3271 bool ItemBoxContainer::test_suggestedRect()
3273 auto itemToDrop =
new Item(hostWidget());
3275 const Item::List children = visibleChildren();
3276 for (Item *relativeTo : children) {
3277 if (
auto c = relativeTo->asBoxContainer()) {
3278 c->test_suggestedRect();
3282 const QRect rect = suggestedDropRect(itemToDrop, relativeTo, loc);
3285 qWarning() << Q_FUNC_INFO <<
"Empty rect";
3287 }
else if (!root()->rect().contains(rect)) {
3288 root()->dumpLayout();
3289 qWarning() << Q_FUNC_INFO <<
"Suggested rect is out of bounds" << rect
3290 <<
"; loc=" << loc <<
"; relativeTo=" << relativeTo;
3295 root()->dumpLayout();
3296 qWarning() << Q_FUNC_INFO <<
"Invalid suggested rects" << rects
3297 <<
this <<
"; relativeTo=" << relativeTo;
3310 Layouting::Separator::List separators = d->m_separators;
3312 for (Item *item : qAsConst(m_children)) {
3313 if (
auto c = item->asBoxContainer())
3314 separators << c->separators_recursive();
3322 return d->m_separators;
3325 bool ItemBoxContainer::Private::isOverflowing()
const
3330 int contentsLength = 0;
3332 for (Item *item : qAsConst(q->m_children)) {
3333 if (item->isVisible()) {
3334 contentsLength += item->
length(m_orientation);
3339 contentsLength += qMax(0, Item::separatorThickness * (numVisible - 1));
3340 return contentsLength > q->length();
3343 void ItemBoxContainer::Private::relayoutIfNeeded()
3349 if (!q->missingSize().isNull())
3350 q->setSize_recursive(q->minSize());
3352 if (isOverflowing()) {
3353 const QSize size = q->size();
3354 q->m_sizingInfo.setSize(size +
QSize(1, 1));
3355 q->setSize_recursive(size);
3356 q->updateChildPercentages();
3360 for (Item *item : qAsConst(q->m_children)) {
3361 if (item->isVisible()) {
3362 if (
auto c = item->asBoxContainer())
3363 c->d->relayoutIfNeeded();
3368 const Item *ItemBoxContainer::Private::itemFromPath(
const QVector<int> &path)
const
3372 for (
int i = 0; i < path.
size(); ++i) {
3373 const int index = path[i];
3374 const bool isLast = i == path.
size() - 1;
3375 if (index < 0 || index >= container->m_children.size()) {
3377 q->root()->dumpLayout();
3378 qWarning() << Q_FUNC_INFO <<
"Invalid index" << index
3379 <<
this << path << q->isRoot();
3384 return container->m_children.
at(index);
3386 container = container->m_children.at(index)->asBoxContainer();
3388 qWarning() << Q_FUNC_INFO <<
"Invalid index" << path;
3397 Separator *ItemBoxContainer::Private::neighbourSeparator(
const Item *item, Side side,
Qt::Orientation orientation)
const
3399 Item::List children = q->visibleChildren();
3400 const auto itemIndex = children.indexOf(
const_cast<Item *
>(item));
3401 if (itemIndex == -1) {
3402 qWarning() << Q_FUNC_INFO <<
"Item not found" << item
3404 q->root()->dumpLayout();
3408 if (orientation != q->orientation()) {
3413 return q->parentBoxContainer()->d->neighbourSeparator(q, side, orientation);
3417 const auto separatorIndex = side == Side1 ? itemIndex - 1
3420 if (separatorIndex < 0 || separatorIndex >= m_separators.size())
3423 return m_separators[separatorIndex];
3426 Separator *ItemBoxContainer::Private::neighbourSeparator_recursive(
const Item *item, Side side,
3429 Separator *separator = neighbourSeparator(item, side, orientation);
3433 if (!q->parentContainer())
3436 return q->parentBoxContainer()->d->neighbourSeparator_recursive(q, side, orientation);
3439 void ItemBoxContainer::Private::updateWidgets_recursive()
3441 for (Item *item : qAsConst(q->m_children)) {
3442 if (
auto c = item->asBoxContainer()) {
3443 c->d->updateWidgets_recursive();
3445 if (item->isVisible()) {
3446 if (
Widget *widget = item->guestWidget()) {
3447 widget->setGeometry(q->mapToRoot(item->geometry()));
3448 widget->setVisible(
true);
3450 qWarning() << Q_FUNC_INFO <<
"visible item doesn't have a guest"
3458 SizingInfo::SizingInfo()
3459 : minSize(
Layouting::Item::hardcodedMinimumSize)
3460 , maxSizeHint(
Layouting::Item::hardcodedMaximumSize)
3469 QVariantMap SizingInfo::toVariantMap()
const
3472 result[QStringLiteral(
"geometry")] = rectToMap(geometry);
3473 result[QStringLiteral(
"minSize")] = sizeToMap(minSize);
3474 result[QStringLiteral(
"maxSize")] = sizeToMap(maxSizeHint);
3478 void SizingInfo::fromVariantMap(
const QVariantMap &map)
3480 *
this = SizingInfo();
3481 geometry = mapToRect(map[QStringLiteral(
"geometry")].toMap());
3482 minSize = mapToSize(map[QStringLiteral(
"minSize")].toMap());
3483 maxSizeHint = mapToSize(map[QStringLiteral(
"maxSize")].toMap());
3486 int ItemBoxContainer::Private::defaultLengthFor(Item *item,
InitialOption option)
const
3490 if (option.
hasPreferredLength(m_orientation) && option.sizeMode != DefaultSizeMode::NoDefaultSizeMode) {
3493 switch (option.sizeMode) {
3494 case DefaultSizeMode::NoDefaultSizeMode:
3496 case DefaultSizeMode::Fair: {
3497 const int numVisibleChildren = q->numVisibleChildren() + 1;
3498 const int usableLength = q->length() - (Item::separatorThickness * (numVisibleChildren - 1));
3499 result = usableLength / numVisibleChildren;
3502 case DefaultSizeMode::FairButFloor: {
3503 int length = defaultLengthFor(item, DefaultSizeMode::Fair);
3504 result = qMin(length, item->length(m_orientation));
3507 case DefaultSizeMode::ItemSize:
3508 result = item->length(m_orientation);
3513 result = qMax(item->minLength(m_orientation), result);
3517 struct ItemContainer::Private
3522 ( void )Config::self();
3532 : Item(true, hostWidget, parent)
3533 , d(new Private(this))
3535 connect(
this, &Item::xChanged,
this, [
this] {
3536 for (Item *item : qAsConst(m_children)) {
3537 Q_EMIT item->xChanged();
3541 connect(
this, &Item::yChanged,
this, [
this] {
3542 for (Item *item : qAsConst(m_children)) {
3543 Q_EMIT item->yChanged();
3548 ItemContainer::ItemContainer(
Widget *hostWidget)
3549 : Item(true, hostWidget, nullptr)
3550 , d(new Private(this))
3554 ItemContainer::~ItemContainer()
3559 const Item::List ItemContainer::childItems()
const
3564 bool ItemContainer::hasChildren()
const
3566 return !m_children.isEmpty();
3569 bool ItemContainer::hasVisibleChildren(
bool excludeBeingInserted)
const
3571 for (Item *item : qAsConst(m_children)) {
3572 if (item->isVisible(excludeBeingInserted))
3579 int ItemContainer::numChildren()
const
3581 return m_children.size();
3584 int ItemContainer::numVisibleChildren()
const
3587 for (Item *child : qAsConst(m_children)) {
3588 if (child->isVisible())
3594 bool ItemContainer::isEmpty()
const
3596 return m_children.isEmpty();
3599 bool ItemContainer::hasSingleVisibleItem()
const
3601 return numVisibleChildren() == 1;
3604 bool ItemContainer::contains(
const Item *item)
const
3606 return m_children.contains(
const_cast<Item *
>(item));
3609 Item *ItemContainer::itemForObject(
const QObject *o)
const
3611 for (Item *item : qAsConst(m_children)) {
3612 if (item->isContainer()) {
3613 if (Item *result = item->asContainer()->itemForObject(o))
3615 }
else if (
auto guest = item->guestWidget()) {
3624 Item *ItemContainer::itemForWidget(
const Widget *w)
const
3626 for (Item *item : qAsConst(m_children)) {
3627 if (item->isContainer()) {
3628 if (Item *result = item->asContainer()->itemForWidget(w))
3630 }
else if (item->guestWidget() == w) {
3638 Item::List ItemContainer::visibleChildren(
bool includeBeingInserted)
const
3641 items.reserve(m_children.size());
3642 for (Item *item : qAsConst(m_children)) {
3643 if (includeBeingInserted) {
3644 if (item->isVisible() || item->isBeingInserted())
3647 if (item->isVisible() && !item->isBeingInserted())
3655 Item::List ItemContainer::items_recursive()
const
3659 for (Item *item : qAsConst(m_children)) {
3660 if (
auto c = item->asContainer()) {
3661 items << c->items_recursive();
3670 bool ItemContainer::contains_recursive(
const Item *item)
const
3672 for (Item *it : qAsConst(m_children)) {
3675 }
else if (it->isContainer()) {
3676 if (it->asContainer()->contains_recursive(item))
3685 int ItemContainer::visibleCount_recursive()
const
3688 for (Item *item : qAsConst(m_children)) {
3689 count += item->visibleCount_recursive();
3695 int ItemContainer::count_recursive()
const
3698 for (Item *item : qAsConst(m_children)) {
3699 if (
auto c = item->asContainer()) {
3700 count += c->count_recursive();
3710 #pragma warning(pop)