13#include "ItemFreeContainer_p.h"
14#include "LayoutingHost_p.h"
15#include "LayoutingGuest_p.h"
16#include "LayoutingSeparator_p.h"
18#include "core/Logging_p.h"
19#include "core/ObjectGuard_p.h"
20#include "core/ScopedValueRollback_p.h"
21#include "core/nlohmann_helpers_p.h"
28#ifdef KDDW_FRONTEND_QT
29#include "core/Platform_p.h"
33#define LAYOUT_DUMP_INDENT 6
37#pragma warning(disable : 4138)
38#pragma warning(disable : 4244)
39#pragma warning(disable : 4457)
40#pragma warning(disable : 4702)
48int Core::Item::separatorThickness = 5;
49int Core::Item::layoutSpacing = 5;
50bool Core::Item::s_silenceSanityChecks =
false;
52DumpScreenInfoFunc Core::Item::s_dumpScreenInfoFunc =
nullptr;
53CreateSeparatorFunc Core::Item::s_createSeparatorFunc =
nullptr;
56Size Core::Item::hardcodedMinimumSize = Size(80, 90);
57Size Core::Item::hardcodedMaximumSize = Size(16777215, 16777215);
59bool Core::ItemBoxContainer::s_inhibitSimplify =
false;
60LayoutingSeparator *LayoutingSeparator::s_separatorBeingDragged =
nullptr;
95 r.adjust(0, p1, 0, p2);
97 r.adjust(p1, 0, p2, 0);
109 int available()
const
111 return std::max(0, length - minLength);
116 return std::max(0, minLength - length);
128ItemBoxContainer *Item::root()
const
130 return m_parent ? m_parent->root()
131 :
const_cast<ItemBoxContainer *
>(object_cast<const ItemBoxContainer *>(
this));
134Rect Item::mapToRoot(Rect r)
const
136 const Point topLeft = mapToRoot(r.topLeft());
137 r.moveTopLeft(topLeft);
141Point Item::mapToRoot(Point p)
const
146 return p + parentContainer()->mapToRoot(pos());
152 return mapToRoot(Point(0, p)).y();
153 return mapToRoot(Point(p, 0)).x();
156Point Item::mapFromRoot(Point p)
const
158 const Item *it =
this;
161 it = it->parentContainer();
167Rect Item::mapFromRoot(Rect r)
const
169 const Point topLeft = mapFromRoot(r.topLeft());
170 r.moveTopLeft(topLeft);
174Point Item::mapFromParent(Point p)
const
185 return mapFromRoot(Point(0, p)).y();
186 return mapFromRoot(Point(p, 0)).x();
189void Item::setGuest(LayoutingGuest *guest)
191 assert(!guest || !m_guest);
194 m_parentChangedConnection.disconnect();
195 m_guestDestroyedConnection->disconnect();
196 m_layoutInvalidatedConnection->disconnect();
199 m_guest->setHost(m_host);
200 m_guest->setLayoutItem(
this);
202 m_parentChangedConnection = m_guest->hostChanged.connect([
this](LayoutingHost *host) {
203 if (host != this->host()) {
206 turnIntoPlaceholder();
211 ScopedValueRollback guard(m_isSettingGuest,
true);
212 setMinSize(guest->minSize());
213 setMaxSizeHint(guest->maxSizeHint());
216 m_guestDestroyedConnection =
217 m_guest->beingDestroyed.connect(&Item::onGuestDestroyed,
this);
219 m_layoutInvalidatedConnection =
220 guest->layoutInvalidated.connect(&Item::onWidgetLayoutRequested,
this);
222 if (m_sizingInfo.geometry.isEmpty()) {
224 Rect widgetGeo = m_guest->geometry();
226 widgetGeo.size().expandedTo(minSize()).expandedTo(Item::hardcodedMinimumSize));
227 setGeometry(mapFromRoot(widgetGeo));
229 updateWidgetGeometries();
234void Item::updateWidgetGeometries()
237 m_guest->setGeometry(mapToRoot(rect()));
241void Item::to_json(nlohmann::json &json)
const
243 json[
"sizingInfo"] = m_sizingInfo;
244 json[
"isVisible"] = m_isVisible;
245 json[
"isContainer"] = isContainer();
247 json[
"guestId"] = m_guest->id();
250void Item::fillFromJson(
const nlohmann::json &j,
251 const std::unordered_map<QString, LayoutingGuest *> &widgets)
253 m_sizingInfo = j.value(
"sizingInfo", SizingInfo());
254 m_isVisible = j.value(
"isVisible",
false);
257 auto it = widgets.find(guestId);
258 if (it != widgets.cend()) {
259 setGuest(it->second);
260 m_guest->setHost(host());
262 KDDW_ERROR(
"Couldn't find group to restore for item={}", (
void * )
this);
268Item *Item::createFromJson(LayoutingHost *hostWidget, ItemContainer *parent,
const nlohmann::json &json,
269 const std::unordered_map<QString, LayoutingGuest *> &widgets)
271 auto item =
new Item(hostWidget, parent);
272 item->fillFromJson(json, widgets);
276void Item::setDumpScreenInfoFunc(DumpScreenInfoFunc f)
278 s_dumpScreenInfoFunc = f;
281void Item::setCreateSeparatorFunc(CreateSeparatorFunc f)
283 s_createSeparatorFunc = f;
293 assert(m_refCount > 0);
295 if (m_refCount == 0) {
297 parentContainer()->removeItem(
this);
301int Item::refCount()
const
306LayoutingHost *Item::host()
const
311LayoutingGuest *Item::guest()
const
316void Item::restore(LayoutingGuest *guest)
318 if (isVisible() || m_guest) {
319 KDDW_ERROR(
"Hitting assert. visible={}, guest={}", isVisible(), (
void * )
this);
324 KDDW_ERROR(
"Containers can't be restored");
327 parentContainer()->restore(
this);
347 const Item *it =
this;
349 if (
auto p = it->parentContainer()) {
350 const auto index = p->indexOfChild(it);
361void Item::setHost(LayoutingHost *host)
363 if (m_host != host) {
366 m_guest->setHost(host);
367 m_guest->setVisible(
true);
368 updateWidgetGeometries();
373void Item::setSize_recursive(Size newSize, ChildrenResizeStrategy)
378Size Item::missingSize()
const
380 Size missing = minSize() - this->size();
381 missing.setWidth(std::max(missing.width(), 0));
382 missing.setHeight(std::max(missing.height(), 0));
387bool Item::isBeingInserted()
const
389 return m_sizingInfo.isBeingInserted;
392void Item::setBeingInserted(
bool is)
394 m_sizingInfo.isBeingInserted = is;
398 if (
auto parent = parentContainer()) {
400 if (!parent->hasVisibleChildren())
401 parent->setBeingInserted(
true);
403 parent->setBeingInserted(
false);
408void Item::setParentContainer(ItemContainer *parent)
410 if (parent == m_parent)
414 m_minSizeChangedHandle.disconnect();
415 m_visibleChangedHandle.disconnect();
416 visibleChanged.emit(
this,
false);
419 if (
auto c = asContainer()) {
420 const bool ceasingToBeRoot = !m_parent && parent;
421 if (ceasingToBeRoot && !c->hasVisibleChildren()) {
429 connectParent(parent);
434void Item::connectParent(ItemContainer *parent)
437 m_minSizeChangedHandle =
438 minSizeChanged.connect(&ItemContainer::onChildMinSizeChanged, parent);
439 m_visibleChangedHandle =
440 visibleChanged.connect(&ItemContainer::onChildVisibleChanged, parent);
444 setHost(parent->host());
447 updateWidgetGeometries();
450 visibleChanged.emit(
this, isVisible());
454ItemContainer *Item::parentContainer()
const
459ItemBoxContainer *Item::parentBoxContainer()
const
461 return object_cast<ItemBoxContainer *>(m_parent);
464int Item::indexInAncestor(ItemContainer *ancestor,
bool visibleOnly)
const
467 while (
auto p = it->parentBoxContainer()) {
470 const auto children = visibleOnly ? ancestor->visibleChildren() : ancestor->childItems();
471 return children.indexOf(
const_cast<Item *
>(it));
479ItemBoxContainer *Item::ancestorBoxContainerWithOrientation(
Qt::Orientation o)
const
481 auto p = parentBoxContainer();
483 if (p->orientation() == o)
485 p = p->parentBoxContainer();
491const ItemContainer *Item::asContainer()
const
493 return object_cast<const ItemContainer *>(
this);
496ItemContainer *Item::asContainer()
498 return object_cast<ItemContainer *>(
this);
501ItemBoxContainer *Item::asBoxContainer()
503 return object_cast<ItemBoxContainer *>(
this);
506void Item::setMinSize(Size sz)
508 if (sz != m_sizingInfo.minSize) {
509 m_sizingInfo.minSize = sz;
510 minSizeChanged.emit(
this);
511 if (!m_isSettingGuest)
512 setSize_recursive(size().expandedTo(sz));
516void Item::setMaxSizeHint(Size sz)
518 if (sz != m_sizingInfo.maxSizeHint) {
519 m_sizingInfo.maxSizeHint = sz;
520 maxSizeChanged.emit(
this);
524Item *Item::outermostNeighbor(
Location loc,
bool visibleOnly)
const
550 return outermostNeighbor(side, o, visibleOnly);
553Item *Item::outermostNeighbor(Side side,
Qt::Orientation o,
bool visibleOnly)
const
555 auto p = parentBoxContainer();
559 const auto siblings = visibleOnly ? p->visibleChildren() : p->m_children;
560 const int index = siblings.indexOf(
const_cast<Item *
>(
this));
561 if (index == -1 || siblings.isEmpty()) {
563 KDDW_ERROR(
"Item::outermostNeighbor: item not in parent's child list");
567 const int lastIndex = siblings.count() - 1;
568 if (p->orientation() == o) {
569 if ((index == 0 && side == Side1) || (index == lastIndex && side == Side2)) {
574 auto sibling = siblings.at(side == Side1 ? 0 : lastIndex);
576 if (
auto siblingContainer = object_cast<ItemBoxContainer *>(sibling)) {
577 if (siblingContainer->orientation() == o) {
579 return siblingContainer->outermostNeighbor(side, o, visibleOnly);
586 if (
auto ancestor = p->ancestorBoxContainerWithOrientation(o)) {
587 const int indexInAncestor = this->indexInAncestor(ancestor, visibleOnly);
588 if (indexInAncestor == -1) {
590 KDDW_ERROR(
"Item::outermostNeighbor: item not in ancestor's child list");
593 return ancestor->childItems().at(indexInAncestor)->outermostNeighbor(side, o, visibleOnly);
601Size Item::minSize()
const
603 return m_sizingInfo.minSize;
606Size Item::maxSizeHint()
const
608 return m_sizingInfo.maxSizeHint.boundedTo(hardcodedMaximumSize);
611void Item::setPos(Point pos)
613 Rect geo = m_sizingInfo.geometry;
614 geo.moveTopLeft(pos);
621 setPos({ x(), pos });
623 setPos({ pos, y() });
634 return m_sizingInfo.geometry.x();
639 return m_sizingInfo.geometry.y();
642int Item::width()
const
644 return m_sizingInfo.geometry.width();
647int Item::height()
const
649 return m_sizingInfo.geometry.height();
652Size Item::size()
const
654 return m_sizingInfo.geometry.size();
657void Item::setSize(Size sz)
659 ScopedValueRollback guard(m_inSetSize,
true);
661 Rect newGeo = m_sizingInfo.geometry;
666void Item::requestResize(
int left,
int top,
int right,
int bottom)
668 if (left == 0 && right == 0 && top == 0 && bottom == 0)
671 ItemBoxContainer *parent = parentBoxContainer();
674 KDDW_ERROR(
"Item::requestResize: Could not find parent container");
679 auto moveSeparators = [](
int side1Delta,
int side2Delta, LayoutingSeparator *separator1, LayoutingSeparator *separator2) {
680 if (side1Delta != 0 && separator1) {
681 const auto ancestor = separator1->parentContainer();
682 const int min = ancestor->minPosForSeparator_global(separator1);
683 const int pos = separator1->position();
684 const int max = ancestor->maxPosForSeparator_global(separator1);
685 int newPos = pos - side1Delta;
686 newPos =
bound(min, newPos, max);
687 const int delta = newPos - pos;
689 ancestor->requestSeparatorMove(separator1, delta);
692 if (side2Delta != 0 && separator2) {
693 const auto ancestor = separator2->parentContainer();
694 const int min = ancestor->minPosForSeparator_global(separator2);
695 const int pos = separator2->position();
696 const int max = ancestor->maxPosForSeparator_global(separator2);
697 int newPos = pos + side2Delta;
698 newPos =
bound(min, newPos, max);
699 const int delta = newPos - pos;
701 ancestor->requestSeparatorMove(separator2, delta);
707 const int side1Delta = parent->isHorizontal() ?
left : top;
708 const int side2Delta = parent->isHorizontal() ?
right : bottom;
709 auto separator1 = parent->separatorForChild(
this, Side1);
710 auto separator2 = parent->separatorForChild(
this, Side2);
711 moveSeparators(side1Delta, side2Delta, separator1, separator2);
716 const int side1Delta = parent->isHorizontal() ? top :
left;
717 const int side2Delta = parent->isHorizontal() ? bottom :
right;
718 auto separator1 = parent->adjacentSeparatorForChild(
this, Side1);
719 auto separator2 = parent->adjacentSeparatorForChild(
this, Side2);
721 moveSeparators(side1Delta, side2Delta, separator1, separator2);
725Point Item::pos()
const
727 return m_sizingInfo.geometry.topLeft();
730Rect Item::geometry()
const
732 return isBeingInserted() ? Rect() : m_sizingInfo.geometry;
735Rect Item::rect()
const
737 return Rect(0, 0, width(), height());
740bool Item::isContainer()
const
742 return m_isContainer;
747 return Core::length(minSize(), o);
752 return Core::length(maxSizeHint(), o);
759 const int w = std::max(width(), hardcodedMinimumSize.width());
760 setSize(Size(w, length));
762 const int h = std::max(height(), hardcodedMinimumSize.height());
763 setSize(Size(length, h));
769 setLength(length, o);
774 return Core::length(size(), o);
779 return length(o) - minLength(o);
782bool Item::isPlaceholder()
const
787bool Item::isVisible(
bool excludeBeingInserted)
const
789 return m_isVisible && !(excludeBeingInserted && isBeingInserted());
792void Item::setIsVisible(
bool is)
794 if (is != m_isVisible) {
796 visibleChanged.emit(
this, is);
800 m_guest->setGeometry(mapToRoot(rect()));
801 m_guest->setVisible(
true);
805void Item::setGeometry_recursive(Rect rect)
811bool Item::checkSanity()
816 if (minSize().width() > width() || minSize().height() > height()) {
817 root()->dumpLayout();
818 KDDW_ERROR(
"Size constraints not honoured this={}, min={}, size={}", (
void * )
this, minSize(), size());
823 if (m_guest->host() != host()) {
825 root()->dumpLayout();
826 KDDW_ERROR(
"Unexpected host for our guest. m_guest->host()={}, host()={}",
827 (
void * )m_guest->host(), (
void * )host());
832 if (!m_guest->isVisible() && (!m_guest->parent() || m_guest->parentWidget()->isVisible())) {
834 KDDW_ERROR(
"Guest widget isn't visible {}",
this);
841 if (m_guest->geometry() != mapToRoot(rect())) {
842 root()->dumpLayout();
843 KDDW_ERROR(
"Guest widget doesn't have correct geometry. m_guest->guestGeometry={}, item.mapToRoot(rect())={}", m_guest->geometry(), mapToRoot(rect()));
851bool Item::isMDI()
const
853 return object_cast<ItemFreeContainer *>(parentContainer()) !=
nullptr;
856bool Item::inSetSize()
const
861void Item::setGeometry(Rect rect)
863 Rect &m_geometry = m_sizingInfo.geometry;
865 if (rect != m_geometry) {
866 const Rect oldGeo = m_geometry;
870 if (rect.isEmpty()) {
872 ItemContainer *c = asContainer();
874 if (c->hasVisibleChildren()) {
880 KDDW_ERROR(
"Empty rect");
884 const Size minSz = minSize();
885 if (!s_silenceSanityChecks
886 && (rect.width() < minSz.width() || rect.height() < minSz.height())) {
889 KDDW_ERROR(
"Constraints not honoured. this={}, sz={}, min={}, parent={}", (
void * )
this, rect.size(), minSz, (
void * )parentContainer());
892 geometryChanged.emit();
894 if (oldGeo.x() != x())
896 if (oldGeo.y() != y())
898 if (oldGeo.width() != width())
900 if (oldGeo.height() != height())
901 heightChanged.emit();
903 updateWidgetGeometries();
907void Item::dumpLayout(
int level,
bool)
911 std::cerr << indent <<
"- Widget: " << m_sizingInfo.geometry
912 <<
"; min=" << minSize();
914 if (maxSizeHint() != hardcodedMaximumSize)
915 std::cerr <<
"; max=" << maxSizeHint() <<
"; ";
918 std::cerr <<
";hidden;";
923 if (m_guest && geometry() != m_guest->geometry()) {
924 std::cerr <<
"; guest geometry=" << m_guest->geometry();
927 if (m_sizingInfo.isBeingInserted)
928 std::cerr <<
";beingInserted;";
930 std::cerr <<
"; item=" <<
this;
932 std::cerr <<
"; m_guest=" << m_guest->toDebugString() <<
"\n";
936Item::Item(LayoutingHost *hostWidget, ItemContainer *parent)
937 : Core::Object(parent)
938 , m_isContainer(false)
942 connectParent(parent);
945Item::Item(
bool isContainer, LayoutingHost *hostWidget, ItemContainer *parent)
946 : Core::Object(parent)
947 , m_isContainer(isContainer)
951 connectParent(parent);
957 aboutToBeDeleted.emit();
959 m_minSizeChangedHandle.disconnect();
960 m_visibleChangedHandle.disconnect();
961 m_parentChangedConnection.disconnect();
966void Item::turnIntoPlaceholder()
968 assert(!isContainer());
973 parentContainer()->removeItem(
this,
false);
976void Item::onGuestDestroyed()
979 m_parentChangedConnection.disconnect();
980 m_guestDestroyedConnection->disconnect();
983 turnIntoPlaceholder();
984 }
else if (!isRoot()) {
985 parentContainer()->removeItem(
this);
989void Item::onWidgetLayoutRequested()
991 if (
auto w = guest()) {
992 const Size guestSize = w->geometry().size();
993 if (guestSize != size() && !isMDI()) {
995 std::cerr <<
"Item::onWidgetLayoutRequested"
996 <<
"TODO: Not implemented yet. Widget can't just decide to resize yet"
997 <<
"View.size=" << guestSize <<
"Item.size=" << size() << m_sizingInfo.geometry
998 << m_sizingInfo.isBeingInserted <<
"\n";
1001 if (w->minSize() != minSize()) {
1002 setMinSize(m_guest->minSize());
1005 setMaxSizeHint(w->maxSizeHint());
1009bool Item::isRoot()
const
1011 return m_parent ==
nullptr;
1014LayoutBorderLocations Item::adjacentLayoutBorders()
const
1017 return LayoutBorderLocation_All;
1020 ItemBoxContainer *c = parentBoxContainer();
1022 return LayoutBorderLocation_None;
1024 const int indexInParent = c->indexOfVisibleChild(
this);
1025 const int numVisibleChildren = c->numVisibleChildren();
1026 const bool isFirst = indexInParent == 0;
1027 const bool isLast = indexInParent == numVisibleChildren - 1;
1028 if (indexInParent == -1)
1029 return LayoutBorderLocation_None;
1031 LayoutBorderLocations locations = LayoutBorderLocation_None;
1033 if (c->isVertical()) {
1034 locations |= LayoutBorderLocation_West;
1035 locations |= LayoutBorderLocation_East;
1038 locations |= LayoutBorderLocation_North;
1040 locations |= LayoutBorderLocation_South;
1042 locations |= LayoutBorderLocation_North;
1043 locations |= LayoutBorderLocation_South;
1046 locations |= LayoutBorderLocation_West;
1048 locations |= LayoutBorderLocation_East;
1051 const LayoutBorderLocations parentBorders = c->adjacentLayoutBorders();
1052 if (c->isVertical()) {
1053 if (parentBorders & LayoutBorderLocation_West)
1054 locations |= LayoutBorderLocation_West;
1056 if (parentBorders & LayoutBorderLocation_East)
1057 locations |= LayoutBorderLocation_East;
1059 if (isFirst && (parentBorders & LayoutBorderLocation_North))
1060 locations |= LayoutBorderLocation_North;
1062 if (isLast && (parentBorders & LayoutBorderLocation_South))
1063 locations |= LayoutBorderLocation_South;
1066 if (parentBorders & LayoutBorderLocation_North)
1067 locations |= LayoutBorderLocation_North;
1069 if (parentBorders & LayoutBorderLocation_South)
1070 locations |= LayoutBorderLocation_South;
1072 if (isFirst && (parentBorders & LayoutBorderLocation_West))
1073 locations |= LayoutBorderLocation_West;
1075 if (isLast && (parentBorders & LayoutBorderLocation_East))
1076 locations |= LayoutBorderLocation_East;
1083int Item::visibleCount_recursive()
const
1085 return isVisible() ? 1 : 0;
1088struct ItemBoxContainer::Private
1090 explicit Private(ItemBoxContainer *qq)
1093 if (!Item::s_createSeparatorFunc) {
1094 KDDW_ERROR(
"Item doesn't know how to create separators! Aborting.\n"
1095 "If you're using the layouting engine outside of KDDW, don't forget"
1096 " to call KDDockWidgets::Core::Item::createSeparatorFunc()");
1103 for (
const auto &sep :
std::as_const(m_separators))
1105 m_separators.clear();
1109 int defaultLengthFor(Item *item,
const InitialOption &option)
const;
1111 void relayoutIfNeeded();
1112 const Item *itemFromPath(
const Vector<int> &path)
const;
1113 void resizeChildren(Size oldSize, Size newSize, SizingInfo::List &sizes,
1114 ChildrenResizeStrategy);
1115 void honourMaxSizes(SizingInfo::List &sizes);
1116 void scheduleCheckSanity()
const;
1117 LayoutingSeparator *neighbourSeparator(
const Item *item, Side,
1119 LayoutingSeparator *neighbourSeparator_recursive(
const Item *item, Side,
1121 void updateWidgets_recursive();
1125 void updateSeparators();
1126 void deleteSeparators();
1127 LayoutingSeparator *separatorAt(
int p)
const;
1129 bool isDummy()
const;
1130 void deleteSeparators_recursive();
1131 void updateSeparators_recursive();
1132 Size minSize(
const Item::List &items)
const;
1133 int excessLength()
const;
1135 mutable bool m_checkSanityScheduled =
false;
1137 bool m_convertingItemToContainer =
false;
1138 bool m_blockUpdatePercentages =
false;
1139 bool m_isDeserializing =
false;
1140 bool m_isSimplifying =
false;
1142 ItemBoxContainer *
const q;
1145ItemBoxContainer::ItemBoxContainer(LayoutingHost *hostWidget, ItemContainer *parent)
1146 : ItemContainer(hostWidget, parent)
1147 , d(new Private(this))
1152ItemBoxContainer::ItemBoxContainer(LayoutingHost *hostWidget)
1153 : ItemContainer(hostWidget, nullptr)
1154 , d(new Private(this))
1158ItemBoxContainer::~ItemBoxContainer()
1166 if (d->m_orientation == o) {
1168 for (Item *child : m_children) {
1169 if (ItemBoxContainer *container = child->asBoxContainer()) {
1170 num += container->numSideBySide_recursive(o);
1171 }
else if (!child->isPlaceholder()) {
1177 for (Item *child : m_children) {
1178 if (ItemBoxContainer *container = child->asBoxContainer()) {
1179 num = std::max(num, container->numSideBySide_recursive(o));
1180 }
else if (!child->isPlaceholder()) {
1181 num = std::max(num, 1);
1189bool ItemBoxContainer::percentagesAreSane()
const
1191 const Item::List visibleChildren = this->visibleChildren();
1193 const double totalPercentage = std::accumulate(percentages.begin(), percentages.end(), 0.0);
1194 const double expectedPercentage = visibleChildren.isEmpty() ? 0.0 : 1.0;
1195 if (!
fuzzyCompare(totalPercentage, expectedPercentage)) {
1196 root()->dumpLayout();
1197 KDDW_ERROR(
"Percentages don't add up", totalPercentage, percentages, (
void * )
this);
1204bool ItemBoxContainer::checkSanity()
1206 d->m_checkSanityScheduled =
false;
1208#ifdef KDDW_FRONTEND_QT
1210 if (!
plat ||
plat->
d->inDestruction()) {
1222 if (!Item::checkSanity())
1225 if (numChildren() == 0 && !isRoot()) {
1226 KDDW_ERROR(
"Container is empty. Should be deleted");
1231 KDDW_ERROR(
"Invalid orientation={}, this={}", d->m_orientation, (
void * )
this);
1236 int expectedPos = 0;
1237 const auto children = childItems();
1238 for (Item *item : children) {
1239 if (!item->isVisible())
1241 const int pos = Core::pos(item->pos(), d->m_orientation);
1242 if (expectedPos != pos) {
1243 root()->dumpLayout();
1244 KDDW_ERROR(
"Unexpected pos={}, expected={}, item={}, isContainer={}", pos, expectedPos, (
void * )item,
1245 item->isContainer());
1249 expectedPos = pos + Core::length(item->size(), d->m_orientation) + layoutSpacing;
1253 for (Item *item : children) {
1254 if (item->parentContainer() !=
this) {
1255 KDDW_ERROR(
"Invalid parent container for item={}, is={}, expected={}", (
void * )item, (
void * )item->parentContainer(), (
void * )
this);
1259 if (item->parent() !=
this) {
1260 KDDW_ERROR(
"Invalid Object parent for item={}, is={}, expected={}", (
void * )item, (
void * )item->parent(), (
void * )
this);
1264 if (item->isVisible()) {
1268 root()->dumpLayout();
1269 KDDW_ERROR(
"Invalid size for item {}, Container.length={}, item.length={}", (
void * )item, h1, h2);
1273 if (!rect().contains(item->geometry())) {
1274 root()->dumpLayout();
1275 KDDW_ERROR(
"Item geo is out of bounds. item={}, geo={}, parent.rect={}", (
void * )item, item->geometry(), rect());
1280 if (!item->checkSanity())
1284 const Item::List visibleChildren = this->visibleChildren();
1285 const bool isEmptyRoot = isRoot() && visibleChildren.isEmpty();
1287 auto occupied = std::max(0, Item::layoutSpacing * (
int(visibleChildren.size()) - 1));
1288 for (Item *item : visibleChildren) {
1289 occupied += item->length(d->m_orientation);
1292 if (occupied != length()) {
1293 root()->dumpLayout();
1294 KDDW_ERROR(
"Unexpected length. Expected={}, got={}, this={}", occupied, length(), (
void * )
this);
1298 if (!percentagesAreSane()) {
1300 const_cast<ItemBoxContainer *
>(
this)->d->updateSeparators_recursive();
1301 if (!percentagesAreSane())
1306 const auto numVisibleChildren = int(visibleChildren.size());
1307 if (d->m_separators.size() != std::max(0, numVisibleChildren - 1)) {
1308 root()->dumpLayout();
1309 KDDW_ERROR(
"Unexpected number of separators sz={}, numVisibleChildren={}", d->m_separators.size(), numVisibleChildren);
1313 const Size expectedSeparatorSize = isVertical() ? Size(width(), Item::separatorThickness)
1314 : Size(Item::separatorThickness, height());
1316 const int pos2 = Core::pos(mapToRoot(Point(0, 0)),
oppositeOrientation(d->m_orientation));
1318 for (
int i = 0; i < d->m_separators.size(); ++i) {
1319 LayoutingSeparator *separator = d->m_separators.at(i);
1320 Item *item = visibleChildren.at(i);
1321 const int expectedSeparatorPos =
1322 mapToRoot(item->m_sizingInfo.edge(d->m_orientation) + 1, d->m_orientation);
1324 if (separator->m_host != host()) {
1325 KDDW_ERROR(
"Invalid host widget for separator this={}", (
void * )
this);
1329 if (separator->parentContainer() !=
this) {
1330 KDDW_ERROR(
"Invalid parent container for separator parent={}, separator={}, this={}", (
void * )separator->parentContainer(), (
void * )separator, (
void * )
this);
1334 if (separator->position() != expectedSeparatorPos) {
1335 root()->dumpLayout();
1336 KDDW_ERROR(
"Unexpected separator position, expected={}, separator={}, this={}", separator->position(), expectedSeparatorPos, (
void * )separator, (
void * )
this);
1339 const Rect separatorGeometry = separator->geometry();
1340 if (separatorGeometry.size() != expectedSeparatorSize) {
1341 KDDW_ERROR(
"Unexpected separator size={}, expected={}, separator={}, this={}", separatorGeometry.size(), expectedSeparatorSize, (
void * )separator, (
void * )
this);
1345 const int separatorPos2 = Core::pos(separatorGeometry.topLeft(),
1347 if (Core::pos(separatorGeometry.topLeft(),
1350 root()->dumpLayout();
1351 KDDW_ERROR(
"Unexpected position pos2={}, expected={}, separator={}, this={}", separatorPos2, pos2, (
void * )separator, (
void * )
this);
1357 const int separatorMinPos = minPosForSeparator_global(separator,
false);
1358 const int separatorMaxPos = maxPosForSeparator_global(separator,
false);
1359 const int separatorPos = separator->position();
1360 if (separatorPos < separatorMinPos || separatorPos > separatorMaxPos || separatorMinPos < 0
1361 || separatorMaxPos <= 0) {
1362 root()->dumpLayout();
1363 KDDW_ERROR(
"Invalid bounds for separator, pos={}, min={}, max={}, separator={}", separatorPos, separatorMinPos, separatorMaxPos, (
void * )separator);
1368#ifdef DOCKS_DEVELOPER_MODE
1371 if (!asBoxContainer()->test_suggestedRect())
1379void ItemBoxContainer::Private::scheduleCheckSanity()
const
1381#ifdef KDDW_FRONTEND_QT
1382 if (!m_checkSanityScheduled) {
1383 m_checkSanityScheduled =
true;
1389bool ItemBoxContainer::hasOrientation()
const
1391 return isVertical() || isHorizontal();
1394int ItemBoxContainer::indexOfVisibleChild(
const Item *item)
const
1396 const Item::List items = visibleChildren();
1397 return items.indexOf(
const_cast<Item *
>(item));
1400void ItemBoxContainer::restore(Item *child)
1405void ItemBoxContainer::removeItem(Item *item,
bool hardRemove)
1407 assert(!item->isRoot());
1409 if (!contains(item)) {
1410 if (item->parentContainer() ==
this) {
1412 KDDW_ERROR(
"ItemBoxContainer::removeItem: Could not find item as children, but it has us as parent!");
1418 item->parentContainer()->removeItem(item, hardRemove);
1422 Item *side1Item = visibleNeighbourFor(item, Side1);
1423 Item *side2Item = visibleNeighbourFor(item, Side2);
1425 const bool isContainer = item->isContainer();
1426 const bool wasVisible = !isContainer && item->isVisible();
1429 m_children.removeOne(item);
1432 root()->numItemsChanged.emit();
1434 item->setIsVisible(
false);
1435 item->setGuest(
nullptr);
1437 if (!wasVisible && !isContainer) {
1444 root()->numVisibleItemsChanged.emit(root()->numVisibleChildren());
1449 if (
auto p = parentContainer())
1450 p->removeItem(
this,
true);
1451 }
else if (!hasVisibleChildren()) {
1452 if (
auto p = parentContainer()) {
1453 p->removeItem(
this,
false);
1454 setGeometry(Rect());
1458 growNeighbours(side1Item, side2Item);
1459 itemsChanged.emit();
1461 updateSizeConstraints();
1462 d->updateSeparators_recursive();
1466void ItemBoxContainer::setGeometry_recursive(Rect rect)
1468 setPos(rect.topLeft());
1471 setSize_recursive(rect.size());
1474ItemBoxContainer *ItemBoxContainer::convertChildToContainer(Item *leaf,
const InitialOption &opt)
1476 ScopedValueRollback converting(d->m_convertingItemToContainer,
true);
1478 const auto index = m_children.indexOf(leaf);
1479 assert(index != -1);
1480 auto container =
new ItemBoxContainer(host(),
this);
1481 container->setParentContainer(
nullptr);
1482 container->setParentContainer(
this);
1486 insertItem(container, index, option);
1488 m_children.removeOne(leaf);
1489 container->setGeometry(leaf->isVisible() ? leaf->geometry() : Rect());
1490 if (!leaf->isVisible())
1494 itemsChanged.emit();
1495 d->updateSeparators_recursive();
1502void ItemBoxContainer::insertItemRelativeTo(Item *item, Item *relativeTo,
Location loc,
1505 assert(item != relativeTo);
1507 if (
auto asContainer = relativeTo->asBoxContainer()) {
1508 asContainer->insertItem(item, loc, option);
1513 assert(!(option.
startsHidden() && item->isContainer()));
1515 ItemBoxContainer *parent = relativeTo->parentBoxContainer();
1517 KDDW_ERROR(
"This method should only be called for box containers parent={}", (
void * )item->parent());
1521 if (parent->hasOrientationFor(loc)) {
1523 auto indexInParent = parent->indexOfChild(relativeTo);
1528 if (orientation != parent->orientation()) {
1529 assert(parent->visibleChildren().size() == 1);
1532 parent->setOrientation(orientation);
1535 parent->insertItem(item, indexInParent, option);
1537 ItemBoxContainer *container = parent->convertChildToContainer(relativeTo, option);
1538 container->insertItem(item, loc, option);
1542void ItemBoxContainer::insertItem(Item *item,
Location loc,
1545 assert(item !=
this);
1546 if (contains(item)) {
1547 KDDW_ERROR(
"Item already exists");
1552 assert(!(initialOption.
startsHidden() && item->isContainer()));
1556 if (hasOrientationFor(loc)) {
1557 if (m_children.size() == 1) {
1559 d->m_orientation = locOrientation;
1563 insertItem(item, index, initialOption);
1567 auto container =
new ItemBoxContainer(host(),
this);
1568 container->setGeometry(rect());
1569 container->setChildren(m_children, d->m_orientation);
1573 insertItem(container, 0, {});
1576 insertItem(item, loc, initialOption);
1578 if (!container->hasVisibleChildren())
1579 container->setGeometry(Rect());
1582 d->updateSeparators_recursive();
1583 d->scheduleCheckSanity();
1586void ItemBoxContainer::onChildMinSizeChanged(Item *child)
1588 if (d->m_convertingItemToContainer || d->m_isDeserializing || !child->isVisible()) {
1593 updateSizeConstraints();
1595 if (child->isBeingInserted())
1598 if (numVisibleChildren() == 1 && child->isVisible()) {
1600 child->setGeometry(rect());
1601 updateChildPercentages();
1605 const Size missingForChild = child->missingSize();
1606 if (!missingForChild.isNull()) {
1609 growItem(child, Core::length(missingForChild, d->m_orientation),
1613 updateChildPercentages();
1616void ItemBoxContainer::updateSizeConstraints()
1618 const Size missingSize = this->missingSize();
1619 if (!missingSize.isNull()) {
1622 setSize_recursive(size() + missingSize);
1627 minSizeChanged.emit(
this);
1630void ItemBoxContainer::onChildVisibleChanged(Item *,
bool visible)
1632 if (d->m_isDeserializing || isInSimplify())
1635 const int numVisible = numVisibleChildren();
1636 if (visible && numVisible == 1) {
1639 visibleChanged.emit(
this,
true);
1640 }
else if (!visible && numVisible == 0) {
1641 visibleChanged.emit(
this,
false);
1645Rect ItemBoxContainer::suggestedDropRect(
const Item *item,
const Item *relativeTo,
1655 if (relativeTo && !relativeTo->parentContainer()) {
1656 KDDW_ERROR(
"No parent container");
1660 if (relativeTo && relativeTo->parentContainer() !=
this) {
1661 KDDW_ERROR(
"Called on the wrong container");
1665 if (relativeTo && !relativeTo->isVisible()) {
1666 KDDW_ERROR(
"relative to isn't visible");
1671 KDDW_ERROR(
"Invalid location");
1675 const Size availableSize = root()->availableSize();
1676 const Size minSize = item->minSize();
1677 const bool isEmpty = !root()->hasVisibleChildren();
1678 const int extraWidth = (isEmpty ||
locationIsVertical(loc)) ? 0 : Item::layoutSpacing;
1679 const int extraHeight = (isEmpty || !
locationIsVertical(loc)) ? 0 : Item::layoutSpacing;
1680 const bool windowNeedsGrowing = availableSize.width() < minSize.width() + extraWidth
1681 || availableSize.height() < minSize.height() + extraHeight;
1683 if (windowNeedsGrowing)
1684 return suggestedDropRectFallback(item, relativeTo, loc);
1686 nlohmann::json rootSerialized;
1687 root()->to_json(rootSerialized);
1689 ItemBoxContainer rootCopy(
nullptr);
1690 rootCopy.fillFromJson(rootSerialized, {});
1693 relativeTo = rootCopy.d->itemFromPath(relativeTo->pathFromRoot());
1695 nlohmann::json itemSerialized;
1696 item->to_json(itemSerialized);
1697 auto itemCopy =
new Item(
nullptr);
1698 itemCopy->fillFromJson(itemSerialized, {});
1702 auto r =
const_cast<Item *
>(relativeTo);
1703 ItemBoxContainer::insertItemRelativeTo(itemCopy, r, loc, opt);
1705 rootCopy.insertItem(itemCopy, loc, opt);
1708 if (rootCopy.size() != root()->size()) {
1710 KDDW_ERROR(
"The root copy grew ?! copy={}, sz={}, loc={}", rootCopy.size(), root()->size(), loc);
1711 return suggestedDropRectFallback(item, relativeTo, loc);
1714 return itemCopy->mapToRoot(itemCopy->rect());
1718Rect ItemBoxContainer::suggestedDropRectFallback(
const Item *item,
const Item *relativeTo,
1721 const Size minSize = item->minSize();
1722 const int itemMin = Core::length(minSize, d->m_orientation);
1723 const int available = availableLength() - Item::layoutSpacing;
1725 int suggestedPos = 0;
1726 const Rect relativeToGeo = relativeTo->geometry();
1730 suggestedPos = relativeToGeo.x();
1733 suggestedPos = relativeToGeo.y();
1736 suggestedPos = relativeToGeo.right() - suggestedLength + 1;
1739 suggestedPos = relativeToGeo.bottom() - suggestedLength + 1;
1747 rect.setTopLeft(Point(relativeTo->x(), suggestedPos));
1748 rect.setSize(Size(relativeTo->width(), suggestedLength));
1750 rect.setTopLeft(Point(suggestedPos, relativeTo->y()));
1751 rect.setSize(Size(suggestedLength, relativeTo->height()));
1754 return mapToRoot(rect);
1755 }
else if (isRoot()) {
1757 Rect rect = this->rect();
1758 const int oneThird = length() / 3;
1759 const int suggestedLength = std::max(std::min(available, oneThird), itemMin);
1763 rect.setWidth(suggestedLength);
1766 rect.setHeight(suggestedLength);
1769 rect.adjust(rect.width() - suggestedLength, 0, 0, 0);
1772 rect.adjust(0, rect.bottom() - suggestedLength, 0, 0);
1781 KDDW_ERROR(
"Shouldn't happen");
1787void ItemBoxContainer::positionItems()
1789 SizingInfo::List sizes = this->sizes();
1790 positionItems(sizes);
1791 applyPositions(sizes);
1793 d->updateSeparators_recursive();
1796void ItemBoxContainer::positionItems_recursive()
1799 for (Item *item :
std::as_const(m_children)) {
1800 if (item->isVisible()) {
1801 if (
auto c = item->asBoxContainer())
1802 c->positionItems_recursive();
1807void ItemBoxContainer::applyPositions(
const SizingInfo::List &sizes)
1809 const Item::List items = visibleChildren();
1810 const auto count = items.size();
1811 assert(count == sizes.size());
1812 for (
int i = 0; i < count; ++i) {
1813 Item *item = items.at(i);
1814 const SizingInfo &sizing = sizes[i];
1815 if (sizing.isBeingInserted) {
1824 item->setPos(sizing.geometry.topLeft());
1830 return d->m_orientation;
1833void ItemBoxContainer::positionItems(SizingInfo::List &sizes)
1836 const auto count = sizes.count();
1838 for (
auto i = 0; i < count; ++i) {
1839 SizingInfo &sizing = sizes[i];
1840 if (sizing.isBeingInserted) {
1841 nextPos += Item::layoutSpacing;
1851 sizing.setPos(nextPos, d->m_orientation);
1852 nextPos += sizing.length(d->m_orientation) + Item::layoutSpacing;
1856void ItemBoxContainer::clear()
1858 for (Item *item :
std::as_const(m_children)) {
1859 if (ItemBoxContainer *container = item->asBoxContainer())
1865 d->deleteSeparators();
1868Item *ItemBoxContainer::itemAt(Point p)
const
1870 for (Item *item :
std::as_const(m_children)) {
1871 if (item->isVisible() && item->geometry().contains(p))
1878Item *ItemBoxContainer::itemAt_recursive(Point p)
const
1880 if (Item *item = itemAt(p)) {
1881 if (
auto c = item->asBoxContainer()) {
1882 return c->itemAt_recursive(c->mapFromParent(p));
1891void ItemBoxContainer::setHost(LayoutingHost *host)
1893 Item::setHost(host);
1894 d->deleteSeparators_recursive();
1895 for (Item *item :
std::as_const(m_children)) {
1896 item->setHost(host);
1899 d->updateSeparators_recursive();
1902void ItemBoxContainer::setIsVisible(
bool)
1907bool ItemBoxContainer::isVisible(
bool excludeBeingInserted)
const
1909 return hasVisibleChildren(excludeBeingInserted);
1912void ItemBoxContainer::setLength_recursive(
int length,
Qt::Orientation o)
1916 sz.setHeight(length);
1918 sz.setWidth(length);
1921 setSize_recursive(sz);
1924void ItemBoxContainer::insertItem(Item *item,
int index,
const InitialOption &option)
1926 const bool containerWasVisible = hasVisibleChildren(
true);
1931 const int suggestedLength = d->defaultLengthFor(item, option);
1932 item->setLength_recursive(suggestedLength, d->m_orientation);
1934 if (!containerWasVisible) {
1948 const auto l = std::max(item->minLength(crossAxis), option.
preferredLength(crossAxis));
1949 item->setLength_recursive(l, crossAxis);
1954 m_children.insert(index, item);
1955 item->setParentContainer(
this);
1957 itemsChanged.emit();
1959 if (!d->m_convertingItemToContainer && item->isVisible()) {
1961 const bool restoreItself = !containerWasVisible && m_children.count() > 1;
1966 const bool shouldEmitVisibleChanged = item->isVisible();
1968 if (!d->m_convertingItemToContainer && !s_inhibitSimplify)
1971 if (shouldEmitVisibleChanged)
1972 root()->numVisibleItemsChanged.emit(root()->numVisibleChildren());
1973 root()->numItemsChanged.emit();
1976bool ItemBoxContainer::hasOrientationFor(
Location loc)
const
1978 if (m_children.size() <= 1)
1984int ItemBoxContainer::usableLength()
const
1986 const Item::List children = visibleChildren();
1987 const auto numVisibleChildren = children.size();
1989 if (children.size() <= 1)
1990 return Core::length(size(), d->m_orientation);
1992 const int separatorWaste = layoutSpacing * (numVisibleChildren - 1);
1993 return length() - separatorWaste;
1996void ItemBoxContainer::setChildren(
const List &children,
Qt::Orientation o)
1998 m_children = children;
1999 for (Item *item : children)
2000 item->setParentContainer(this);
2007 if (o != d->m_orientation) {
2008 d->m_orientation = o;
2009 d->updateSeparators_recursive();
2013Size ItemBoxContainer::Private::minSize(
const Item::List &items)
const
2018 if (!q->m_children.isEmpty()) {
2019 for (Item *item : items) {
2020 if (!(item->isVisible() || item->isBeingInserted()))
2023 if (q->isVertical()) {
2024 minW = std::max(minW, item->minSize().width());
2025 minH += item->minSize().height();
2027 minH = std::max(minH, item->minSize().height());
2028 minW += item->minSize().width();
2032 const int separatorWaste = std::max(0, (numVisible - 1) * layoutSpacing);
2033 if (q->isVertical())
2034 minH += separatorWaste;
2036 minW += separatorWaste;
2039 return Size(minW, minH);
2042Size ItemBoxContainer::minSize()
const
2044 return d->minSize(m_children);
2047Size ItemBoxContainer::maxSizeHint()
const
2049 int maxW = isVertical() ? hardcodedMaximumSize.width() : 0;
2050 int maxH = isVertical() ? 0 : hardcodedMaximumSize.height();
2052 const Item::List visibleChildren = this->visibleChildren(
false);
2053 if (!visibleChildren.isEmpty()) {
2054 for (Item *item : visibleChildren) {
2055 if (item->isBeingInserted())
2057 const Size itemMaxSz = item->maxSizeHint();
2058 const int itemMaxWidth = itemMaxSz.width();
2059 const int itemMaxHeight = itemMaxSz.height();
2061 maxW = std::min(maxW, itemMaxWidth);
2062 maxH = std::min(maxH + itemMaxHeight, hardcodedMaximumSize.height());
2064 maxH = std::min(maxH, itemMaxHeight);
2065 maxW = std::min(maxW + itemMaxWidth, hardcodedMaximumSize.width());
2069 const auto separatorWaste = (int(visibleChildren.size()) - 1) * layoutSpacing;
2071 maxH = std::min(maxH + separatorWaste, hardcodedMaximumSize.height());
2073 maxW = std::min(maxW + separatorWaste, hardcodedMaximumSize.width());
2078 maxW = hardcodedMaximumSize.width();
2081 maxH = hardcodedMaximumSize.height();
2083 return Size(maxW, maxH).expandedTo(d->minSize(visibleChildren));
2086void ItemBoxContainer::Private::resizeChildren(Size oldSize, Size newSize,
2087 SizingInfo::List &childSizes,
2088 ChildrenResizeStrategy strategy)
2095 const Vector<double> childPercentages = this->childPercentages();
2096 const auto count = childSizes.count();
2097 const bool widthChanged = oldSize.width() != newSize.width();
2098 const bool heightChanged = oldSize.height() != newSize.height();
2099 const bool lengthChanged =
2100 (q->isVertical() && heightChanged) || (q->isHorizontal() && widthChanged);
2101 const int totalNewLength = q->usableLength();
2103 if (strategy == ChildrenResizeStrategy::Percentage) {
2108 int remaining = totalNewLength;
2109 for (
int i = 0; i < count; ++i) {
2110 const bool isLast = i == count - 1;
2112 SizingInfo &itemSize = childSizes[i];
2114 const double childPercentage = childPercentages.
at(i);
2115 const int newItemLength = lengthChanged
2116 ? (isLast ? remaining : int(childPercentage * totalNewLength))
2117 : itemSize.length(m_orientation);
2119 if (newItemLength <= 0) {
2120 q->root()->dumpLayout();
2121 KDDW_ERROR(
"Invalid resize newItemLength={}", newItemLength);
2126 remaining = remaining - newItemLength;
2128 if (q->isVertical()) {
2129 itemSize.geometry.setSize({ q->width(), newItemLength });
2131 itemSize.geometry.setSize({ newItemLength, q->height() });
2134 }
else if (strategy == ChildrenResizeStrategy::Side1SeparatorMove
2135 || strategy == ChildrenResizeStrategy::Side2SeparatorMove) {
2136 int remaining = Core::length(
2140 const bool isGrowing = remaining > 0;
2141 remaining = std::abs(remaining);
2149 const bool isSide1SeparatorMove = strategy == ChildrenResizeStrategy::Side1SeparatorMove;
2150 bool resizeHeadFirst =
false;
2151 if (isGrowing && isSide1SeparatorMove) {
2152 resizeHeadFirst =
true;
2153 }
else if (isGrowing && !isSide1SeparatorMove) {
2154 resizeHeadFirst =
false;
2155 }
else if (!isGrowing && isSide1SeparatorMove) {
2156 resizeHeadFirst =
false;
2157 }
else if (!isGrowing && !isSide1SeparatorMove) {
2158 resizeHeadFirst =
true;
2161 for (
int i = 0; i < count; i++) {
2162 const auto index = resizeHeadFirst ? i : count - 1 - i;
2164 SizingInfo &size = childSizes[index];
2168 size.incrementLength(remaining, m_orientation);
2171 const int availableToGive = size.availableLength(m_orientation);
2172 const int took = std::min(availableToGive, remaining);
2173 size.incrementLength(-took, m_orientation);
2181 honourMaxSizes(childSizes);
2184void ItemBoxContainer::Private::honourMaxSizes(SizingInfo::List &sizes)
2189 int amountNeededToShrink = 0;
2190 int amountAvailableToGrow = 0;
2194 for (
int i = 0; i < sizes.count(); ++i) {
2195 SizingInfo &info = sizes[i];
2196 const int neededToShrink = info.neededToShrink(m_orientation);
2197 const int availableToGrow = info.availableToGrow(m_orientation);
2199 if (neededToShrink > 0) {
2200 amountNeededToShrink += neededToShrink;
2201 indexesOfShrinkers.push_back(i);
2202 }
else if (availableToGrow > 0) {
2203 amountAvailableToGrow = std::min(amountAvailableToGrow + availableToGrow, q->length());
2204 indexesOfGrowers.push_back(i);
2209 amountAvailableToGrow = std::min(amountNeededToShrink, amountAvailableToGrow);
2212 amountNeededToShrink = std::min(amountAvailableToGrow, amountNeededToShrink);
2214 if (amountNeededToShrink == 0 || amountAvailableToGrow == 0)
2221 while (amountAvailableToGrow > 0) {
2223 auto toGrow = std::max(1, amountAvailableToGrow /
int(indexesOfGrowers.
size()));
2225 for (
auto it = indexesOfGrowers.begin(); it != indexesOfGrowers.end();) {
2226 const int index = *it;
2227 SizingInfo &sizing = sizes[index];
2228 const auto grew = std::min(sizing.availableToGrow(m_orientation), toGrow);
2229 sizing.incrementLength(grew, m_orientation);
2230 amountAvailableToGrow -= grew;
2232 if (amountAvailableToGrow == 0) {
2237 if (sizing.availableToGrow(m_orientation) == 0) {
2239 it = indexesOfGrowers.erase(it);
2247 while (amountNeededToShrink > 0) {
2249 auto toShrink = std::max(1, amountNeededToShrink /
int(indexesOfShrinkers.
size()));
2251 for (
auto it = indexesOfShrinkers.begin(); it != indexesOfShrinkers.end();) {
2252 const int index = *it;
2253 SizingInfo &sizing = sizes[index];
2254 const auto shrunk = std::min(sizing.neededToShrink(m_orientation), toShrink);
2255 sizing.incrementLength(-shrunk, m_orientation);
2256 amountNeededToShrink -= shrunk;
2258 if (amountNeededToShrink == 0) {
2263 if (sizing.neededToShrink(m_orientation) == 0) {
2265 it = indexesOfShrinkers.erase(it);
2273bool ItemBoxContainer::hostSupportsHonouringLayoutMinSize()
const
2280 return m_host->supportsHonouringLayoutMinSize();
2283void ItemBoxContainer::setSize_recursive(Size newSize, ChildrenResizeStrategy strategy)
2285 ScopedValueRollback block(d->m_blockUpdatePercentages,
true);
2287 const Size minSize = this->minSize();
2288 if (newSize.width() < minSize.width() || newSize.height() < minSize.height()) {
2289 if (!s_silenceSanityChecks && hostSupportsHonouringLayoutMinSize()) {
2290 root()->dumpLayout();
2291 KDDW_ERROR(
"New size doesn't respect size constraints new={}, min={}, this={}", newSize, minSize, (
void * )
this);
2296 if (newSize == size())
2299 const Size oldSize = size();
2302 const Item::List children = visibleChildren();
2303 const auto count = children.size();
2304 SizingInfo::List childSizes = sizes();
2314 d->resizeChildren(oldSize, newSize, childSizes, strategy);
2317 positionItems( childSizes);
2320 for (
int i = 0; i < count; ++i) {
2321 SizingInfo &size = childSizes[i];
2322 const int missing = size.missingLength(d->m_orientation);
2324 growItem(i, childSizes, missing, GrowthStrategy::BothSidesEqually,
2329 applyGeometries(childSizes, strategy);
2332int ItemBoxContainer::length()
const
2334 return isVertical() ? height() : width();
2337void ItemBoxContainer::dumpLayout(
int level,
bool printSeparators)
2339 if (level == 0 && host() && s_dumpScreenInfoFunc)
2340 s_dumpScreenInfoFunc();
2343 const std::string beingInserted =
2344 m_sizingInfo.isBeingInserted ?
"; beingInserted;" :
"";
2345 const std::string visible = !isVisible() ?
";hidden;" :
"";
2346 const bool isOverflow = isOverflowing();
2347 const Size missingSize_ = missingSize();
2348 const std::string isOverflowStr = isOverflow ?
"; overflowing ;" :
"";
2349 const std::string missingSizeStr = missingSize_.isNull() ?
"" : (std::string(
"; missingSize=") + std::to_string(missingSize_.width()) +
"x" + std::to_string(missingSize_.height()));
2351 const std::string typeStr = isRoot() ?
"- Root " :
"- Layout ";
2354 const std::string orientationStr = d->m_orientation ==
Qt::Vertical ?
"V" :
"H";
2355 std::cerr << indent << typeStr << orientationStr <<
": "
2356 << m_sizingInfo.geometry
2358 <<
"; min=" << minSize() <<
"; this=" <<
this << beingInserted << visible
2359 <<
"; %=" << d->childPercentages();
2361 if (maxSizeHint() != Item::hardcodedMaximumSize)
2362 std::cerr <<
"; max=" << maxSizeHint();
2364 std::cerr << missingSizeStr << isOverflowStr <<
"\n";
2368 for (Item *item :
std::as_const(m_children)) {
2369 item->dumpLayout(level + 1, printSeparators);
2370 if (printSeparators && item->isVisible()) {
2371 if (i < d->m_separators.size()) {
2372 auto separator = d->m_separators.at(i);
2373 std::cerr << std::string(
LAYOUT_DUMP_INDENT *
size_t(level + 1),
' ') <<
"- Separator: "
2374 <<
"local.geo=" << mapFromRoot(separator->geometry())
2375 <<
" ; global.geo=" << separator->geometry() <<
"; separator=" << separator <<
"\n";
2382void ItemBoxContainer::updateChildPercentages()
2384 if (root()->d->m_blockUpdatePercentages)
2387 const int usable = usableLength();
2388 for (Item *item :
std::as_const(m_children)) {
2389 if (item->isVisible() && !item->isBeingInserted()) {
2390 item->m_sizingInfo.percentageWithinParent =
2391 (1.0 * item->length(d->m_orientation)) / usable;
2393 item->m_sizingInfo.percentageWithinParent = 0.0;
2398void ItemBoxContainer::updateChildPercentages_recursive()
2400 updateChildPercentages();
2401 for (Item *item :
std::as_const(m_children)) {
2402 if (
auto c = item->asBoxContainer())
2403 c->updateChildPercentages_recursive();
2407Vector<double> ItemBoxContainer::Private::childPercentages()
const
2410 percentages.
reserve(q->m_children.size());
2412 for (Item *item :
std::as_const(q->m_children)) {
2413 if (item->isVisible() && !item->isBeingInserted())
2414 percentages.push_back(item->m_sizingInfo.percentageWithinParent);
2420void ItemBoxContainer::restoreChild(Item *item,
bool forceRestoreContainer,
NeighbourSqueezeStrategy neighbourSqueezeStrategy)
2422 assert(contains(item));
2424 const bool shouldRestoreContainer = forceRestoreContainer || !hasVisibleChildren(
true);
2426 item->setBeingInserted(
true);
2427 item->setIsVisible(
true);
2429 const int excessLength = d->excessLength();
2431 if (shouldRestoreContainer) {
2433 if (
auto c = parentBoxContainer()) {
2434 setSize(item->size());
2436 c->restoreChild(
this,
false, neighbourSqueezeStrategy);
2441 updateSizeConstraints();
2443 item->setBeingInserted(
false);
2445 if (numVisibleChildren() == 1) {
2447 item->setGeometry_recursive(rect());
2448 d->updateSeparators_recursive();
2452 const int available = availableToSqueezeOnSide(item, Side1)
2453 + availableToSqueezeOnSide(item, Side2) - Item::layoutSpacing;
2455 const int max = std::min(available, item->maxLengthHint(d->m_orientation));
2456 const int min = item->minLength(d->m_orientation);
2466 const int proposed = std::max(Core::length(item->size(), d->m_orientation),
2467 excessLength - Item::layoutSpacing);
2468 const int newLength =
bound(min, proposed, max);
2470 assert(item->isVisible());
2475 item->m_sizingInfo.geometry.setHeight(0);
2477 item->m_sizingInfo.geometry.setWidth(0);
2480 growItem(item, newLength, GrowthStrategy::BothSidesEqually, neighbourSqueezeStrategy,
2482 d->updateSeparators_recursive();
2485void ItemBoxContainer::updateWidgetGeometries()
2487 for (Item *item :
std::as_const(m_children))
2488 item->updateWidgetGeometries();
2491int ItemBoxContainer::oppositeLength()
const
2493 return isVertical() ? width() : height();
2496void ItemBoxContainer::requestSeparatorMove(LayoutingSeparator *separator,
2499 const auto separatorIndex = d->m_separators.indexOf(separator);
2500 if (separatorIndex == -1) {
2502 KDDW_ERROR(
"Unknown separator {}, this={}", (
void * )separator, (
void * )
this);
2503 root()->dumpLayout();
2510 const int min = minPosForSeparator_global(separator);
2511 const int pos = separator->position();
2512 const int max = maxPosForSeparator_global(separator);
2514 if ((pos + delta < min && delta < 0) ||
2516 (pos + delta > max && delta > 0)) {
2519 root()->dumpLayout();
2520 KDDW_ERROR(
"Separator would have gone out of bounds, separator={}, min={}, pos={}, max={}, deleta={}", (
void * )separator,
2521 min, pos, max, delta);
2525 const Side moveDirection = delta < 0 ? Side1 : Side2;
2526 const Item::List children = visibleChildren();
2527 if (children.size() <= separatorIndex) {
2529 KDDW_ERROR(
"Not enough children for separator index", (
void * )separator, (
void * )
this, separatorIndex);
2530 root()->dumpLayout();
2534 int remainingToTake = std::abs(delta);
2535 int tookLocally = 0;
2537 Item *side1Neighbour = children[separatorIndex];
2538 Item *side2Neighbour = children[separatorIndex + 1];
2540 Side nextSeparatorDirection = moveDirection;
2542 if (moveDirection == Side1) {
2544 const int availableSqueeze1 = availableToSqueezeOnSide(side2Neighbour, Side1);
2545 const int availableGrow2 = availableToGrowOnSide(side1Neighbour, Side2);
2549 tookLocally = std::min(availableSqueeze1, remainingToTake);
2550 tookLocally = std::min(tookLocally, availableGrow2);
2552 if (tookLocally != 0) {
2553 growItem(side2Neighbour, tookLocally, GrowthStrategy::Side1Only,
2555 ChildrenResizeStrategy::Side1SeparatorMove);
2558 if (availableGrow2 == tookLocally)
2559 nextSeparatorDirection = Side2;
2563 const int availableSqueeze2 = availableToSqueezeOnSide(side1Neighbour, Side2);
2564 const int availableGrow1 = availableToGrowOnSide(side2Neighbour, Side1);
2567 tookLocally = std::min(availableSqueeze2, remainingToTake);
2568 tookLocally = std::min(tookLocally, availableGrow1);
2570 if (tookLocally != 0) {
2571 growItem(side1Neighbour, tookLocally, GrowthStrategy::Side2Only,
2573 ChildrenResizeStrategy::Side2SeparatorMove);
2576 if (availableGrow1 == tookLocally)
2577 nextSeparatorDirection = Side1;
2580 remainingToTake -= tookLocally;
2582 if (remainingToTake > 0) {
2586 KDDW_ERROR(
"Not enough space to move separator {}", (
void * )
this);
2588 LayoutingSeparator *nextSeparator =
2589 parentBoxContainer()->d->neighbourSeparator_recursive(
this, nextSeparatorDirection,
2591 if (!nextSeparator) {
2593 KDDW_ERROR(
"nextSeparator is null, report a bug");
2598 const int remainingDelta = moveDirection == Side1 ? -remainingToTake : remainingToTake;
2599 nextSeparator->parentContainer()->requestSeparatorMove(nextSeparator, remainingDelta);
2604void ItemBoxContainer::requestEqualSize(LayoutingSeparator *separator)
2606 const auto separatorIndex = d->m_separators.indexOf(separator);
2607 if (separatorIndex == -1) {
2609 KDDW_ERROR(
"Separator not found {}", (
void * )separator);
2613 const Item::List children = visibleChildren();
2614 Item *side1Item = children.at(separatorIndex);
2615 Item *side2Item = children.at(separatorIndex + 1);
2617 const int length1 = side1Item->length(d->m_orientation);
2618 const int length2 = side2Item->length(d->m_orientation);
2620 if (std::abs(length1 - length2) <= 1) {
2626 if (!(side1Item->m_sizingInfo.isPastMax(d->m_orientation)
2627 || side2Item->m_sizingInfo.isPastMax(d->m_orientation))) {
2632 const int newLength = (length1 + length2) / 2;
2635 if (length1 < newLength) {
2637 delta = newLength - length1;
2638 }
else if (length2 < newLength) {
2640 delta = -(newLength - length2);
2644 const int min = minPosForSeparator_global(separator,
true);
2645 const int max = maxPosForSeparator_global(separator,
true);
2646 const int newPos =
bound(min, separator->position() + delta, max);
2649 delta = newPos - separator->position();
2652 requestSeparatorMove(separator, delta);
2655void ItemBoxContainer::layoutEqually()
2657 SizingInfo::List childSizes = sizes();
2658 if (!childSizes.isEmpty()) {
2659 layoutEqually(childSizes);
2660 applyGeometries(childSizes);
2664void ItemBoxContainer::layoutEqually(SizingInfo::List &sizes)
2666 const auto numItems = sizes.count();
2668 satisfiedIndexes.
reserve(numItems);
2670 int lengthToGive = length() - (d->m_separators.size() * Item::layoutSpacing);
2673 for (SizingInfo &size : sizes) {
2674 size.setLength(0, d->m_orientation);
2677 while (satisfiedIndexes.
count() < sizes.count()) {
2678 const int remainingItems = int(sizes.count() - satisfiedIndexes.
count());
2679 const int suggestedToGive = std::max(1, lengthToGive / remainingItems);
2680 const auto oldLengthToGive = lengthToGive;
2682 for (
int i = 0; i < numItems; ++i) {
2686 SizingInfo &size = sizes[i];
2687 if (size.availableToGrow(d->m_orientation) <= 0) {
2689 satisfiedIndexes.push_back(i);
2697 const auto othersMissing =
2699 std::accumulate(sizes.constBegin(), sizes.constEnd(), 0,
2700 [
this](
size_t sum,
const SizingInfo &sz) {
2701 return int(sum) + sz.missingLength(d->m_orientation);
2703 - size.missingLength(d->m_orientation);
2705 const auto maxLength =
2706 std::min(size.length(d->m_orientation) + lengthToGive - othersMissing,
2707 size.maxLengthHint(d->m_orientation));
2709 const auto newItemLenght =
2710 bound(size.minLength(d->m_orientation),
2711 size.length(d->m_orientation) + suggestedToGive, maxLength);
2712 const auto toGive = newItemLenght - size.length(d->m_orientation);
2716 satisfiedIndexes.push_back(i);
2718 lengthToGive -= toGive;
2719 size.incrementLength(toGive, d->m_orientation);
2720 if (size.availableToGrow(d->m_orientation) <= 0) {
2721 satisfiedIndexes.push_back(i);
2723 if (lengthToGive == 0)
2726 if (lengthToGive < 0) {
2727 KDDW_ERROR(
"Breaking infinite loop");
2733 if (oldLengthToGive == lengthToGive) {
2740void ItemBoxContainer::layoutEqually_recursive()
2743 for (Item *item :
std::as_const(m_children)) {
2744 if (item->isVisible()) {
2745 if (
auto c = item->asBoxContainer())
2746 c->layoutEqually_recursive();
2751Item *ItemBoxContainer::visibleNeighbourFor(
const Item *item, Side side)
const
2754 const auto index = m_children.indexOf(
const_cast<Item *
>(item));
2756 if (side == Side1) {
2757 for (
auto i = index - 1; i >= 0; --i) {
2758 Item *child = m_children.at(i);
2759 if (child->isVisible())
2763 for (
auto i = index + 1; i < m_children.size(); ++i) {
2764 Item *child = m_children.at(i);
2765 if (child->isVisible())
2773Size ItemBoxContainer::availableSize()
const
2775 return size() - this->minSize();
2778int ItemBoxContainer::availableLength()
const
2780 return isVertical() ? availableSize().height() : availableSize().width();
2783LengthOnSide ItemBoxContainer::lengthOnSide(
const SizingInfo::List &sizes,
int fromIndex, Side side,
2789 const auto count = sizes.count();
2790 if (fromIndex >= count)
2795 if (side == Side1) {
2803 LengthOnSide result;
2804 for (
int i = start; i <= end; ++i) {
2805 const SizingInfo &size = sizes.at(i);
2806 result.length += size.length(o);
2807 result.minLength += size.minLength(o);
2813int ItemBoxContainer::neighboursLengthFor(
const Item *item, Side side,
Qt::Orientation o)
const
2815 const Item::List children = visibleChildren();
2816 const auto index = children.indexOf(
const_cast<Item *
>(item));
2818 KDDW_ERROR(
"Couldn't find item {}", (
void * )item);
2822 if (o == d->m_orientation) {
2823 int neighbourLength = 0;
2826 if (side == Side1) {
2831 end = children.size() - 1;
2834 for (
int i = start; i <= end; ++i)
2835 neighbourLength += children.at(i)->length(d->m_orientation);
2837 return neighbourLength;
2844int ItemBoxContainer::neighboursLengthFor_recursive(
const Item *item, Side side,
2847 return neighboursLengthFor(item, side, o)
2848 + (isRoot() ? 0 : parentBoxContainer()->neighboursLengthFor_recursive(
this, side, o));
2851int ItemBoxContainer::neighboursMinLengthFor(
const Item *item, Side side,
Qt::Orientation o)
const
2853 const Item::List children = visibleChildren();
2854 const auto index = children.indexOf(
const_cast<Item *
>(item));
2856 KDDW_ERROR(
"Couldn't find item {}", (
void * )item);
2860 if (o == d->m_orientation) {
2861 int neighbourMinLength = 0;
2864 if (side == Side1) {
2869 end = children.size() - 1;
2872 for (
int i = start; i <= end; ++i)
2873 neighbourMinLength += children.at(i)->minLength(d->m_orientation);
2875 return neighbourMinLength;
2882int ItemBoxContainer::neighboursMaxLengthFor(
const Item *item, Side side,
Qt::Orientation o)
const
2884 const Item::List children = visibleChildren();
2885 const auto index = children.indexOf(
const_cast<Item *
>(item));
2887 KDDW_ERROR(
"Couldn't find item {}", (
void * )item);
2891 if (o == d->m_orientation) {
2892 int neighbourMaxLength = 0;
2895 if (side == Side1) {
2900 end = children.size() - 1;
2903 for (
int i = start; i <= end; ++i)
2904 neighbourMaxLength =
2905 std::min(Core::length(root()->size(), d->m_orientation),
2906 neighbourMaxLength + children.at(i)->maxLengthHint(d->m_orientation));
2908 return neighbourMaxLength;
2915int ItemBoxContainer::availableToSqueezeOnSide(
const Item *child, Side side)
const
2917 const int length = neighboursLengthFor(child, side, d->m_orientation);
2918 const int min = neighboursMinLengthFor(child, side, d->m_orientation);
2920 const int available = length - min;
2921 if (available < 0) {
2922 root()->dumpLayout();
2928int ItemBoxContainer::availableToGrowOnSide(
const Item *child, Side side)
const
2930 const int length = neighboursLengthFor(child, side, d->m_orientation);
2931 const int max = neighboursMaxLengthFor(child, side, d->m_orientation);
2933 return max - length;
2936int ItemBoxContainer::availableToSqueezeOnSide_recursive(
const Item *child, Side side,
2939 if (orientation == d->m_orientation) {
2940 const int available = availableToSqueezeOnSide(child, side);
2944 + parentBoxContainer()->availableToSqueezeOnSide_recursive(
this, side, orientation));
2948 : parentBoxContainer()->availableToSqueezeOnSide_recursive(
this, side, orientation);
2952int ItemBoxContainer::availableToGrowOnSide_recursive(
const Item *child, Side side,
2955 if (orientation == d->m_orientation) {
2956 const int available = availableToGrowOnSide(child, side);
2960 + parentBoxContainer()->availableToGrowOnSide_recursive(
this, side, orientation));
2964 : parentBoxContainer()->availableToGrowOnSide_recursive(
this, side, orientation);
2968void ItemBoxContainer::growNeighbours(Item *side1Neighbour, Item *side2Neighbour)
2970 if (!side1Neighbour && !side2Neighbour)
2973 SizingInfo::List childSizes = sizes();
2975 if (side1Neighbour && side2Neighbour) {
2976 const int index1 = indexOfVisibleChild(side1Neighbour);
2977 const int index2 = indexOfVisibleChild(side2Neighbour);
2979 if (index1 == -1 || index2 == -1 || index1 >= childSizes.count()
2980 || index2 >= childSizes.count()) {
2982 KDDW_ERROR(
"Invalid indexes {} {} {}", index1, index2, childSizes.count());
2987 Rect &geo1 = childSizes[index1].geometry;
2988 Rect &geo2 = childSizes[index2].geometry;
2991 const int available = geo2.y() - geo1.bottom() - layoutSpacing;
2992 geo1.setHeight(geo1.height() + available / 2);
2993 geo2.setTop(geo1.bottom() + layoutSpacing + 1);
2995 const int available = geo2.x() - geo1.right() - layoutSpacing;
2996 geo1.setWidth(geo1.width() + available / 2);
2997 geo2.setLeft(geo1.right() + layoutSpacing + 1);
3000 }
else if (side1Neighbour) {
3001 const int index1 = indexOfVisibleChild(side1Neighbour);
3002 if (index1 == -1 || index1 >= childSizes.count()) {
3004 KDDW_ERROR(
"Invalid indexes {} {}", index1, childSizes.count());
3009 Rect &geo = childSizes[index1].geometry;
3011 geo.setBottom(rect().bottom());
3013 geo.setRight(rect().
right());
3015 }
else if (side2Neighbour) {
3016 const int index2 = indexOfVisibleChild(side2Neighbour);
3017 if (index2 == -1 || index2 >= childSizes.count()) {
3019 KDDW_ERROR(
"Invalid indexes {} {}", index2, childSizes.count());
3024 Rect &geo = childSizes[index2].geometry;
3032 d->honourMaxSizes(childSizes);
3033 positionItems( childSizes);
3034 applyGeometries(childSizes);
3037void ItemBoxContainer::growItem(
int index, SizingInfo::List &sizes,
int missing,
3038 GrowthStrategy growthStrategy,
3040 bool accountForNewSeparator)
3042 int toSteal = missing;
3043 if (accountForNewSeparator)
3044 toSteal += Item::layoutSpacing;
3046 assert(index != -1);
3051 SizingInfo &sizingInfo = sizes[index];
3052 sizingInfo.setOppositeLength(oppositeLength(), d->m_orientation);
3053 const bool isFirst = index == 0;
3054 const bool isLast = index == sizes.count() - 1;
3056 int side1Growth = 0;
3057 int side2Growth = 0;
3059 if (growthStrategy == GrowthStrategy::BothSidesEqually) {
3060 sizingInfo.setLength(sizingInfo.length(d->m_orientation) + missing, d->m_orientation);
3061 const auto count = sizes.count();
3064 sizingInfo.incrementLength(missing, d->m_orientation);
3070 const LengthOnSide side1Length = lengthOnSide(sizes, index - 1, Side1, d->m_orientation);
3071 const LengthOnSide side2Length = lengthOnSide(sizes, index + 1, Side2, d->m_orientation);
3073 int available1 = side1Length.available();
3074 int available2 = side2Length.available();
3076 if (toSteal > available1 + available2) {
3077 root()->dumpLayout();
3081 while (toSteal > 0) {
3082 if (available1 == 0) {
3083 assert(available2 >= toSteal);
3084 side2Growth += toSteal;
3086 }
else if (available2 == 0) {
3087 assert(available1 >= toSteal);
3088 side1Growth += toSteal;
3092 const int toTake = std::max(1, toSteal / 2);
3093 const int took1 = std::min(toTake, available1);
3095 available1 -= took1;
3096 side1Growth += took1;
3100 const int took2 = std::min(toTake, available2);
3102 side2Growth += took2;
3103 available2 -= took2;
3105 shrinkNeighbours(index, sizes, side1Growth, side2Growth, neighbourSqueezeStrategy);
3106 }
else if (growthStrategy == GrowthStrategy::Side1Only) {
3107 side1Growth = std::min(missing, sizingInfo.availableToGrow(d->m_orientation));
3108 sizingInfo.setLength(sizingInfo.length(d->m_orientation) + side1Growth, d->m_orientation);
3109 if (side1Growth > 0)
3110 shrinkNeighbours(index, sizes, side1Growth, 0,
3111 neighbourSqueezeStrategy);
3112 if (side1Growth < missing) {
3113 missing = missing - side1Growth;
3117 KDDW_ERROR(
"No more items to grow");
3119 growItem(index + 1, sizes, missing, growthStrategy, neighbourSqueezeStrategy,
3120 accountForNewSeparator);
3124 }
else if (growthStrategy == GrowthStrategy::Side2Only) {
3125 side2Growth = std::min(missing, sizingInfo.availableToGrow(d->m_orientation));
3126 sizingInfo.setLength(sizingInfo.length(d->m_orientation) + side2Growth, d->m_orientation);
3128 if (side2Growth > 0)
3129 shrinkNeighbours(index, sizes, 0, side2Growth,
3130 neighbourSqueezeStrategy);
3131 if (side2Growth < missing) {
3132 missing = missing - side2Growth;
3136 KDDW_ERROR(
"No more items to grow");
3138 growItem(index - 1, sizes, missing, growthStrategy, neighbourSqueezeStrategy,
3139 accountForNewSeparator);
3145void ItemBoxContainer::growItem(Item *item,
int amount, GrowthStrategy growthStrategy,
3147 bool accountForNewSeparator,
3148 ChildrenResizeStrategy childResizeStrategy)
3150 const Item::List items = visibleChildren();
3151 const auto index = items.indexOf(item);
3152 SizingInfo::List sizes = this->sizes();
3154 growItem(index, sizes, amount, growthStrategy, neighbourSqueezeStrategy,
3155 accountForNewSeparator);
3157 applyGeometries(sizes, childResizeStrategy);
3160void ItemBoxContainer::applyGeometries(
const SizingInfo::List &sizes,
3161 ChildrenResizeStrategy strategy)
3163 const Item::List items = visibleChildren();
3164 const auto count = items.size();
3165 assert(count == sizes.size());
3167 for (
int i = 0; i < count; ++i) {
3168 Item *item = items.at(i);
3169 item->setSize_recursive(sizes[i].geometry.size(), strategy);
3175SizingInfo::List ItemBoxContainer::sizes(
bool ignoreBeingInserted)
const
3177 const Item::List children = visibleChildren(ignoreBeingInserted);
3178 SizingInfo::List result;
3179 result.reserve(children.count());
3180 for (Item *item : children) {
3181 if (item->isContainer()) {
3184 item->m_sizingInfo.minSize = item->minSize();
3185 item->m_sizingInfo.maxSizeHint = item->maxSizeHint();
3187 result.push_back(item->m_sizingInfo);
3194 SizingInfo::List::const_iterator begin,
3195 SizingInfo::List::const_iterator end,
int needed,
3199 for (
auto it = begin; it < end; ++it) {
3200 availabilities.push_back(it->availableLength(d->m_orientation));
3203 const auto count = availabilities.
count();
3206 squeezes.resize(count);
3207 std::fill(squeezes.begin(), squeezes.end(), 0);
3209 int missing = needed;
3212 while (missing > 0) {
3213 const int numDonors = int(std::count_if(availabilities.cbegin(), availabilities.cend(),
3214 [](
int num) { return num > 0; }));
3216 if (numDonors == 0) {
3217 root()->dumpLayout();
3222 int toTake = missing / numDonors;
3226 for (
int i = 0; i < count; ++i) {
3227 const int available = availabilities.
at(i);
3230 const int took = std::min(missing, std::min(toTake, available));
3231 availabilities[i] -= took;
3233 squeezes[i] += took;
3239 for (
int i = 0; i < count; i++) {
3240 const auto index = reversed ? count - 1 - i : i;
3242 const int available = availabilities.
at(index);
3243 if (available > 0) {
3244 const int took = std::min(missing, available);
3246 squeezes[index] += took;
3256 KDDW_ERROR(
"Missing is negative. missing={}, squeezes={}", missing, squeezes);
3262void ItemBoxContainer::shrinkNeighbours(
int index, SizingInfo::List &sizes,
int side1Amount,
3265 assert(side1Amount > 0 || side2Amount > 0);
3266 assert(side1Amount >= 0 && side2Amount >= 0);
3268 if (side1Amount > 0) {
3269 auto begin = sizes.cbegin();
3270 auto end = sizes.cbegin() + index;
3273 calculateSqueezes(begin, end, side1Amount, strategy, reversed);
3274 for (
int i = 0; i < squeezes.
size(); ++i) {
3275 const int squeeze = squeezes.
at(i);
3276 SizingInfo &sizing = sizes[i];
3279 sizing.setSize(
adjustedRect(sizing.geometry, d->m_orientation, 0, -squeeze).size());
3283 if (side2Amount > 0) {
3284 auto begin = sizes.cbegin() + index + 1;
3285 auto end = sizes.cend();
3287 const Vector<int> squeezes = calculateSqueezes(begin, end, side2Amount, strategy);
3288 for (
int i = 0; i < squeezes.
size(); ++i) {
3289 const int squeeze = squeezes.
at(i);
3290 SizingInfo &sizing = sizes[i + index + 1];
3291 sizing.setSize(
adjustedRect(sizing.geometry, d->m_orientation, squeeze, 0).size());
3296Vector<int> ItemBoxContainer::Private::requiredSeparatorPositions()
const
3298 const int numSeparators = std::max(0, q->numVisibleChildren() - 1);
3300 positions.
reserve(numSeparators);
3302 for (Item *item :
std::as_const(q->m_children)) {
3303 if (positions.
size() == numSeparators)
3306 if (item->isVisible()) {
3307 const int localPos = item->m_sizingInfo.edge(m_orientation) + 1;
3308 positions.push_back(q->mapToRoot(localPos, m_orientation));
3315void ItemBoxContainer::Private::updateSeparators()
3320 const Vector<int> positions = requiredSeparatorPositions();
3321 const auto requiredNumSeparators = positions.
size();
3323 const bool numSeparatorsChanged = requiredNumSeparators != m_separators.size();
3324 if (numSeparatorsChanged) {
3327 LayoutingSeparator::List newSeparators;
3328 newSeparators.reserve(requiredNumSeparators);
3330 for (
int position : positions) {
3331 LayoutingSeparator *separator = separatorAt(position);
3334 newSeparators.push_back(separator);
3335 m_separators.removeOne(separator);
3337 separator = s_createSeparatorFunc(q->host(), m_orientation, q);
3338 newSeparators.push_back(separator);
3345 m_separators = newSeparators;
3350 q->isVertical() ? q->mapToRoot(Point(0, 0)).x() : q->mapToRoot(Point(0, 0)).y();
3353 for (
int position : positions) {
3354 m_separators.at(i)->setGeometry(position, pos2, q->oppositeLength());
3359 for (
auto sep :
std::as_const(m_separators))
3362 q->updateChildPercentages();
3365void ItemBoxContainer::Private::deleteSeparators()
3367 for (
const auto &sep :
std::as_const(m_separators))
3369 m_separators.clear();
3372void ItemBoxContainer::Private::deleteSeparators_recursive()
3377 for (Item *item :
std::as_const(q->m_children)) {
3378 if (
auto c = item->asBoxContainer())
3379 c->d->deleteSeparators_recursive();
3383void ItemBoxContainer::Private::updateSeparators_recursive()
3388 const Item::List items = q->visibleChildren();
3389 for (Item *item : items) {
3390 if (
auto c = item->asBoxContainer())
3391 c->d->updateSeparators_recursive();
3395int ItemBoxContainer::Private::excessLength()
const
3398 return std::max(0, Core::length(q->size(), m_orientation) - q->maxLengthHint(m_orientation));
3401void ItemBoxContainer::simplify()
3406 ScopedValueRollback isInSimplify(d->m_isSimplifying,
true);
3408 Item::List newChildren;
3409 newChildren.reserve(m_children.size() + 20);
3411 for (Item *child :
std::as_const(m_children)) {
3412 if (ItemBoxContainer *childContainer = child->asBoxContainer()) {
3413 childContainer->simplify();
3415 if (childContainer->orientation() == d->m_orientation
3416 || childContainer->m_children.size() == 1) {
3419 const auto children = childContainer->childItems();
3420 for (Item *child2 : children) {
3421 child2->setParentContainer(
this);
3422 newChildren.push_back(child2);
3425 delete childContainer;
3427 newChildren.push_back(child);
3430 newChildren.push_back(child);
3434 if (m_children != newChildren) {
3435 m_children = newChildren;
3437 updateChildPercentages();
3441LayoutingSeparator *ItemBoxContainer::Private::separatorAt(
int p)
const
3443 for (
auto separator : m_separators) {
3444 if (separator->position() == p)
3451bool ItemBoxContainer::isVertical()
const
3456bool ItemBoxContainer::isHorizontal()
const
3461int ItemBoxContainer::indexOf(LayoutingSeparator *separator)
const
3463 return d->m_separators.indexOf(separator);
3466bool ItemBoxContainer::isInSimplify()
const
3468 if (d->m_isSimplifying)
3471 auto p = parentBoxContainer();
3472 return p && p->isInSimplify();
3475int ItemBoxContainer::minPosForSeparator(LayoutingSeparator *separator,
3476 bool honourMax)
const
3478 const int globalMin = minPosForSeparator_global(separator, honourMax);
3479 return mapFromRoot(globalMin, d->m_orientation);
3482int ItemBoxContainer::maxPosForSeparator(LayoutingSeparator *separator,
3483 bool honourMax)
const
3485 const int globalMax = maxPosForSeparator_global(separator, honourMax);
3486 return mapFromRoot(globalMax, d->m_orientation);
3489int ItemBoxContainer::minPosForSeparator_global(LayoutingSeparator *separator,
3490 bool honourMax)
const
3492 const int separatorIndex = indexOf(separator);
3493 assert(separatorIndex != -1);
3495 const Item::List children = visibleChildren();
3496 assert(separatorIndex + 1 < children.size());
3497 Item *item2 = children.at(separatorIndex + 1);
3499 const int availableToSqueeze =
3500 availableToSqueezeOnSide_recursive(item2, Side1, d->m_orientation);
3505 Item *item1 = children.at(separatorIndex);
3506 const int availabletoGrow = availableToGrowOnSide_recursive(item1, Side2, d->m_orientation);
3507 return separator->position() - std::min(availabletoGrow, availableToSqueeze);
3510 return separator->position() - availableToSqueeze;
3513int ItemBoxContainer::maxPosForSeparator_global(LayoutingSeparator *separator,
3514 bool honourMax)
const
3516 const int separatorIndex = indexOf(separator);
3517 assert(separatorIndex != -1);
3519 const Item::List children = visibleChildren();
3520 Item *item1 = children.at(separatorIndex);
3522 const int availableToSqueeze =
3523 availableToSqueezeOnSide_recursive(item1, Side2, d->m_orientation);
3528 Item *item2 = children.at(separatorIndex + 1);
3529 const int availabletoGrow = availableToGrowOnSide_recursive(item2, Side1, d->m_orientation);
3530 return separator->position() + std::min(availabletoGrow, availableToSqueeze);
3533 return separator->position() + availableToSqueeze;
3536void ItemBoxContainer::to_json(nlohmann::json &j)
const
3540 j[
"children"] = m_children;
3541 j[
"orientation"] = d->m_orientation;
3544void ItemBoxContainer::fillFromJson(
const nlohmann::json &j,
3545 const std::unordered_map<QString, LayoutingGuest *> &widgets)
3547 if (!j.is_object()) {
3548 KDDW_ERROR(
"Expected a JSON object");
3552 ScopedValueRollback deserializing(d->m_isDeserializing,
true);
3553 Item::fillFromJson(j, widgets);
3557 for (
const auto &child : j.value(
"children", nlohmann::json::array())) {
3558 const bool isContainer = child.value<
bool>(
"isContainer", {});
3560 isContainer ?
new ItemBoxContainer(host(),
this) : new Item(host(), this);
3561 childItem->fillFromJson(child, widgets);
3562 m_children.push_back(childItem);
3566 updateChildPercentages_recursive();
3568 d->updateSeparators_recursive();
3569 d->updateWidgets_recursive();
3572 d->relayoutIfNeeded();
3573 positionItems_recursive();
3575 minSizeChanged.emit(
this);
3576#ifdef DOCKS_DEVELOPER_MODE
3578 KDDW_ERROR(
"Resulting layout is invalid");
3583bool ItemBoxContainer::Private::isDummy()
const
3585 return q->host() ==
nullptr;
3588#ifdef DOCKS_DEVELOPER_MODE
3589void ItemBoxContainer::relayoutIfNeeded()
3591 d->relayoutIfNeeded();
3594bool ItemBoxContainer::test_suggestedRect()
3596 auto itemToDrop =
new Item(host());
3598 const Item::List children = visibleChildren();
3599 for (Item *relativeTo : children) {
3600 if (
auto c = relativeTo->asBoxContainer()) {
3601 c->test_suggestedRect();
3603 std::unordered_map<Location, Rect> rects;
3606 const Rect rect = suggestedDropRect(itemToDrop, relativeTo, loc);
3608 if (rect.isEmpty()) {
3609 KDDW_ERROR(
"Empty rect");
3611 }
else if (!root()->rect().contains(rect)) {
3612 root()->dumpLayout();
3613 KDDW_ERROR(
"Suggested rect is out of bounds rect={}, loc={}, relativeTo={}", rect, loc, (
void * )relativeTo);
3618 auto rectFor = [&rects](
Location loc) -> Rect {
3619 auto it = rects.find(loc);
3620 return it == rects.cend() ? Rect() : it->second;
3625 root()->dumpLayout();
3626 KDDW_ERROR(
"Invalid suggested rects. this={}, relativeTo={}", (
void * )
this, (
void * )relativeTo);
3639 LayoutingSeparator::List separators = d->m_separators;
3641 for (Item *item :
std::as_const(m_children)) {
3642 if (
auto c = item->asBoxContainer())
3643 separators.
append(c->separators_recursive());
3651 return d->m_separators;
3654LayoutingSeparator *ItemBoxContainer::separatorForChild(Item *child, Side side)
const
3656 if (!child || !child->isVisible()) {
3657 KDDW_ERROR(
"ItemBoxContainer::separatorForChild: Unexpected nullptr or invisible child");
3661 const Item::List children = visibleChildren();
3662 const int childIndex = children.indexOf(child);
3663 if (childIndex == -1) {
3664 KDDW_ERROR(
"ItemBoxContainer::separatorForChild: Could not find child");
3668 int separatorIndex = -1;
3670 if (side == Side1) {
3672 if (childIndex == 0)
3675 separatorIndex = childIndex - 1;
3678 if (childIndex == children.size() - 1)
3681 separatorIndex = childIndex;
3684 if (separatorIndex < 0 || separatorIndex >= d->m_separators.size()) {
3685 KDDW_ERROR(
"ItemBoxContainer::separatorForChild: Not enough separators {} {} {}", d->m_separators.size(), children.size(), childIndex);
3689 return d->m_separators.at(separatorIndex);
3692LayoutingSeparator *ItemBoxContainer::adjacentSeparatorForChild(Item *child, Side side)
const
3694 if (!child || !child->isVisible()) {
3695 KDDW_ERROR(
"ItemBoxContainer::adjacentSeparatorForChild: Unexpected nullptr or invisible child");
3699 int separatorIndex = -1;
3702 if (ItemBoxContainer *ancestor = ancestorBoxContainerWithOrientation(
oppositeOrientation(orientation()))) {
3704 const int childIndex = indexInAncestor(ancestor);
3705 const auto children = ancestor->visibleChildren();
3706 const auto separators = ancestor->separators();
3708 if (childIndex == -1) {
3710 KDDW_ERROR(
"ItemBoxContainer::adjacentSeparatorForChild: Could not find index inside ancestor");
3714 if (side == Side1) {
3715 if (childIndex == 0)
3718 separatorIndex = childIndex - 1;
3720 if (childIndex == children.size() - 1)
3723 separatorIndex = childIndex;
3726 if (separatorIndex < 0 || separatorIndex >= separators.size()) {
3727 KDDW_ERROR(
"ItemBoxContainer::adjacentSeparatorForChild: Not enough separators {} {} {}", separators.size(), children.size(), childIndex);
3731 return separators[separatorIndex];
3738bool ItemBoxContainer::isDeserializing()
const
3740 return d->m_isDeserializing;
3743bool ItemBoxContainer::isOverflowing()
const
3748 int contentsLength = 0;
3750 for (Item *item :
std::as_const(m_children)) {
3751 if (item->isVisible()) {
3752 contentsLength += item->length(d->m_orientation);
3757 contentsLength += std::max(0, Item::layoutSpacing * (numVisible - 1));
3758 return contentsLength > length();
3761void ItemBoxContainer::Private::relayoutIfNeeded()
3769 const Size missing = q->missingSize();
3770 if (!missing.isNull())
3771 q->setSize_recursive(q->size() + missing);
3775 for (Item *child :
std::as_const(q->m_children)) {
3776 const int missingLength = ::length(child->missingSize(), m_orientation);
3777 if (!child->isVisible() || missingLength == 0)
3784 if (q->isOverflowing()) {
3785 const Size size = q->size();
3786 q->m_sizingInfo.setSize(size + Size(1, 1));
3787 q->setSize_recursive(size);
3788 q->updateChildPercentages();
3792 for (Item *item :
std::as_const(q->m_children)) {
3793 if (item->isVisible()) {
3794 if (
auto c = item->asBoxContainer())
3795 c->d->relayoutIfNeeded();
3800const Item *ItemBoxContainer::Private::itemFromPath(
const Vector<int> &path)
const
3802 const ItemBoxContainer *container = q;
3804 for (
int i = 0; i < path.
size(); ++i) {
3805 const int index = path[i];
3806 const bool isLast = i == path.
size() - 1;
3807 if (index < 0 || index >= container->m_children.size()) {
3809 q->root()->dumpLayout();
3810 KDDW_ERROR(
"Invalid index {}, this={}, path={}, isRoot={}", index, (
void * )
this, path, q->isRoot());
3815 return container->m_children.at(index);
3817 container = container->m_children.at(index)->asBoxContainer();
3819 KDDW_ERROR(
"Invalid index path={}", path);
3829ItemBoxContainer::Private::neighbourSeparator(
const Item *item, Side side,
3832 Item::List children = q->visibleChildren();
3833 const auto itemIndex = children.indexOf(
const_cast<Item *
>(item));
3834 if (itemIndex == -1) {
3835 KDDW_ERROR(
"Item not found item={}, this={}", (
void * )item, (
void * )
this);
3836 q->root()->dumpLayout();
3840 if (orientation != q->orientation()) {
3845 return q->parentBoxContainer()->d->neighbourSeparator(q, side, orientation);
3849 const auto separatorIndex = side == Side1 ? itemIndex - 1 : itemIndex;
3851 if (separatorIndex < 0 || separatorIndex >= m_separators.size())
3854 return m_separators[separatorIndex];
3858ItemBoxContainer::Private::neighbourSeparator_recursive(
const Item *item, Side side,
3861 LayoutingSeparator *separator = neighbourSeparator(item, side, orientation);
3865 if (!q->parentContainer())
3868 return q->parentBoxContainer()->d->neighbourSeparator_recursive(q, side, orientation);
3871void ItemBoxContainer::Private::updateWidgets_recursive()
3873 for (Item *item :
std::as_const(q->m_children)) {
3874 if (
auto c = item->asBoxContainer()) {
3875 c->d->updateWidgets_recursive();
3877 if (item->isVisible()) {
3878 if (
auto guest = item->guest()) {
3879 guest->setGeometry(q->mapToRoot(item->geometry()));
3880 guest->setVisible(
true);
3882 KDDW_ERROR(
"visible item doesn't have a guest item=", (
void * )item);
3889SizingInfo::SizingInfo()
3890 : minSize(Core::Item::hardcodedMinimumSize)
3891 , maxSizeHint(Core::Item::hardcodedMaximumSize)
3902 return std::max(minLength(o), Core::length(maxSizeHint, o));
3907 return std::max(0, length(o) - minLength(o));
3912 return std::max(0, minLength(o) - length(o));
3917 return std::max(0, length(o) - maxLengthHint(o));
3920void Core::to_json(nlohmann::json &j,
const SizingInfo &info)
3922 j[
"geometry"] = info.geometry;
3923 j[
"minSize"] = info.minSize;
3924 j[
"maxSizeHint"] = info.maxSizeHint;
3925 j[
"percentageWithinParent"] = info.percentageWithinParent;
3928void Core::from_json(
const nlohmann::json &j, SizingInfo &info)
3930 info.geometry = j.value(
"geometry", Rect());
3931 info.minSize = j.value(
"minSize", Size());
3932 info.maxSizeHint = j.value(
"maxSizeHint", Size());
3933 info.percentageWithinParent = j.value<
double>(
"percentageWithinParent", {});
3936void Core::to_json(nlohmann::json &j, Item *item)
3945int ItemBoxContainer::Private::defaultLengthFor(Item *item,
const InitialOption &option)
const
3953 switch (option.sizeMode) {
3957 const int numVisibleChildren =
3958 q->numVisibleChildren() + 1;
3959 const int usableLength =
3960 q->length() - (Item::layoutSpacing * (numVisibleChildren - 1));
3961 result = usableLength / numVisibleChildren;
3966 result = std::min(length, item->length(m_orientation));
3970 result = item->length(m_orientation);
3975 result = std::max(item->minLength(m_orientation), result);
3979struct ItemContainer::Private
3981 explicit Private(ItemContainer *qq)
3989 ItemContainer *
const q;
3992ItemContainer::ItemContainer(LayoutingHost *hostWidget, ItemContainer *parent)
3993 : Item(true, hostWidget, parent)
3994 , d(new Private(this))
3996 xChanged.connect([
this] {
3997 for (Item *item :
std::as_const(m_children)) {
3998 item->xChanged.emit();
4002 yChanged.connect([
this] {
4003 for (Item *item :
std::as_const(m_children)) {
4004 item->yChanged.emit();
4009ItemContainer::ItemContainer(LayoutingHost *hostWidget)
4010 : Item(true, hostWidget, nullptr)
4011 , d(new Private(this))
4015ItemContainer::~ItemContainer()
4020Item::List ItemContainer::childItems()
const
4025int ItemContainer::indexOfChild(
const Item *child)
const
4027 return m_children.indexOf(
const_cast<Item *
>(child));
4030bool ItemContainer::hasChildren()
const
4032 return !m_children.isEmpty();
4035bool ItemContainer::hasVisibleChildren(
bool excludeBeingInserted)
const
4037 for (Item *item :
std::as_const(m_children)) {
4038 if (item->isVisible(excludeBeingInserted))
4045int ItemContainer::numChildren()
const
4047 return m_children.size();
4050int ItemContainer::numVisibleChildren()
const
4053 for (Item *child :
std::as_const(m_children)) {
4054 if (child->isVisible())
4060bool ItemContainer::isEmpty()
const
4062 return m_children.isEmpty();
4065bool ItemContainer::hasSingleVisibleItem()
const
4067 return numVisibleChildren() == 1;
4070bool ItemContainer::contains(
const Item *item)
const
4072 return m_children.contains(
const_cast<Item *
>(item));
4075Item *ItemContainer::itemForView(
const LayoutingGuest *w)
const
4077 for (Item *item :
std::as_const(m_children)) {
4078 if (item->isContainer()) {
4079 if (Item *result = item->asContainer()->itemForView(w))
4081 }
else if (item->guest() == w) {
4089Item::List ItemContainer::visibleChildren(
bool includeBeingInserted)
const
4092 items.reserve(m_children.size());
4093 for (Item *item :
std::as_const(m_children)) {
4094 if (includeBeingInserted) {
4095 if (item->isVisible() || item->isBeingInserted())
4096 items.push_back(item);
4098 if (item->isVisible() && !item->isBeingInserted())
4099 items.push_back(item);
4106Item::List ItemContainer::items_recursive()
const
4110 for (Item *item :
std::as_const(m_children)) {
4111 if (
auto c = item->asContainer()) {
4112 items.append(c->items_recursive());
4114 items.push_back(item);
4121bool ItemContainer::contains_recursive(
const Item *item)
const
4123 for (Item *it :
std::as_const(m_children)) {
4126 }
else if (it->isContainer()) {
4127 if (it->asContainer()->contains_recursive(item))
4136int ItemContainer::visibleCount_recursive()
const
4139 for (Item *item :
std::as_const(m_children)) {
4140 count += item->visibleCount_recursive();
4146int ItemContainer::count_recursive()
const
4149 for (Item *item :
std::as_const(m_children)) {
4150 if (
auto c = item->asContainer()) {
4151 count += c->count_recursive();
4160bool ItemContainer::inSetSize()
const
4162 return std::any_of(m_children.cbegin(), m_children.cend(), [](Item *child) {
4163 return child->inSetSize();
4167LayoutingHost::~LayoutingHost() =
default;
4168LayoutingSeparator::~LayoutingSeparator() =
default;
4170LayoutingSeparator::LayoutingSeparator(LayoutingHost *host,
Qt::Orientation orientation, Core::ItemBoxContainer *container)
4172 , m_orientation(orientation)
4173 , m_parentContainer(container)
4177bool LayoutingSeparator::isVertical()
const
4182int LayoutingSeparator::position()
const
4184 const Point topLeft = geometry().topLeft();
4185 return (isVertical() ? topLeft.y() : topLeft.x()) - offset();
4188ItemBoxContainer *LayoutingSeparator::parentContainer()
const
4190 return m_parentContainer;
4195 return m_orientation;
4199void LayoutingSeparator::setGeometry(
int pos,
int pos2,
int length)
4202 Rect newGeo = geometry();
4205 newGeo.setSize(Size(length, Core::Item::separatorThickness));
4206 newGeo.moveTo(pos2, pos);
4209 newGeo.setSize(Size(Core::Item::separatorThickness, length));
4210 newGeo.moveTo(pos, pos2);
4213 setGeometry(newGeo);
4216void LayoutingSeparator::free()
4221bool LayoutingSeparator::isBeingDragged()
const
4223 return LayoutingSeparator::s_separatorBeingDragged !=
nullptr;
4226void LayoutingSeparator::onMousePress()
4228 LayoutingSeparator::s_separatorBeingDragged =
this;
4231void LayoutingSeparator::onMouseRelease()
4233 LayoutingSeparator::s_separatorBeingDragged =
nullptr;
4236int LayoutingSeparator::onMouseMove(Point pos,
bool moveSeparator)
4238 if (!isBeingDragged())
4241 const int positionToGoTo = Core::pos(pos, m_orientation);
4242 const int minPos = m_parentContainer->minPosForSeparator_global(
this);
4243 const int maxPos = m_parentContainer->maxPosForSeparator_global(
this);
4245 if ((positionToGoTo > maxPos && position() <= positionToGoTo)
4246 || (positionToGoTo < minPos && position() >= positionToGoTo)) {
4258 m_parentContainer->requestSeparatorMove(
this, positionToGoTo - position());
4260 return positionToGoTo;
4263int LayoutingSeparator::offset()
const
4266 const int diff = Item::layoutSpacing - Item::separatorThickness;
4272void LayoutingSeparator::raise()
4278class LayoutingGuest::Private
4281 ObjectGuard<Core::Item> layoutItem;
4284Core::Item *LayoutingGuest::layoutItem()
const
4286 return d->layoutItem;
4289void LayoutingGuest::setLayoutItem(Item *item)
4291 if (d->layoutItem == item)
4295 d->layoutItem->unref();
4300 d->layoutItem = item;
4302 setLayoutItem_impl(item);
4305LayoutingGuest::LayoutingGuest()
4310LayoutingGuest::~LayoutingGuest()
4318void LayoutingHost::insertItem(Core::LayoutingGuest *guest,
Location loc,
4321 if (!guest || !guest->layoutItem()) {
4326 if (
auto box = m_rootItem->asBoxContainer())
4327 box->insertItem(guest->layoutItem(), loc, initialOption);
4333void LayoutingHost::insertItemRelativeTo(Core::LayoutingGuest *guest, Core::LayoutingGuest *relativeTo,
Location loc,
4336 if (!guest || !relativeTo || !guest->layoutItem() || !relativeTo->layoutItem()) {
4341 if (
auto box = m_rootItem->asBoxContainer())
4342 box->insertItemRelativeTo(guest->layoutItem(), relativeTo->layoutItem(), loc, initialOption);
bool locationIsSide1(Location loc)
Qt::Orientation oppositeOrientation(Qt::Orientation o)
#define LAYOUT_DUMP_INDENT
Qt::Orientation orientationForLocation(Location loc)
bool locationIsVertical(Location loc)
Rect adjustedRect(Rect r, Qt::Orientation o, int p1, int p2)
bool isEmpty() const const
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)