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;
49bool Core::Item::s_silenceSanityChecks =
false;
51DumpScreenInfoFunc Core::Item::s_dumpScreenInfoFunc =
nullptr;
52CreateSeparatorFunc Core::Item::s_createSeparatorFunc =
nullptr;
55Size Core::Item::hardcodedMinimumSize = Size(80, 90);
56Size Core::Item::hardcodedMaximumSize = Size(16777215, 16777215);
58bool Core::ItemBoxContainer::s_inhibitSimplify =
false;
59LayoutingSeparator *LayoutingSeparator::s_separatorBeingDragged =
nullptr;
94 r.adjust(0, p1, 0, p2);
96 r.adjust(p1, 0, p2, 0);
108 int available()
const
110 return std::max(0, length - minLength);
115 return std::max(0, minLength - length);
121ItemBoxContainer *Item::root()
const
123 return m_parent ? m_parent->root()
124 :
const_cast<ItemBoxContainer *
>(object_cast<const ItemBoxContainer *>(
this));
127Rect Item::mapToRoot(Rect r)
const
129 const Point topLeft = mapToRoot(r.topLeft());
130 r.moveTopLeft(topLeft);
134Point Item::mapToRoot(Point p)
const
139 return p + parentContainer()->mapToRoot(pos());
145 return mapToRoot(Point(0, p)).y();
146 return mapToRoot(Point(p, 0)).x();
149Point Item::mapFromRoot(Point p)
const
151 const Item *it =
this;
154 it = it->parentContainer();
160Rect Item::mapFromRoot(Rect r)
const
162 const Point topLeft = mapFromRoot(r.topLeft());
163 r.moveTopLeft(topLeft);
167Point Item::mapFromParent(Point p)
const
178 return mapFromRoot(Point(0, p)).y();
179 return mapFromRoot(Point(p, 0)).x();
182void Item::setGuest(LayoutingGuest *guest)
184 assert(!guest || !m_guest);
187 m_parentChangedConnection.disconnect();
188 m_guestDestroyedConnection->disconnect();
189 m_layoutInvalidatedConnection->disconnect();
192 m_guest->setHost(m_host);
193 m_guest->setLayoutItem(
this);
195 m_parentChangedConnection = m_guest->hostChanged.connect([
this](LayoutingHost *host) {
196 if (host != this->host()) {
199 turnIntoPlaceholder();
204 ScopedValueRollback guard(m_isSettingGuest,
true);
205 setMinSize(guest->minSize());
206 setMaxSizeHint(guest->maxSizeHint());
209 m_guestDestroyedConnection =
210 m_guest->beingDestroyed.connect(&Item::onGuestDestroyed,
this);
212 m_layoutInvalidatedConnection =
213 guest->layoutInvalidated.connect(&Item::onWidgetLayoutRequested,
this);
215 if (m_sizingInfo.geometry.isEmpty()) {
217 Rect widgetGeo = m_guest->geometry();
219 widgetGeo.size().expandedTo(minSize()).expandedTo(Item::hardcodedMinimumSize));
220 setGeometry(mapFromRoot(widgetGeo));
222 updateWidgetGeometries();
229void Item::updateWidgetGeometries()
232 m_guest->setGeometry(mapToRoot(rect()));
236void Item::to_json(nlohmann::json &json)
const
238 json[
"sizingInfo"] = m_sizingInfo;
239 json[
"isVisible"] = m_isVisible;
240 json[
"isContainer"] = isContainer();
241 json[
"objectName"] = objectName();
243 json[
"guestId"] = m_guest->id();
246void Item::fillFromJson(
const nlohmann::json &j,
247 const std::unordered_map<QString, LayoutingGuest *> &widgets)
249 m_sizingInfo = j.value(
"sizingInfo", SizingInfo());
250 m_isVisible = j.value(
"isVisible",
false);
251 setObjectName(j.value(
"objectName",
QString()));
254 auto it = widgets.find(guestId);
255 if (it != widgets.cend()) {
256 setGuest(it->second);
257 m_guest->setHost(host());
259 KDDW_ERROR(
"Couldn't find group to restore for item={}", (
void * )
this);
265Item *Item::createFromJson(LayoutingHost *hostWidget, ItemContainer *parent,
const nlohmann::json &json,
266 const std::unordered_map<QString, LayoutingGuest *> &widgets)
268 auto item =
new Item(hostWidget, parent);
269 item->fillFromJson(json, widgets);
273void Item::setDumpScreenInfoFunc(DumpScreenInfoFunc f)
275 s_dumpScreenInfoFunc = f;
278void Item::setCreateSeparatorFunc(CreateSeparatorFunc f)
280 s_createSeparatorFunc = f;
290 assert(m_refCount > 0);
292 if (m_refCount == 0) {
294 parentContainer()->removeItem(
this);
298int Item::refCount()
const
303LayoutingHost *Item::host()
const
308LayoutingGuest *Item::guest()
const
313void Item::restore(LayoutingGuest *guest)
315 if (isVisible() || m_guest) {
316 KDDW_ERROR(
"Hitting assert. visible={}, guest={}", isVisible(), (
void * )
this);
321 KDDW_ERROR(
"Containers can't be restored");
324 parentContainer()->restore(
this);
344 const Item *it =
this;
346 if (
auto p = it->parentContainer()) {
347 const auto index = p->indexOfChild(it);
358void Item::setHost(LayoutingHost *host)
360 if (m_host != host) {
363 m_guest->setHost(host);
364 m_guest->setVisible(
true);
365 updateWidgetGeometries();
370void Item::setSize_recursive(Size newSize, ChildrenResizeStrategy)
375Size Item::missingSize()
const
377 Size missing = minSize() - this->size();
378 missing.setWidth(std::max(missing.width(), 0));
379 missing.setHeight(std::max(missing.height(), 0));
384bool Item::isBeingInserted()
const
386 return m_sizingInfo.isBeingInserted;
389void Item::setBeingInserted(
bool is)
391 m_sizingInfo.isBeingInserted = is;
395 if (
auto parent = parentContainer()) {
397 if (!parent->hasVisibleChildren())
398 parent->setBeingInserted(
true);
400 parent->setBeingInserted(
false);
405void Item::setParentContainer(ItemContainer *parent)
407 if (parent == m_parent)
411 m_minSizeChangedHandle.disconnect();
412 m_visibleChangedHandle.disconnect();
413 visibleChanged.emit(
this,
false);
416 if (
auto c = asContainer()) {
417 const bool ceasingToBeRoot = !m_parent && parent;
418 if (ceasingToBeRoot && !c->hasVisibleChildren()) {
426 connectParent(parent);
431void Item::connectParent(ItemContainer *parent)
434 m_minSizeChangedHandle =
435 minSizeChanged.connect(&ItemContainer::onChildMinSizeChanged, parent);
436 m_visibleChangedHandle =
437 visibleChanged.connect(&ItemContainer::onChildVisibleChanged, parent);
441 setHost(parent->host());
444 updateWidgetGeometries();
447 visibleChanged.emit(
this, isVisible());
451ItemContainer *Item::parentContainer()
const
456ItemBoxContainer *Item::parentBoxContainer()
const
458 return object_cast<ItemBoxContainer *>(m_parent);
461int Item::indexInAncestor(ItemContainer *ancestor)
const
464 while (
auto p = it->parentBoxContainer()) {
467 const auto children = ancestor->visibleChildren();
468 return children.indexOf(
const_cast<Item *
>(it));
476ItemBoxContainer *Item::ancestorBoxContainerWithOrientation(
Qt::Orientation o)
const
478 auto p = parentBoxContainer();
480 if (p->orientation() == o)
482 p = p->parentBoxContainer();
488const ItemContainer *Item::asContainer()
const
490 return object_cast<const ItemContainer *>(
this);
493ItemContainer *Item::asContainer()
495 return object_cast<ItemContainer *>(
this);
498ItemBoxContainer *Item::asBoxContainer()
500 return object_cast<ItemBoxContainer *>(
this);
503void Item::setMinSize(Size sz)
505 if (sz != m_sizingInfo.minSize) {
506 m_sizingInfo.minSize = sz;
507 minSizeChanged.emit(
this);
508 if (!m_isSettingGuest)
509 setSize_recursive(size().expandedTo(sz));
513void Item::setMaxSizeHint(Size sz)
515 if (sz != m_sizingInfo.maxSizeHint) {
516 m_sizingInfo.maxSizeHint = sz;
517 maxSizeChanged.emit(
this);
521Size Item::minSize()
const
523 return m_sizingInfo.minSize;
526Size Item::maxSizeHint()
const
528 return m_sizingInfo.maxSizeHint.boundedTo(hardcodedMaximumSize);
531void Item::setPos(Point pos)
533 Rect geo = m_sizingInfo.geometry;
534 geo.moveTopLeft(pos);
541 setPos({ x(), pos });
543 setPos({ pos, y() });
554 return m_sizingInfo.geometry.x();
559 return m_sizingInfo.geometry.y();
562int Item::width()
const
564 return m_sizingInfo.geometry.width();
567int Item::height()
const
569 return m_sizingInfo.geometry.height();
572Size Item::size()
const
574 return m_sizingInfo.geometry.size();
577void Item::setSize(Size sz)
579 Rect newGeo = m_sizingInfo.geometry;
584void Item::requestResize(
int left,
int top,
int right,
int bottom)
586 if (left == 0 && right == 0 && top == 0 && bottom == 0)
589 ItemBoxContainer *parent = parentBoxContainer();
592 KDDW_ERROR(
"Item::requestResize: Could not find parent container");
597 auto moveSeparators = [](
int side1Delta,
int side2Delta, LayoutingSeparator *separator1, LayoutingSeparator *separator2) {
598 if (side1Delta != 0 && separator1) {
599 const auto ancestor = separator1->parentContainer();
600 const int min = ancestor->minPosForSeparator_global(separator1);
601 const int pos = separator1->position();
602 const int max = ancestor->maxPosForSeparator_global(separator1);
603 int newPos = pos - side1Delta;
604 newPos =
bound(min, newPos, max);
605 const int delta = newPos - pos;
607 ancestor->requestSeparatorMove(separator1, delta);
610 if (side2Delta != 0 && separator2) {
611 const auto ancestor = separator2->parentContainer();
612 const int min = ancestor->minPosForSeparator_global(separator2);
613 const int pos = separator2->position();
614 const int max = ancestor->maxPosForSeparator_global(separator2);
615 int newPos = pos + side2Delta;
616 newPos =
bound(min, newPos, max);
617 const int delta = newPos - pos;
619 ancestor->requestSeparatorMove(separator2, delta);
625 const int side1Delta = parent->isHorizontal() ?
left : top;
626 const int side2Delta = parent->isHorizontal() ?
right : bottom;
627 auto separator1 = parent->separatorForChild(
this, Side1);
628 auto separator2 = parent->separatorForChild(
this, Side2);
629 moveSeparators(side1Delta, side2Delta, separator1, separator2);
634 const int side1Delta = parent->isHorizontal() ? top :
left;
635 const int side2Delta = parent->isHorizontal() ? bottom :
right;
636 auto separator1 = parent->adjacentSeparatorForChild(
this, Side1);
637 auto separator2 = parent->adjacentSeparatorForChild(
this, Side2);
640 KDDW_WARN(
"SEP {} {}", separator2->position(), separator2->orientation());
641 moveSeparators(side1Delta, side2Delta, separator1, separator2);
645Point Item::pos()
const
647 return m_sizingInfo.geometry.topLeft();
650Rect Item::geometry()
const
652 return isBeingInserted() ? Rect() : m_sizingInfo.geometry;
655Rect Item::rect()
const
657 return Rect(0, 0, width(), height());
660bool Item::isContainer()
const
662 return m_isContainer;
667 return Core::length(minSize(), o);
672 return Core::length(maxSizeHint(), o);
679 const int w = std::max(width(), hardcodedMinimumSize.width());
680 setSize(Size(w, length));
682 const int h = std::max(height(), hardcodedMinimumSize.height());
683 setSize(Size(length, h));
689 setLength(length, o);
694 return Core::length(size(), o);
699 return length(o) - minLength(o);
702bool Item::isPlaceholder()
const
707bool Item::isVisible(
bool excludeBeingInserted)
const
709 return m_isVisible && !(excludeBeingInserted && isBeingInserted());
712void Item::setIsVisible(
bool is)
714 if (is != m_isVisible) {
716 visibleChanged.emit(
this, is);
720 m_guest->setGeometry(mapToRoot(rect()));
721 m_guest->setVisible(
true);
727void Item::setGeometry_recursive(Rect rect)
733bool Item::checkSanity()
738 if (minSize().width() > width() || minSize().height() > height()) {
739 root()->dumpLayout();
740 KDDW_ERROR(
"Size constraints not honoured this={}, min={}, size={}", (
void * )
this, minSize(), size());
745 if (m_guest->host() != host()) {
747 root()->dumpLayout();
748 KDDW_ERROR(
"Unexpected host for our guest. m_guest->host()={}, host()={}",
749 (
void * )m_guest->host(), (
void * )host());
754 if (!m_guest->isVisible() && (!m_guest->parent() || m_guest->parentWidget()->isVisible())) {
756 KDDW_ERROR(
"Guest widget isn't visible {}",
this);
760 if (m_guest->geometry() != mapToRoot(rect())) {
761 root()->dumpLayout();
762 KDDW_ERROR(
"Guest widget doesn't have correct geometry. m_guest->guestGeometry={}, item.mapToRoot(rect())={}", m_guest->geometry(), mapToRoot(rect()));
770bool Item::isMDI()
const
772 return object_cast<ItemFreeContainer *>(parentContainer()) !=
nullptr;
775void Item::setGeometry(Rect rect)
777 Rect &m_geometry = m_sizingInfo.geometry;
779 if (rect != m_geometry) {
780 const Rect oldGeo = m_geometry;
784 if (rect.isEmpty()) {
786 ItemContainer *c = asContainer();
788 if (c->hasVisibleChildren()) {
794 KDDW_ERROR(
"Empty rect");
798 const Size minSz = minSize();
799 if (!s_silenceSanityChecks
800 && (rect.width() < minSz.width() || rect.height() < minSz.height())) {
803 KDDW_ERROR(
"Constraints not honoured. this={}, sz={}, min={}, parent={}", (
void * )
this, rect.size(), minSz, (
void * )parentContainer());
806 geometryChanged.emit();
808 if (oldGeo.x() != x())
810 if (oldGeo.y() != y())
812 if (oldGeo.width() != width())
814 if (oldGeo.height() != height())
815 heightChanged.emit();
817 updateWidgetGeometries();
821void Item::dumpLayout(
int level,
bool)
825 std::cerr << indent <<
"- Widget: " << m_sizingInfo.geometry
826 <<
"; min=" << minSize();
828 if (maxSizeHint() != hardcodedMaximumSize)
829 std::cerr <<
"; max=" << maxSizeHint() <<
"; ";
832 std::cerr <<
";hidden;";
834 if (m_guest && geometry() != m_guest->geometry()) {
835 std::cerr <<
"; guest geometry=" << m_guest->geometry();
838 if (m_sizingInfo.isBeingInserted)
839 std::cerr <<
";beingInserted;";
841 std::cerr <<
"; guest=" <<
this <<
"; name=" << objectName().toStdString() <<
"\n";
844Item::Item(LayoutingHost *hostWidget, ItemContainer *parent)
845 : Core::Object(parent)
846 , m_isContainer(false)
850 connectParent(parent);
853Item::Item(
bool isContainer, LayoutingHost *hostWidget, ItemContainer *parent)
854 : Core::Object(parent)
855 , m_isContainer(isContainer)
859 connectParent(parent);
865 aboutToBeDeleted.emit();
867 m_minSizeChangedHandle.disconnect();
868 m_visibleChangedHandle.disconnect();
869 m_parentChangedConnection.disconnect();
874void Item::turnIntoPlaceholder()
876 assert(!isContainer());
881 parentContainer()->removeItem(
this,
false);
884void Item::updateObjectName()
889 if (
auto w = guest()) {
890 setObjectName(w->debugName().isEmpty() ? QStringLiteral(
"widget") : w->debugName());
891 }
else if (!isVisible()) {
892 setObjectName(QStringLiteral(
"hidden"));
893 }
else if (!m_guest) {
894 setObjectName(QStringLiteral(
"null"));
896 setObjectName(QStringLiteral(
"empty"));
900void Item::onGuestDestroyed()
903 m_parentChangedConnection.disconnect();
904 m_guestDestroyedConnection->disconnect();
907 turnIntoPlaceholder();
908 }
else if (!isRoot()) {
909 parentContainer()->removeItem(
this);
913void Item::onWidgetLayoutRequested()
915 if (
auto w = guest()) {
916 const Size guestSize = w->geometry().size();
917 if (guestSize != size() && !isMDI()) {
919 std::cerr <<
"Item::onWidgetLayoutRequested"
920 <<
"TODO: Not implemented yet. Widget can't just decide to resize yet"
921 <<
"View.size=" << guestSize <<
"Item.size=" << size() << m_sizingInfo.geometry
922 << m_sizingInfo.isBeingInserted <<
"\n";
925 if (w->minSize() != minSize()) {
926 setMinSize(m_guest->minSize());
929 setMaxSizeHint(w->maxSizeHint());
933bool Item::isRoot()
const
935 return m_parent ==
nullptr;
938LayoutBorderLocations Item::adjacentLayoutBorders()
const
941 return LayoutBorderLocation_All;
944 ItemBoxContainer *c = parentBoxContainer();
946 return LayoutBorderLocation_None;
948 const int indexInParent = c->indexOfVisibleChild(
this);
949 const int numVisibleChildren = c->numVisibleChildren();
950 const bool isFirst = indexInParent == 0;
951 const bool isLast = indexInParent == numVisibleChildren - 1;
952 if (indexInParent == -1)
953 return LayoutBorderLocation_None;
955 LayoutBorderLocations locations = LayoutBorderLocation_None;
957 if (c->isVertical()) {
958 locations |= LayoutBorderLocation_West;
959 locations |= LayoutBorderLocation_East;
962 locations |= LayoutBorderLocation_North;
964 locations |= LayoutBorderLocation_South;
966 locations |= LayoutBorderLocation_North;
967 locations |= LayoutBorderLocation_South;
970 locations |= LayoutBorderLocation_West;
972 locations |= LayoutBorderLocation_East;
975 const LayoutBorderLocations parentBorders = c->adjacentLayoutBorders();
976 if (c->isVertical()) {
977 if (parentBorders & LayoutBorderLocation_West)
978 locations |= LayoutBorderLocation_West;
980 if (parentBorders & LayoutBorderLocation_East)
981 locations |= LayoutBorderLocation_East;
983 if (isFirst && (parentBorders & LayoutBorderLocation_North))
984 locations |= LayoutBorderLocation_North;
986 if (isLast && (parentBorders & LayoutBorderLocation_South))
987 locations |= LayoutBorderLocation_South;
990 if (parentBorders & LayoutBorderLocation_North)
991 locations |= LayoutBorderLocation_North;
993 if (parentBorders & LayoutBorderLocation_South)
994 locations |= LayoutBorderLocation_South;
996 if (isFirst && (parentBorders & LayoutBorderLocation_West))
997 locations |= LayoutBorderLocation_West;
999 if (isLast && (parentBorders & LayoutBorderLocation_East))
1000 locations |= LayoutBorderLocation_East;
1007int Item::visibleCount_recursive()
const
1009 return isVisible() ? 1 : 0;
1012struct ItemBoxContainer::Private
1014 explicit Private(ItemBoxContainer *qq)
1017 if (!Item::s_createSeparatorFunc) {
1018 KDDW_ERROR(
"Item doesn't know how to create separators! Aborting.\n"
1019 "If you're using the layouting engine outside of KDDW, don't forget"
1020 " to call KDDockWidgets::Core::Item::createSeparatorFunc()");
1027 for (
const auto &sep :
std::as_const(m_separators))
1029 m_separators.clear();
1032 int defaultLengthFor(Item *item,
InitialOption option)
const;
1033 void relayoutIfNeeded();
1034 const Item *itemFromPath(
const Vector<int> &path)
const;
1035 void resizeChildren(Size oldSize, Size newSize, SizingInfo::List &sizes,
1036 ChildrenResizeStrategy);
1037 void honourMaxSizes(SizingInfo::List &sizes);
1038 void scheduleCheckSanity()
const;
1039 LayoutingSeparator *neighbourSeparator(
const Item *item, Side,
1041 LayoutingSeparator *neighbourSeparator_recursive(
const Item *item, Side,
1043 void updateWidgets_recursive();
1047 void updateSeparators();
1048 void deleteSeparators();
1049 LayoutingSeparator *separatorAt(
int p)
const;
1051 bool isDummy()
const;
1052 void deleteSeparators_recursive();
1053 void updateSeparators_recursive();
1054 Size minSize(
const Item::List &items)
const;
1055 int excessLength()
const;
1057 mutable bool m_checkSanityScheduled =
false;
1059 bool m_convertingItemToContainer =
false;
1060 bool m_blockUpdatePercentages =
false;
1061 bool m_isDeserializing =
false;
1062 bool m_isSimplifying =
false;
1064 ItemBoxContainer *
const q;
1067ItemBoxContainer::ItemBoxContainer(LayoutingHost *hostWidget, ItemContainer *parent)
1068 : ItemContainer(hostWidget, parent)
1069 , d(new Private(this))
1074ItemBoxContainer::ItemBoxContainer(LayoutingHost *hostWidget)
1075 : ItemContainer(hostWidget, nullptr)
1076 , d(new Private(this))
1080ItemBoxContainer::~ItemBoxContainer()
1088 if (d->m_orientation == o) {
1090 for (Item *child : m_children) {
1091 if (ItemBoxContainer *container = child->asBoxContainer()) {
1092 num += container->numSideBySide_recursive(o);
1093 }
else if (!child->isPlaceholder()) {
1099 for (Item *child : m_children) {
1100 if (ItemBoxContainer *container = child->asBoxContainer()) {
1101 num = std::max(num, container->numSideBySide_recursive(o));
1102 }
else if (!child->isPlaceholder()) {
1103 num = std::max(num, 1);
1111bool ItemBoxContainer::checkSanity()
1113 d->m_checkSanityScheduled =
false;
1115#ifdef KDDW_FRONTEND_QT
1117 if (!
plat ||
plat->
d->inDestruction()) {
1129 if (!Item::checkSanity())
1132 if (numChildren() == 0 && !isRoot()) {
1133 KDDW_ERROR(
"Container is empty. Should be deleted");
1138 KDDW_ERROR(
"Invalid orientation={}, this={}", d->m_orientation, (
void * )
this);
1143 int expectedPos = 0;
1144 const auto children = childItems();
1145 for (Item *item : children) {
1146 if (!item->isVisible())
1148 const int pos = Core::pos(item->pos(), d->m_orientation);
1149 if (expectedPos != pos) {
1150 root()->dumpLayout();
1151 KDDW_ERROR(
"Unexpected pos={}, expected={}, item={}, isContainer={}", pos, expectedPos, (
void * )item,
1152 item->isContainer());
1156 expectedPos = pos + Core::length(item->size(), d->m_orientation) + separatorThickness;
1160 for (Item *item : children) {
1161 if (item->parentContainer() !=
this) {
1162 KDDW_ERROR(
"Invalid parent container for item={}, is={}, expected={}", (
void * )item, (
void * )item->parentContainer(), (
void * )
this);
1166 if (item->parent() !=
this) {
1167 KDDW_ERROR(
"Invalid Object parent for item={}, is={}, expected={}", (
void * )item, (
void * )item->parent(), (
void * )
this);
1171 if (item->isVisible()) {
1175 root()->dumpLayout();
1176 KDDW_ERROR(
"Invalid size for item {}, Container.length={}, item.length={}", (
void * )item, h1, h2);
1180 if (!rect().contains(item->geometry())) {
1181 root()->dumpLayout();
1182 KDDW_ERROR(
"Item geo is out of bounds. item={}, geo={}, parent.rect={}", (
void * )item, item->geometry(), rect());
1187 if (!item->checkSanity())
1191 const Item::List visibleChildren = this->visibleChildren();
1192 const bool isEmptyRoot = isRoot() && visibleChildren.isEmpty();
1194 auto occupied = std::max(0, Item::separatorThickness * (
int(visibleChildren.size()) - 1));
1195 for (Item *item : visibleChildren) {
1196 occupied += item->length(d->m_orientation);
1199 if (occupied != length()) {
1200 root()->dumpLayout();
1201 KDDW_ERROR(
"Unexpected length. Expected={}, got={}, this={}", occupied, length(), (
void * )
this);
1206 const double totalPercentage = std::accumulate(percentages.begin(), percentages.end(), 0.0);
1207 const double expectedPercentage = visibleChildren.isEmpty() ? 0.0 : 1.0;
1208 if (!
fuzzyCompare(totalPercentage, expectedPercentage)) {
1209 root()->dumpLayout();
1210 KDDW_ERROR(
"Percentages don't add up", totalPercentage, percentages, (
void * )
this);
1211 const_cast<ItemBoxContainer *
>(
this)->d->updateSeparators_recursive();
1212 KDDW_ERROR(
"percentages={}", d->childPercentages());
1217 const auto numVisibleChildren = int(visibleChildren.size());
1218 if (d->m_separators.size() != std::max(0, numVisibleChildren - 1)) {
1219 root()->dumpLayout();
1220 KDDW_ERROR(
"Unexpected number of separators sz={}, numVisibleChildren={}", d->m_separators.size(), numVisibleChildren);
1224 const Size expectedSeparatorSize = isVertical() ? Size(width(), Item::separatorThickness)
1225 : Size(Item::separatorThickness, height());
1227 const int pos2 = Core::pos(mapToRoot(Point(0, 0)),
oppositeOrientation(d->m_orientation));
1229 for (
int i = 0; i < d->m_separators.size(); ++i) {
1230 LayoutingSeparator *separator = d->m_separators.at(i);
1231 Item *item = visibleChildren.at(i);
1232 const int expectedSeparatorPos =
1233 mapToRoot(item->m_sizingInfo.edge(d->m_orientation) + 1, d->m_orientation);
1235 if (separator->m_host != host()) {
1236 KDDW_ERROR(
"Invalid host widget for separator this={}", (
void * )
this);
1240 if (separator->parentContainer() !=
this) {
1241 KDDW_ERROR(
"Invalid parent container for separator parent={}, separator={}, this={}", (
void * )separator->parentContainer(), (
void * )separator, (
void * )
this);
1245 if (separator->position() != expectedSeparatorPos) {
1246 root()->dumpLayout();
1247 KDDW_ERROR(
"Unexpected separator position, expected={}, separator={}, this={}", separator->position(), expectedSeparatorPos, (
void * )separator, (
void * )
this);
1250 const Rect separatorGeometry = separator->geometry();
1251 if (separatorGeometry.size() != expectedSeparatorSize) {
1252 KDDW_ERROR(
"Unexpected separator size={}, expected={}, separator={}, this={}", separatorGeometry.size(), expectedSeparatorSize, (
void * )separator, (
void * )
this);
1256 const int separatorPos2 = Core::pos(separatorGeometry.topLeft(),
1258 if (Core::pos(separatorGeometry.topLeft(),
1261 root()->dumpLayout();
1262 KDDW_ERROR(
"Unexpected position pos2={}, expected={}, separator={}, this={}", separatorPos2, pos2, (
void * )separator, (
void * )
this);
1268 const int separatorMinPos = minPosForSeparator_global(separator,
false);
1269 const int separatorMaxPos = maxPosForSeparator_global(separator,
false);
1270 const int separatorPos = separator->position();
1271 if (separatorPos < separatorMinPos || separatorPos > separatorMaxPos || separatorMinPos < 0
1272 || separatorMaxPos <= 0) {
1273 root()->dumpLayout();
1274 KDDW_ERROR(
"Invalid bounds for separator, pos={}, min={}, max={}, separator={}", separatorPos, separatorMinPos, separatorMaxPos, (
void * )separator);
1279#ifdef DOCKS_DEVELOPER_MODE
1282 if (!asBoxContainer()->test_suggestedRect())
1290void ItemBoxContainer::Private::scheduleCheckSanity()
const
1292#ifdef KDDW_FRONTEND_QT
1293 if (!m_checkSanityScheduled) {
1294 m_checkSanityScheduled =
true;
1300bool ItemBoxContainer::hasOrientation()
const
1302 return isVertical() || isHorizontal();
1305int ItemBoxContainer::indexOfVisibleChild(
const Item *item)
const
1307 const Item::List items = visibleChildren();
1308 return items.indexOf(
const_cast<Item *
>(item));
1311void ItemBoxContainer::restore(Item *child)
1313 restoreChild(child, NeighbourSqueezeStrategy::ImmediateNeighboursFirst);
1316void ItemBoxContainer::removeItem(Item *item,
bool hardRemove)
1318 assert(!item->isRoot());
1320 if (!contains(item)) {
1321 if (item->parentContainer() ==
this) {
1323 KDDW_ERROR(
"ItemBoxContainer::removeItem: Could not find item as children, but it has us as parent!");
1329 item->parentContainer()->removeItem(item, hardRemove);
1333 Item *side1Item = visibleNeighbourFor(item, Side1);
1334 Item *side2Item = visibleNeighbourFor(item, Side2);
1336 const bool isContainer = item->isContainer();
1337 const bool wasVisible = !isContainer && item->isVisible();
1340 m_children.removeOne(item);
1343 root()->numItemsChanged.emit();
1345 item->setIsVisible(
false);
1346 item->setGuest(
nullptr);
1348 if (!wasVisible && !isContainer) {
1355 root()->numVisibleItemsChanged.emit(root()->numVisibleChildren());
1360 if (
auto p = parentContainer())
1361 p->removeItem(
this,
true);
1362 }
else if (!hasVisibleChildren()) {
1363 if (
auto p = parentContainer()) {
1364 p->removeItem(
this,
false);
1365 setGeometry(Rect());
1369 growNeighbours(side1Item, side2Item);
1370 itemsChanged.emit();
1372 updateSizeConstraints();
1373 d->updateSeparators_recursive();
1377void ItemBoxContainer::setGeometry_recursive(Rect rect)
1379 setPos(rect.topLeft());
1382 setSize_recursive(rect.size());
1385ItemBoxContainer *ItemBoxContainer::convertChildToContainer(Item *leaf)
1387 ScopedValueRollback converting(d->m_convertingItemToContainer,
true);
1389 const auto index = m_children.indexOf(leaf);
1390 assert(index != -1);
1391 auto container =
new ItemBoxContainer(host(),
this);
1392 container->setParentContainer(
nullptr);
1393 container->setParentContainer(
this);
1395 insertItem(container, index, DefaultSizeMode::NoDefaultSizeMode);
1396 m_children.removeOne(leaf);
1397 container->setGeometry(leaf->geometry());
1398 container->insertItem(leaf,
Location_OnTop, DefaultSizeMode::NoDefaultSizeMode);
1399 itemsChanged.emit();
1400 d->updateSeparators_recursive();
1407void ItemBoxContainer::insertItemRelativeTo(Item *item, Item *relativeTo,
Location loc,
1410 assert(item != relativeTo);
1412 if (
auto asContainer = relativeTo->asBoxContainer()) {
1413 asContainer->insertItem(item, loc, option);
1418 assert(!(option.
startsHidden() && item->isContainer()));
1420 ItemBoxContainer *parent = relativeTo->parentBoxContainer();
1422 KDDW_ERROR(
"This method should only be called for box containers parent={}", (
void * )item->parent());
1426 if (parent->hasOrientationFor(loc)) {
1428 auto indexInParent = parent->indexOfChild(relativeTo);
1433 if (orientation != parent->orientation()) {
1434 assert(parent->visibleChildren().size() == 1);
1437 parent->setOrientation(orientation);
1440 parent->insertItem(item, indexInParent, option);
1442 ItemBoxContainer *container = parent->convertChildToContainer(relativeTo);
1443 container->insertItem(item, loc, option);
1447void ItemBoxContainer::insertItem(Item *item,
Location loc,
1450 assert(item !=
this);
1451 if (contains(item)) {
1452 KDDW_ERROR(
"Item already exists");
1457 assert(!(initialOption.
startsHidden() && item->isContainer()));
1461 if (hasOrientationFor(loc)) {
1462 if (m_children.size() == 1) {
1464 d->m_orientation = locOrientation;
1468 insertItem(item, index, initialOption);
1472 auto container =
new ItemBoxContainer(host(),
this);
1473 container->setGeometry(rect());
1474 container->setChildren(m_children, d->m_orientation);
1477 insertItem(container, 0, DefaultSizeMode::NoDefaultSizeMode);
1480 insertItem(item, loc, initialOption);
1482 if (!container->hasVisibleChildren())
1483 container->setGeometry(Rect());
1486 d->updateSeparators_recursive();
1487 d->scheduleCheckSanity();
1490void ItemBoxContainer::onChildMinSizeChanged(Item *child)
1492 if (d->m_convertingItemToContainer || d->m_isDeserializing || !child->isVisible()) {
1497 updateSizeConstraints();
1499 if (child->isBeingInserted())
1502 if (numVisibleChildren() == 1 && child->isVisible()) {
1504 child->setGeometry(rect());
1505 updateChildPercentages();
1509 const Size missingForChild = child->missingSize();
1510 if (!missingForChild.isNull()) {
1513 growItem(child, Core::length(missingForChild, d->m_orientation),
1514 GrowthStrategy::BothSidesEqually, NeighbourSqueezeStrategy::AllNeighbours);
1517 updateChildPercentages();
1520void ItemBoxContainer::updateSizeConstraints()
1522 const Size missingSize = this->missingSize();
1523 if (!missingSize.isNull()) {
1526 setSize_recursive(size() + missingSize);
1531 minSizeChanged.emit(
this);
1534void ItemBoxContainer::onChildVisibleChanged(Item *,
bool visible)
1536 if (d->m_isDeserializing || isInSimplify())
1539 const int numVisible = numVisibleChildren();
1540 if (visible && numVisible == 1) {
1543 visibleChanged.emit(
this,
true);
1544 }
else if (!visible && numVisible == 0) {
1545 visibleChanged.emit(
this,
false);
1549Rect ItemBoxContainer::suggestedDropRect(
const Item *item,
const Item *relativeTo,
1559 if (relativeTo && !relativeTo->parentContainer()) {
1560 KDDW_ERROR(
"No parent container");
1564 if (relativeTo && relativeTo->parentContainer() !=
this) {
1565 KDDW_ERROR(
"Called on the wrong container");
1569 if (relativeTo && !relativeTo->isVisible()) {
1570 KDDW_ERROR(
"relative to isn't visible");
1575 KDDW_ERROR(
"Invalid location");
1579 const Size availableSize = root()->availableSize();
1580 const Size minSize = item->minSize();
1581 const bool isEmpty = !root()->hasVisibleChildren();
1582 const int extraWidth = (isEmpty ||
locationIsVertical(loc)) ? 0 : Item::separatorThickness;
1583 const int extraHeight = (isEmpty || !
locationIsVertical(loc)) ? 0 : Item::separatorThickness;
1584 const bool windowNeedsGrowing = availableSize.width() < minSize.width() + extraWidth
1585 || availableSize.height() < minSize.height() + extraHeight;
1587 if (windowNeedsGrowing)
1588 return suggestedDropRectFallback(item, relativeTo, loc);
1590 nlohmann::json rootSerialized;
1591 root()->to_json(rootSerialized);
1593 ItemBoxContainer rootCopy(
nullptr);
1594 rootCopy.fillFromJson(rootSerialized, {});
1597 relativeTo = rootCopy.d->itemFromPath(relativeTo->pathFromRoot());
1599 nlohmann::json itemSerialized;
1600 item->to_json(itemSerialized);
1601 auto itemCopy =
new Item(
nullptr);
1602 itemCopy->fillFromJson(itemSerialized, {});
1605 auto r =
const_cast<Item *
>(relativeTo);
1606 ItemBoxContainer::insertItemRelativeTo(itemCopy, r, loc, DefaultSizeMode::FairButFloor);
1608 rootCopy.insertItem(itemCopy, loc, DefaultSizeMode::FairButFloor);
1611 if (rootCopy.size() != root()->size()) {
1613 KDDW_ERROR(
"The root copy grew ?! copy={}, sz={}, loc={}", rootCopy.size(), root()->size(), loc);
1614 return suggestedDropRectFallback(item, relativeTo, loc);
1617 return itemCopy->mapToRoot(itemCopy->rect());
1621Rect ItemBoxContainer::suggestedDropRectFallback(
const Item *item,
const Item *relativeTo,
1624 const Size minSize = item->minSize();
1625 const int itemMin = Core::length(minSize, d->m_orientation);
1626 const int available = availableLength() - Item::separatorThickness;
1628 int suggestedPos = 0;
1629 const Rect relativeToGeo = relativeTo->geometry();
1633 suggestedPos = relativeToGeo.x();
1636 suggestedPos = relativeToGeo.y();
1639 suggestedPos = relativeToGeo.right() - suggestedLength + 1;
1642 suggestedPos = relativeToGeo.bottom() - suggestedLength + 1;
1650 rect.setTopLeft(Point(relativeTo->x(), suggestedPos));
1651 rect.setSize(Size(relativeTo->width(), suggestedLength));
1653 rect.setTopLeft(Point(suggestedPos, relativeTo->y()));
1654 rect.setSize(Size(suggestedLength, relativeTo->height()));
1657 return mapToRoot(rect);
1658 }
else if (isRoot()) {
1660 Rect rect = this->rect();
1661 const int oneThird = length() / 3;
1662 const int suggestedLength = std::max(std::min(available, oneThird), itemMin);
1666 rect.setWidth(suggestedLength);
1669 rect.setHeight(suggestedLength);
1672 rect.adjust(rect.width() - suggestedLength, 0, 0, 0);
1675 rect.adjust(0, rect.bottom() - suggestedLength, 0, 0);
1684 KDDW_ERROR(
"Shouldn't happen");
1690void ItemBoxContainer::positionItems()
1692 SizingInfo::List sizes = this->sizes();
1693 positionItems(sizes);
1694 applyPositions(sizes);
1696 d->updateSeparators_recursive();
1699void ItemBoxContainer::positionItems_recursive()
1702 for (Item *item :
std::as_const(m_children)) {
1703 if (item->isVisible()) {
1704 if (
auto c = item->asBoxContainer())
1705 c->positionItems_recursive();
1710void ItemBoxContainer::applyPositions(
const SizingInfo::List &sizes)
1712 const Item::List items = visibleChildren();
1713 const auto count = items.size();
1714 assert(count == sizes.size());
1715 for (
int i = 0; i < count; ++i) {
1716 Item *item = items.at(i);
1717 const SizingInfo &sizing = sizes[i];
1718 if (sizing.isBeingInserted) {
1727 item->setPos(sizing.geometry.topLeft());
1733 return d->m_orientation;
1736void ItemBoxContainer::positionItems(SizingInfo::List &sizes)
1739 const auto count = sizes.count();
1741 for (
auto i = 0; i < count; ++i) {
1742 SizingInfo &sizing = sizes[i];
1743 if (sizing.isBeingInserted) {
1744 nextPos += Item::separatorThickness;
1754 sizing.setPos(nextPos, d->m_orientation);
1755 nextPos += sizing.length(d->m_orientation) + Item::separatorThickness;
1759void ItemBoxContainer::clear()
1761 for (Item *item :
std::as_const(m_children)) {
1762 if (ItemBoxContainer *container = item->asBoxContainer())
1768 d->deleteSeparators();
1771Item *ItemBoxContainer::itemAt(Point p)
const
1773 for (Item *item :
std::as_const(m_children)) {
1774 if (item->isVisible() && item->geometry().contains(p))
1781Item *ItemBoxContainer::itemAt_recursive(Point p)
const
1783 if (Item *item = itemAt(p)) {
1784 if (
auto c = item->asBoxContainer()) {
1785 return c->itemAt_recursive(c->mapFromParent(p));
1794void ItemBoxContainer::setHost(LayoutingHost *host)
1796 Item::setHost(host);
1797 d->deleteSeparators_recursive();
1798 for (Item *item :
std::as_const(m_children)) {
1799 item->setHost(host);
1802 d->updateSeparators_recursive();
1805void ItemBoxContainer::setIsVisible(
bool)
1810bool ItemBoxContainer::isVisible(
bool excludeBeingInserted)
const
1812 return hasVisibleChildren(excludeBeingInserted);
1815void ItemBoxContainer::setLength_recursive(
int length,
Qt::Orientation o)
1819 sz.setHeight(length);
1821 sz.setWidth(length);
1824 setSize_recursive(sz);
1827void ItemBoxContainer::insertItem(Item *item,
int index,
InitialOption option)
1829 if (option.sizeMode != DefaultSizeMode::NoDefaultSizeMode) {
1831 const int suggestedLength = d->defaultLengthFor(item, option);
1832 item->setLength_recursive(suggestedLength, d->m_orientation);
1835 m_children.insert(index, item);
1836 item->setParentContainer(
this);
1838 itemsChanged.emit();
1840 if (!d->m_convertingItemToContainer && item->isVisible())
1843 const bool shouldEmitVisibleChanged = item->isVisible();
1845 if (!d->m_convertingItemToContainer && !s_inhibitSimplify)
1848 if (shouldEmitVisibleChanged)
1849 root()->numVisibleItemsChanged.emit(root()->numVisibleChildren());
1850 root()->numItemsChanged.emit();
1853bool ItemBoxContainer::hasOrientationFor(
Location loc)
const
1855 if (m_children.size() <= 1)
1861int ItemBoxContainer::usableLength()
const
1863 const Item::List children = visibleChildren();
1864 const auto numVisibleChildren = children.size();
1866 if (children.size() <= 1)
1867 return Core::length(size(), d->m_orientation);
1869 const int separatorWaste = separatorThickness * (numVisibleChildren - 1);
1870 return length() - separatorWaste;
1873void ItemBoxContainer::setChildren(
const List &children,
Qt::Orientation o)
1875 m_children = children;
1876 for (Item *item : children)
1877 item->setParentContainer(this);
1884 if (o != d->m_orientation) {
1885 d->m_orientation = o;
1886 d->updateSeparators_recursive();
1890Size ItemBoxContainer::Private::minSize(
const Item::List &items)
const
1895 if (!q->m_children.isEmpty()) {
1896 for (Item *item : items) {
1897 if (!(item->isVisible() || item->isBeingInserted()))
1900 if (q->isVertical()) {
1901 minW = std::max(minW, item->minSize().width());
1902 minH += item->minSize().height();
1904 minH = std::max(minH, item->minSize().height());
1905 minW += item->minSize().width();
1909 const int separatorWaste = std::max(0, (numVisible - 1) * separatorThickness);
1910 if (q->isVertical())
1911 minH += separatorWaste;
1913 minW += separatorWaste;
1916 return Size(minW, minH);
1919Size ItemBoxContainer::minSize()
const
1921 return d->minSize(m_children);
1924Size ItemBoxContainer::maxSizeHint()
const
1926 int maxW = isVertical() ? hardcodedMaximumSize.width() : 0;
1927 int maxH = isVertical() ? 0 : hardcodedMaximumSize.height();
1929 const Item::List visibleChildren = this->visibleChildren(
false);
1930 if (!visibleChildren.isEmpty()) {
1931 for (Item *item : visibleChildren) {
1932 if (item->isBeingInserted())
1934 const Size itemMaxSz = item->maxSizeHint();
1935 const int itemMaxWidth = itemMaxSz.width();
1936 const int itemMaxHeight = itemMaxSz.height();
1938 maxW = std::min(maxW, itemMaxWidth);
1939 maxH = std::min(maxH + itemMaxHeight, hardcodedMaximumSize.height());
1941 maxH = std::min(maxH, itemMaxHeight);
1942 maxW = std::min(maxW + itemMaxWidth, hardcodedMaximumSize.width());
1946 const auto separatorWaste = (int(visibleChildren.size()) - 1) * separatorThickness;
1948 maxH = std::min(maxH + separatorWaste, hardcodedMaximumSize.height());
1950 maxW = std::min(maxW + separatorWaste, hardcodedMaximumSize.width());
1955 maxW = hardcodedMaximumSize.width();
1958 maxH = hardcodedMaximumSize.height();
1960 return Size(maxW, maxH).expandedTo(d->minSize(visibleChildren));
1963void ItemBoxContainer::Private::resizeChildren(Size oldSize, Size newSize,
1964 SizingInfo::List &childSizes,
1965 ChildrenResizeStrategy strategy)
1972 const Vector<double> childPercentages = this->childPercentages();
1973 const auto count = childSizes.count();
1974 const bool widthChanged = oldSize.width() != newSize.width();
1975 const bool heightChanged = oldSize.height() != newSize.height();
1976 const bool lengthChanged =
1977 (q->isVertical() && heightChanged) || (q->isHorizontal() && widthChanged);
1978 const int totalNewLength = q->usableLength();
1980 if (strategy == ChildrenResizeStrategy::Percentage) {
1985 int remaining = totalNewLength;
1986 for (
int i = 0; i < count; ++i) {
1987 const bool isLast = i == count - 1;
1989 SizingInfo &itemSize = childSizes[i];
1991 const double childPercentage = childPercentages.
at(i);
1992 const int newItemLength = lengthChanged
1993 ? (isLast ? remaining : int(childPercentage * totalNewLength))
1994 : itemSize.length(m_orientation);
1996 if (newItemLength <= 0) {
1997 q->root()->dumpLayout();
1998 KDDW_ERROR(
"Invalid resize newItemLength={}", newItemLength);
2003 remaining = remaining - newItemLength;
2005 if (q->isVertical()) {
2006 itemSize.geometry.setSize({ q->width(), newItemLength });
2008 itemSize.geometry.setSize({ newItemLength, q->height() });
2011 }
else if (strategy == ChildrenResizeStrategy::Side1SeparatorMove
2012 || strategy == ChildrenResizeStrategy::Side2SeparatorMove) {
2013 int remaining = Core::length(
2017 const bool isGrowing = remaining > 0;
2018 remaining = std::abs(remaining);
2026 const bool isSide1SeparatorMove = strategy == ChildrenResizeStrategy::Side1SeparatorMove;
2027 bool resizeHeadFirst =
false;
2028 if (isGrowing && isSide1SeparatorMove) {
2029 resizeHeadFirst =
true;
2030 }
else if (isGrowing && !isSide1SeparatorMove) {
2031 resizeHeadFirst =
false;
2032 }
else if (!isGrowing && isSide1SeparatorMove) {
2033 resizeHeadFirst =
false;
2034 }
else if (!isGrowing && !isSide1SeparatorMove) {
2035 resizeHeadFirst =
true;
2038 for (
int i = 0; i < count; i++) {
2039 const auto index = resizeHeadFirst ? i : count - 1 - i;
2041 SizingInfo &size = childSizes[index];
2045 size.incrementLength(remaining, m_orientation);
2048 const int availableToGive = size.availableLength(m_orientation);
2049 const int took = std::min(availableToGive, remaining);
2050 size.incrementLength(-took, m_orientation);
2058 honourMaxSizes(childSizes);
2061void ItemBoxContainer::Private::honourMaxSizes(SizingInfo::List &sizes)
2066 int amountNeededToShrink = 0;
2067 int amountAvailableToGrow = 0;
2071 for (
int i = 0; i < sizes.count(); ++i) {
2072 SizingInfo &info = sizes[i];
2073 const int neededToShrink = info.neededToShrink(m_orientation);
2074 const int availableToGrow = info.availableToGrow(m_orientation);
2076 if (neededToShrink > 0) {
2077 amountNeededToShrink += neededToShrink;
2078 indexesOfShrinkers.push_back(i);
2079 }
else if (availableToGrow > 0) {
2080 amountAvailableToGrow = std::min(amountAvailableToGrow + availableToGrow, q->length());
2081 indexesOfGrowers.push_back(i);
2086 amountAvailableToGrow = std::min(amountNeededToShrink, amountAvailableToGrow);
2089 amountNeededToShrink = std::min(amountAvailableToGrow, amountNeededToShrink);
2091 if (amountNeededToShrink == 0 || amountAvailableToGrow == 0)
2098 while (amountAvailableToGrow > 0) {
2100 auto toGrow = std::max(1, amountAvailableToGrow /
int(indexesOfGrowers.
size()));
2102 for (
auto it = indexesOfGrowers.begin(); it != indexesOfGrowers.end();) {
2103 const int index = *it;
2104 SizingInfo &sizing = sizes[index];
2105 const auto grew = std::min(sizing.availableToGrow(m_orientation), toGrow);
2106 sizing.incrementLength(grew, m_orientation);
2107 amountAvailableToGrow -= grew;
2109 if (amountAvailableToGrow == 0) {
2114 if (sizing.availableToGrow(m_orientation) == 0) {
2116 it = indexesOfGrowers.erase(it);
2124 while (amountNeededToShrink > 0) {
2126 auto toShrink = std::max(1, amountNeededToShrink /
int(indexesOfShrinkers.
size()));
2128 for (
auto it = indexesOfShrinkers.begin(); it != indexesOfShrinkers.end();) {
2129 const int index = *it;
2130 SizingInfo &sizing = sizes[index];
2131 const auto shrunk = std::min(sizing.neededToShrink(m_orientation), toShrink);
2132 sizing.incrementLength(-shrunk, m_orientation);
2133 amountNeededToShrink -= shrunk;
2135 if (amountNeededToShrink == 0) {
2140 if (sizing.neededToShrink(m_orientation) == 0) {
2142 it = indexesOfShrinkers.erase(it);
2150bool ItemBoxContainer::hostSupportsHonouringLayoutMinSize()
const
2157 return m_host->supportsHonouringLayoutMinSize();
2160void ItemBoxContainer::setSize_recursive(Size newSize, ChildrenResizeStrategy strategy)
2162 ScopedValueRollback block(d->m_blockUpdatePercentages,
true);
2164 const Size minSize = this->minSize();
2165 if (newSize.width() < minSize.width() || newSize.height() < minSize.height()) {
2166 if (!s_silenceSanityChecks && hostSupportsHonouringLayoutMinSize()) {
2167 root()->dumpLayout();
2168 KDDW_ERROR(
"New size doesn't respect size constraints new={}, min={}, this={}", newSize, minSize, (
void * )
this);
2173 if (newSize == size())
2176 const Size oldSize = size();
2179 const Item::List children = visibleChildren();
2180 const auto count = children.size();
2181 SizingInfo::List childSizes = sizes();
2191 d->resizeChildren(oldSize, newSize, childSizes, strategy);
2194 positionItems( childSizes);
2197 for (
int i = 0; i < count; ++i) {
2198 SizingInfo &size = childSizes[i];
2199 const int missing = size.missingLength(d->m_orientation);
2201 growItem(i, childSizes, missing, GrowthStrategy::BothSidesEqually,
2202 NeighbourSqueezeStrategy::AllNeighbours);
2206 applyGeometries(childSizes, strategy);
2209int ItemBoxContainer::length()
const
2211 return isVertical() ? height() : width();
2214void ItemBoxContainer::dumpLayout(
int level,
bool printSeparators)
2216 if (level == 0 && host() && s_dumpScreenInfoFunc)
2217 s_dumpScreenInfoFunc();
2220 const std::string beingInserted =
2221 m_sizingInfo.isBeingInserted ?
"; beingInserted;" :
"";
2222 const std::string visible = !isVisible() ?
";hidden;" :
"";
2223 const bool isOverflow = isOverflowing();
2224 const Size missingSize_ = missingSize();
2225 const std::string isOverflowStr = isOverflow ?
"; overflowing ;" :
"";
2226 const std::string missingSizeStr = missingSize_.isNull() ?
"" : (std::string(
"; missingSize=") + std::to_string(missingSize_.width()) +
"x" + std::to_string(missingSize_.height()));
2228 const std::string typeStr = isRoot() ?
"- Root: " :
"- Layout: ";
2231 std::cerr << indent << typeStr <<
"; isVertical=" << (d->m_orientation ==
Qt::Vertical) <<
"; "
2232 << m_sizingInfo.geometry
2234 <<
"; min=" << minSize() <<
"; this=" <<
this << beingInserted << visible
2235 <<
"; %=" << d->childPercentages();
2237 if (maxSizeHint() != Item::hardcodedMaximumSize)
2238 std::cerr <<
"; max=" << maxSizeHint();
2240 std::cerr << missingSizeStr << isOverflowStr <<
"\n";
2244 for (Item *item :
std::as_const(m_children)) {
2245 item->dumpLayout(level + 1, printSeparators);
2246 if (printSeparators && item->isVisible()) {
2247 if (i < d->m_separators.size()) {
2248 auto separator = d->m_separators.at(i);
2249 std::cerr << std::string(
LAYOUT_DUMP_INDENT *
size_t(level + 1),
' ') <<
"- Separator: "
2250 <<
"local.geo=" << mapFromRoot(separator->geometry())
2251 <<
" ; global.geo=" << separator->geometry() <<
"; separator=" << separator <<
"\n";
2258void ItemBoxContainer::updateChildPercentages()
2260 if (root()->d->m_blockUpdatePercentages)
2263 const int usable = usableLength();
2264 for (Item *item :
std::as_const(m_children)) {
2265 if (item->isVisible() && !item->isBeingInserted()) {
2266 item->m_sizingInfo.percentageWithinParent =
2267 (1.0 * item->length(d->m_orientation)) / usable;
2269 item->m_sizingInfo.percentageWithinParent = 0.0;
2274void ItemBoxContainer::updateChildPercentages_recursive()
2276 updateChildPercentages();
2277 for (Item *item :
std::as_const(m_children)) {
2278 if (
auto c = item->asBoxContainer())
2279 c->updateChildPercentages_recursive();
2283Vector<double> ItemBoxContainer::Private::childPercentages()
const
2286 percentages.
reserve(q->m_children.size());
2288 for (Item *item :
std::as_const(q->m_children)) {
2289 if (item->isVisible() && !item->isBeingInserted())
2290 percentages.push_back(item->m_sizingInfo.percentageWithinParent);
2296void ItemBoxContainer::restoreChild(Item *item, NeighbourSqueezeStrategy neighbourSqueezeStrategy)
2298 assert(contains(item));
2300 const bool hadVisibleChildren = hasVisibleChildren(
true);
2302 item->setIsVisible(
true);
2303 item->setBeingInserted(
true);
2305 const int excessLength = d->excessLength();
2307 if (!hadVisibleChildren) {
2309 if (
auto c = parentBoxContainer()) {
2310 setSize(item->size());
2312 c->restoreChild(
this, neighbourSqueezeStrategy);
2317 updateSizeConstraints();
2319 item->setBeingInserted(
false);
2321 if (numVisibleChildren() == 1) {
2323 item->setGeometry_recursive(rect());
2324 d->updateSeparators_recursive();
2328 const int available = availableToSqueezeOnSide(item, Side1)
2329 + availableToSqueezeOnSide(item, Side2) - Item::separatorThickness;
2331 const int max = std::min(available, item->maxLengthHint(d->m_orientation));
2332 const int min = item->minLength(d->m_orientation);
2342 const int proposed = std::max(Core::length(item->size(), d->m_orientation),
2343 excessLength - Item::separatorThickness);
2344 const int newLength =
bound(min, proposed, max);
2346 assert(item->isVisible());
2351 item->m_sizingInfo.geometry.setHeight(0);
2353 item->m_sizingInfo.geometry.setWidth(0);
2356 growItem(item, newLength, GrowthStrategy::BothSidesEqually, neighbourSqueezeStrategy,
2358 d->updateSeparators_recursive();
2361void ItemBoxContainer::updateWidgetGeometries()
2363 for (Item *item :
std::as_const(m_children))
2364 item->updateWidgetGeometries();
2367int ItemBoxContainer::oppositeLength()
const
2369 return isVertical() ? width() : height();
2372void ItemBoxContainer::requestSeparatorMove(LayoutingSeparator *separator,
2375 const auto separatorIndex = d->m_separators.indexOf(separator);
2376 if (separatorIndex == -1) {
2378 KDDW_ERROR(
"Unknown separator {}, this={}", (
void * )separator, (
void * )
this);
2379 root()->dumpLayout();
2386 const int min = minPosForSeparator_global(separator);
2387 const int pos = separator->position();
2388 const int max = maxPosForSeparator_global(separator);
2390 if ((pos + delta < min && delta < 0) ||
2392 (pos + delta > max && delta > 0)) {
2395 root()->dumpLayout();
2396 KDDW_ERROR(
"Separator would have gone out of bounds, separator={}, min={}, pos={}, max={}, deleta={}", (
void * )separator,
2397 min, pos, max, delta);
2401 const Side moveDirection = delta < 0 ? Side1 : Side2;
2402 const Item::List children = visibleChildren();
2403 if (children.size() <= separatorIndex) {
2405 KDDW_ERROR(
"Not enough children for separator index", (
void * )separator, (
void * )
this, separatorIndex);
2406 root()->dumpLayout();
2410 int remainingToTake = std::abs(delta);
2411 int tookLocally = 0;
2413 Item *side1Neighbour = children[separatorIndex];
2414 Item *side2Neighbour = children[separatorIndex + 1];
2416 Side nextSeparatorDirection = moveDirection;
2418 if (moveDirection == Side1) {
2420 const int availableSqueeze1 = availableToSqueezeOnSide(side2Neighbour, Side1);
2421 const int availableGrow2 = availableToGrowOnSide(side1Neighbour, Side2);
2425 tookLocally = std::min(availableSqueeze1, remainingToTake);
2426 tookLocally = std::min(tookLocally, availableGrow2);
2428 if (tookLocally != 0) {
2429 growItem(side2Neighbour, tookLocally, GrowthStrategy::Side1Only,
2430 NeighbourSqueezeStrategy::ImmediateNeighboursFirst,
false,
2431 ChildrenResizeStrategy::Side1SeparatorMove);
2434 if (availableGrow2 == tookLocally)
2435 nextSeparatorDirection = Side2;
2439 const int availableSqueeze2 = availableToSqueezeOnSide(side1Neighbour, Side2);
2440 const int availableGrow1 = availableToGrowOnSide(side2Neighbour, Side1);
2443 tookLocally = std::min(availableSqueeze2, remainingToTake);
2444 tookLocally = std::min(tookLocally, availableGrow1);
2446 if (tookLocally != 0) {
2447 growItem(side1Neighbour, tookLocally, GrowthStrategy::Side2Only,
2448 NeighbourSqueezeStrategy::ImmediateNeighboursFirst,
false,
2449 ChildrenResizeStrategy::Side2SeparatorMove);
2452 if (availableGrow1 == tookLocally)
2453 nextSeparatorDirection = Side1;
2456 remainingToTake -= tookLocally;
2458 if (remainingToTake > 0) {
2462 KDDW_ERROR(
"Not enough space to move separator {}", (
void * )
this);
2464 LayoutingSeparator *nextSeparator =
2465 parentBoxContainer()->d->neighbourSeparator_recursive(
this, nextSeparatorDirection,
2467 if (!nextSeparator) {
2469 KDDW_ERROR(
"nextSeparator is null, report a bug");
2474 const int remainingDelta = moveDirection == Side1 ? -remainingToTake : remainingToTake;
2475 nextSeparator->parentContainer()->requestSeparatorMove(nextSeparator, remainingDelta);
2480void ItemBoxContainer::requestEqualSize(LayoutingSeparator *separator)
2482 const auto separatorIndex = d->m_separators.indexOf(separator);
2483 if (separatorIndex == -1) {
2485 KDDW_ERROR(
"Separator not found {}", (
void * )separator);
2489 const Item::List children = visibleChildren();
2490 Item *side1Item = children.at(separatorIndex);
2491 Item *side2Item = children.at(separatorIndex + 1);
2493 const int length1 = side1Item->length(d->m_orientation);
2494 const int length2 = side2Item->length(d->m_orientation);
2496 if (std::abs(length1 - length2) <= 1) {
2502 if (!(side1Item->m_sizingInfo.isPastMax(d->m_orientation)
2503 || side2Item->m_sizingInfo.isPastMax(d->m_orientation))) {
2508 const int newLength = (length1 + length2) / 2;
2511 if (length1 < newLength) {
2513 delta = newLength - length1;
2514 }
else if (length2 < newLength) {
2516 delta = -(newLength - length2);
2520 const int min = minPosForSeparator_global(separator,
true);
2521 const int max = maxPosForSeparator_global(separator,
true);
2522 const int newPos =
bound(min, separator->position() + delta, max);
2525 delta = newPos - separator->position();
2528 requestSeparatorMove(separator, delta);
2531void ItemBoxContainer::layoutEqually()
2533 SizingInfo::List childSizes = sizes();
2534 if (!childSizes.isEmpty()) {
2535 layoutEqually(childSizes);
2536 applyGeometries(childSizes);
2540void ItemBoxContainer::layoutEqually(SizingInfo::List &sizes)
2542 const auto numItems = sizes.count();
2544 satisfiedIndexes.
reserve(numItems);
2546 int lengthToGive = length() - (d->m_separators.size() * Item::separatorThickness);
2549 for (SizingInfo &size : sizes) {
2550 size.setLength(0, d->m_orientation);
2553 while (satisfiedIndexes.
count() < sizes.count()) {
2554 const int remainingItems = int(sizes.count() - satisfiedIndexes.
count());
2555 const int suggestedToGive = std::max(1, lengthToGive / remainingItems);
2556 const auto oldLengthToGive = lengthToGive;
2558 for (
int i = 0; i < numItems; ++i) {
2562 SizingInfo &size = sizes[i];
2563 if (size.availableToGrow(d->m_orientation) <= 0) {
2565 satisfiedIndexes.push_back(i);
2573 const auto othersMissing =
2575 std::accumulate(sizes.constBegin(), sizes.constEnd(), 0,
2576 [
this](
size_t sum,
const SizingInfo &sz) {
2577 return int(sum) + sz.missingLength(d->m_orientation);
2579 - size.missingLength(d->m_orientation);
2581 const auto maxLength =
2582 std::min(size.length(d->m_orientation) + lengthToGive - othersMissing,
2583 size.maxLengthHint(d->m_orientation));
2585 const auto newItemLenght =
2586 bound(size.minLength(d->m_orientation),
2587 size.length(d->m_orientation) + suggestedToGive, maxLength);
2588 const auto toGive = newItemLenght - size.length(d->m_orientation);
2592 satisfiedIndexes.push_back(i);
2594 lengthToGive -= toGive;
2595 size.incrementLength(toGive, d->m_orientation);
2596 if (size.availableToGrow(d->m_orientation) <= 0) {
2597 satisfiedIndexes.push_back(i);
2599 if (lengthToGive == 0)
2602 if (lengthToGive < 0) {
2603 KDDW_ERROR(
"Breaking infinite loop");
2609 if (oldLengthToGive == lengthToGive) {
2616void ItemBoxContainer::layoutEqually_recursive()
2619 for (Item *item :
std::as_const(m_children)) {
2620 if (item->isVisible()) {
2621 if (
auto c = item->asBoxContainer())
2622 c->layoutEqually_recursive();
2627Item *ItemBoxContainer::visibleNeighbourFor(
const Item *item, Side side)
const
2630 const auto index = m_children.indexOf(
const_cast<Item *
>(item));
2632 if (side == Side1) {
2633 for (
auto i = index - 1; i >= 0; --i) {
2634 Item *child = m_children.at(i);
2635 if (child->isVisible())
2639 for (
auto i = index + 1; i < m_children.size(); ++i) {
2640 Item *child = m_children.at(i);
2641 if (child->isVisible())
2649Size ItemBoxContainer::availableSize()
const
2651 return size() - this->minSize();
2654int ItemBoxContainer::availableLength()
const
2656 return isVertical() ? availableSize().height() : availableSize().width();
2659LengthOnSide ItemBoxContainer::lengthOnSide(
const SizingInfo::List &sizes,
int fromIndex, Side side,
2665 const auto count = sizes.count();
2666 if (fromIndex >= count)
2671 if (side == Side1) {
2679 LengthOnSide result;
2680 for (
int i = start; i <= end; ++i) {
2681 const SizingInfo &size = sizes.at(i);
2682 result.length += size.length(o);
2683 result.minLength += size.minLength(o);
2689int ItemBoxContainer::neighboursLengthFor(
const Item *item, Side side,
Qt::Orientation o)
const
2691 const Item::List children = visibleChildren();
2692 const auto index = children.indexOf(
const_cast<Item *
>(item));
2694 KDDW_ERROR(
"Couldn't find item {}", (
void * )item);
2698 if (o == d->m_orientation) {
2699 int neighbourLength = 0;
2702 if (side == Side1) {
2707 end = children.size() - 1;
2710 for (
int i = start; i <= end; ++i)
2711 neighbourLength += children.at(i)->length(d->m_orientation);
2713 return neighbourLength;
2720int ItemBoxContainer::neighboursLengthFor_recursive(
const Item *item, Side side,
2723 return neighboursLengthFor(item, side, o)
2724 + (isRoot() ? 0 : parentBoxContainer()->neighboursLengthFor_recursive(
this, side, o));
2727int ItemBoxContainer::neighboursMinLengthFor(
const Item *item, Side side,
Qt::Orientation o)
const
2729 const Item::List children = visibleChildren();
2730 const auto index = children.indexOf(
const_cast<Item *
>(item));
2732 KDDW_ERROR(
"Couldn't find item {}", (
void * )item);
2736 if (o == d->m_orientation) {
2737 int neighbourMinLength = 0;
2740 if (side == Side1) {
2745 end = children.size() - 1;
2748 for (
int i = start; i <= end; ++i)
2749 neighbourMinLength += children.at(i)->minLength(d->m_orientation);
2751 return neighbourMinLength;
2758int ItemBoxContainer::neighboursMaxLengthFor(
const Item *item, Side side,
Qt::Orientation o)
const
2760 const Item::List children = visibleChildren();
2761 const auto index = children.indexOf(
const_cast<Item *
>(item));
2763 KDDW_ERROR(
"Couldn't find item {}", (
void * )item);
2767 if (o == d->m_orientation) {
2768 int neighbourMaxLength = 0;
2771 if (side == Side1) {
2776 end = children.size() - 1;
2779 for (
int i = start; i <= end; ++i)
2780 neighbourMaxLength =
2781 std::min(Core::length(root()->size(), d->m_orientation),
2782 neighbourMaxLength + children.at(i)->maxLengthHint(d->m_orientation));
2784 return neighbourMaxLength;
2791int ItemBoxContainer::availableToSqueezeOnSide(
const Item *child, Side side)
const
2793 const int length = neighboursLengthFor(child, side, d->m_orientation);
2794 const int min = neighboursMinLengthFor(child, side, d->m_orientation);
2796 const int available = length - min;
2797 if (available < 0) {
2798 root()->dumpLayout();
2804int ItemBoxContainer::availableToGrowOnSide(
const Item *child, Side side)
const
2806 const int length = neighboursLengthFor(child, side, d->m_orientation);
2807 const int max = neighboursMaxLengthFor(child, side, d->m_orientation);
2809 return max - length;
2812int ItemBoxContainer::availableToSqueezeOnSide_recursive(
const Item *child, Side side,
2815 if (orientation == d->m_orientation) {
2816 const int available = availableToSqueezeOnSide(child, side);
2820 + parentBoxContainer()->availableToSqueezeOnSide_recursive(
this, side, orientation));
2824 : parentBoxContainer()->availableToSqueezeOnSide_recursive(
this, side, orientation);
2828int ItemBoxContainer::availableToGrowOnSide_recursive(
const Item *child, Side side,
2831 if (orientation == d->m_orientation) {
2832 const int available = availableToGrowOnSide(child, side);
2836 + parentBoxContainer()->availableToGrowOnSide_recursive(
this, side, orientation));
2840 : parentBoxContainer()->availableToGrowOnSide_recursive(
this, side, orientation);
2844void ItemBoxContainer::growNeighbours(Item *side1Neighbour, Item *side2Neighbour)
2846 if (!side1Neighbour && !side2Neighbour)
2849 SizingInfo::List childSizes = sizes();
2851 if (side1Neighbour && side2Neighbour) {
2852 const int index1 = indexOfVisibleChild(side1Neighbour);
2853 const int index2 = indexOfVisibleChild(side2Neighbour);
2855 if (index1 == -1 || index2 == -1 || index1 >= childSizes.count()
2856 || index2 >= childSizes.count()) {
2858 KDDW_ERROR(
"Invalid indexes {} {} {}", index1, index2, childSizes.count());
2863 Rect &geo1 = childSizes[index1].geometry;
2864 Rect &geo2 = childSizes[index2].geometry;
2867 const int available = geo2.y() - geo1.bottom() - separatorThickness;
2868 geo1.setHeight(geo1.height() + available / 2);
2869 geo2.setTop(geo1.bottom() + separatorThickness + 1);
2871 const int available = geo2.x() - geo1.right() - separatorThickness;
2872 geo1.setWidth(geo1.width() + available / 2);
2873 geo2.setLeft(geo1.right() + separatorThickness + 1);
2876 }
else if (side1Neighbour) {
2877 const int index1 = indexOfVisibleChild(side1Neighbour);
2878 if (index1 == -1 || index1 >= childSizes.count()) {
2880 KDDW_ERROR(
"Invalid indexes {} {}", index1, childSizes.count());
2885 Rect &geo = childSizes[index1].geometry;
2887 geo.setBottom(rect().bottom());
2889 geo.setRight(rect().
right());
2891 }
else if (side2Neighbour) {
2892 const int index2 = indexOfVisibleChild(side2Neighbour);
2893 if (index2 == -1 || index2 >= childSizes.count()) {
2895 KDDW_ERROR(
"Invalid indexes {} {}", index2, childSizes.count());
2900 Rect &geo = childSizes[index2].geometry;
2908 d->honourMaxSizes(childSizes);
2909 positionItems( childSizes);
2910 applyGeometries(childSizes);
2913void ItemBoxContainer::growItem(
int index, SizingInfo::List &sizes,
int missing,
2914 GrowthStrategy growthStrategy,
2915 NeighbourSqueezeStrategy neighbourSqueezeStrategy,
2916 bool accountForNewSeparator)
2918 int toSteal = missing;
2919 if (accountForNewSeparator)
2920 toSteal += Item::separatorThickness;
2922 assert(index != -1);
2927 SizingInfo &sizingInfo = sizes[index];
2928 sizingInfo.setOppositeLength(oppositeLength(), d->m_orientation);
2929 const bool isFirst = index == 0;
2930 const bool isLast = index == sizes.count() - 1;
2932 int side1Growth = 0;
2933 int side2Growth = 0;
2935 if (growthStrategy == GrowthStrategy::BothSidesEqually) {
2936 sizingInfo.setLength(sizingInfo.length(d->m_orientation) + missing, d->m_orientation);
2937 const auto count = sizes.count();
2940 sizingInfo.incrementLength(missing, d->m_orientation);
2946 const LengthOnSide side1Length = lengthOnSide(sizes, index - 1, Side1, d->m_orientation);
2947 const LengthOnSide side2Length = lengthOnSide(sizes, index + 1, Side2, d->m_orientation);
2949 int available1 = side1Length.available();
2950 int available2 = side2Length.available();
2952 if (toSteal > available1 + available2) {
2953 root()->dumpLayout();
2957 while (toSteal > 0) {
2958 if (available1 == 0) {
2959 assert(available2 >= toSteal);
2960 side2Growth += toSteal;
2962 }
else if (available2 == 0) {
2963 assert(available1 >= toSteal);
2964 side1Growth += toSteal;
2968 const int toTake = std::max(1, toSteal / 2);
2969 const int took1 = std::min(toTake, available1);
2971 available1 -= took1;
2972 side1Growth += took1;
2976 const int took2 = std::min(toTake, available2);
2978 side2Growth += took2;
2979 available2 -= took2;
2981 shrinkNeighbours(index, sizes, side1Growth, side2Growth, neighbourSqueezeStrategy);
2982 }
else if (growthStrategy == GrowthStrategy::Side1Only) {
2983 side1Growth = std::min(missing, sizingInfo.availableToGrow(d->m_orientation));
2984 sizingInfo.setLength(sizingInfo.length(d->m_orientation) + side1Growth, d->m_orientation);
2985 if (side1Growth > 0)
2986 shrinkNeighbours(index, sizes, side1Growth, 0,
2987 neighbourSqueezeStrategy);
2988 if (side1Growth < missing) {
2989 missing = missing - side1Growth;
2993 KDDW_ERROR(
"No more items to grow");
2995 growItem(index + 1, sizes, missing, growthStrategy, neighbourSqueezeStrategy,
2996 accountForNewSeparator);
3000 }
else if (growthStrategy == GrowthStrategy::Side2Only) {
3001 side2Growth = std::min(missing, sizingInfo.availableToGrow(d->m_orientation));
3002 sizingInfo.setLength(sizingInfo.length(d->m_orientation) + side2Growth, d->m_orientation);
3004 if (side2Growth > 0)
3005 shrinkNeighbours(index, sizes, 0, side2Growth,
3006 neighbourSqueezeStrategy);
3007 if (side2Growth < missing) {
3008 missing = missing - side2Growth;
3012 KDDW_ERROR(
"No more items to grow");
3014 growItem(index - 1, sizes, missing, growthStrategy, neighbourSqueezeStrategy,
3015 accountForNewSeparator);
3021void ItemBoxContainer::growItem(Item *item,
int amount, GrowthStrategy growthStrategy,
3022 NeighbourSqueezeStrategy neighbourSqueezeStrategy,
3023 bool accountForNewSeparator,
3024 ChildrenResizeStrategy childResizeStrategy)
3026 const Item::List items = visibleChildren();
3027 const auto index = items.indexOf(item);
3028 SizingInfo::List sizes = this->sizes();
3030 growItem(index, sizes, amount, growthStrategy, neighbourSqueezeStrategy,
3031 accountForNewSeparator);
3033 applyGeometries(sizes, childResizeStrategy);
3036void ItemBoxContainer::applyGeometries(
const SizingInfo::List &sizes,
3037 ChildrenResizeStrategy strategy)
3039 const Item::List items = visibleChildren();
3040 const auto count = items.size();
3041 assert(count == sizes.size());
3043 for (
int i = 0; i < count; ++i) {
3044 Item *item = items.at(i);
3045 item->setSize_recursive(sizes[i].geometry.size(), strategy);
3051SizingInfo::List ItemBoxContainer::sizes(
bool ignoreBeingInserted)
const
3053 const Item::List children = visibleChildren(ignoreBeingInserted);
3054 SizingInfo::List result;
3055 result.reserve(children.count());
3056 for (Item *item : children) {
3057 if (item->isContainer()) {
3060 item->m_sizingInfo.minSize = item->minSize();
3061 item->m_sizingInfo.maxSizeHint = item->maxSizeHint();
3063 result.push_back(item->m_sizingInfo);
3070 SizingInfo::List::const_iterator begin,
3071 SizingInfo::List::const_iterator end,
int needed,
3072 NeighbourSqueezeStrategy strategy,
bool reversed)
const
3075 for (
auto it = begin; it < end; ++it) {
3076 availabilities.push_back(it->availableLength(d->m_orientation));
3079 const auto count = availabilities.
count();
3082 squeezes.resize(count);
3083 std::fill(squeezes.begin(), squeezes.end(), 0);
3085 int missing = needed;
3087 if (strategy == NeighbourSqueezeStrategy::AllNeighbours) {
3088 while (missing > 0) {
3089 const int numDonors = int(std::count_if(availabilities.cbegin(), availabilities.cend(),
3090 [](
int num) { return num > 0; }));
3092 if (numDonors == 0) {
3093 root()->dumpLayout();
3098 int toTake = missing / numDonors;
3102 for (
int i = 0; i < count; ++i) {
3103 const int available = availabilities.
at(i);
3106 const int took = std::min(missing, std::min(toTake, available));
3107 availabilities[i] -= took;
3109 squeezes[i] += took;
3114 }
else if (strategy == NeighbourSqueezeStrategy::ImmediateNeighboursFirst) {
3115 for (
int i = 0; i < count; i++) {
3116 const auto index = reversed ? count - 1 - i : i;
3118 const int available = availabilities.
at(index);
3119 if (available > 0) {
3120 const int took = std::min(missing, available);
3122 squeezes[index] += took;
3132 KDDW_ERROR(
"Missing is negative. missing={}, squeezes={}", missing, squeezes);
3138void ItemBoxContainer::shrinkNeighbours(
int index, SizingInfo::List &sizes,
int side1Amount,
3139 int side2Amount, NeighbourSqueezeStrategy strategy)
3141 assert(side1Amount > 0 || side2Amount > 0);
3142 assert(side1Amount >= 0 && side2Amount >= 0);
3144 if (side1Amount > 0) {
3145 auto begin = sizes.cbegin();
3146 auto end = sizes.cbegin() + index;
3147 const bool reversed = strategy == NeighbourSqueezeStrategy::ImmediateNeighboursFirst;
3149 calculateSqueezes(begin, end, side1Amount, strategy, reversed);
3150 for (
int i = 0; i < squeezes.
size(); ++i) {
3151 const int squeeze = squeezes.
at(i);
3152 SizingInfo &sizing = sizes[i];
3155 sizing.setSize(
adjustedRect(sizing.geometry, d->m_orientation, 0, -squeeze).size());
3159 if (side2Amount > 0) {
3160 auto begin = sizes.cbegin() + index + 1;
3161 auto end = sizes.cend();
3163 const Vector<int> squeezes = calculateSqueezes(begin, end, side2Amount, strategy);
3164 for (
int i = 0; i < squeezes.
size(); ++i) {
3165 const int squeeze = squeezes.
at(i);
3166 SizingInfo &sizing = sizes[i + index + 1];
3167 sizing.setSize(
adjustedRect(sizing.geometry, d->m_orientation, squeeze, 0).size());
3172Vector<int> ItemBoxContainer::Private::requiredSeparatorPositions()
const
3174 const int numSeparators = std::max(0, q->numVisibleChildren() - 1);
3176 positions.
reserve(numSeparators);
3178 for (Item *item :
std::as_const(q->m_children)) {
3179 if (positions.
size() == numSeparators)
3182 if (item->isVisible()) {
3183 const int localPos = item->m_sizingInfo.edge(m_orientation) + 1;
3184 positions.push_back(q->mapToRoot(localPos, m_orientation));
3191void ItemBoxContainer::Private::updateSeparators()
3196 const Vector<int> positions = requiredSeparatorPositions();
3197 const auto requiredNumSeparators = positions.
size();
3199 const bool numSeparatorsChanged = requiredNumSeparators != m_separators.size();
3200 if (numSeparatorsChanged) {
3203 LayoutingSeparator::List newSeparators;
3204 newSeparators.reserve(requiredNumSeparators);
3206 for (
int position : positions) {
3207 LayoutingSeparator *separator = separatorAt(position);
3210 newSeparators.push_back(separator);
3211 m_separators.removeOne(separator);
3213 separator = s_createSeparatorFunc(q->host(), m_orientation, q);
3214 newSeparators.push_back(separator);
3221 m_separators = newSeparators;
3226 q->isVertical() ? q->mapToRoot(Point(0, 0)).x() : q->mapToRoot(Point(0, 0)).y();
3229 for (
int position : positions) {
3230 m_separators.at(i)->setGeometry(position, pos2, q->oppositeLength());
3234 q->updateChildPercentages();
3237void ItemBoxContainer::Private::deleteSeparators()
3239 for (
const auto &sep :
std::as_const(m_separators))
3241 m_separators.clear();
3244void ItemBoxContainer::Private::deleteSeparators_recursive()
3249 for (Item *item :
std::as_const(q->m_children)) {
3250 if (
auto c = item->asBoxContainer())
3251 c->d->deleteSeparators_recursive();
3255void ItemBoxContainer::Private::updateSeparators_recursive()
3260 const Item::List items = q->visibleChildren();
3261 for (Item *item : items) {
3262 if (
auto c = item->asBoxContainer())
3263 c->d->updateSeparators_recursive();
3267int ItemBoxContainer::Private::excessLength()
const
3270 return std::max(0, Core::length(q->size(), m_orientation) - q->maxLengthHint(m_orientation));
3273void ItemBoxContainer::simplify()
3278 ScopedValueRollback isInSimplify(d->m_isSimplifying,
true);
3280 Item::List newChildren;
3281 newChildren.reserve(m_children.size() + 20);
3283 for (Item *child :
std::as_const(m_children)) {
3284 if (ItemBoxContainer *childContainer = child->asBoxContainer()) {
3285 childContainer->simplify();
3287 if (childContainer->orientation() == d->m_orientation
3288 || childContainer->m_children.size() == 1) {
3291 const auto children = childContainer->childItems();
3292 for (Item *child2 : children) {
3293 child2->setParentContainer(
this);
3294 newChildren.push_back(child2);
3297 delete childContainer;
3299 newChildren.push_back(child);
3302 newChildren.push_back(child);
3306 if (m_children != newChildren) {
3307 m_children = newChildren;
3309 updateChildPercentages();
3313LayoutingSeparator *ItemBoxContainer::Private::separatorAt(
int p)
const
3315 for (
auto separator : m_separators) {
3316 if (separator->position() == p)
3323bool ItemBoxContainer::isVertical()
const
3328bool ItemBoxContainer::isHorizontal()
const
3333int ItemBoxContainer::indexOf(LayoutingSeparator *separator)
const
3335 return d->m_separators.indexOf(separator);
3338bool ItemBoxContainer::isInSimplify()
const
3340 if (d->m_isSimplifying)
3343 auto p = parentBoxContainer();
3344 return p && p->isInSimplify();
3347int ItemBoxContainer::minPosForSeparator(LayoutingSeparator *separator,
3348 bool honourMax)
const
3350 const int globalMin = minPosForSeparator_global(separator, honourMax);
3351 return mapFromRoot(globalMin, d->m_orientation);
3354int ItemBoxContainer::maxPosForSeparator(LayoutingSeparator *separator,
3355 bool honourMax)
const
3357 const int globalMax = maxPosForSeparator_global(separator, honourMax);
3358 return mapFromRoot(globalMax, d->m_orientation);
3361int ItemBoxContainer::minPosForSeparator_global(LayoutingSeparator *separator,
3362 bool honourMax)
const
3364 const int separatorIndex = indexOf(separator);
3365 assert(separatorIndex != -1);
3367 const Item::List children = visibleChildren();
3368 assert(separatorIndex + 1 < children.size());
3369 Item *item2 = children.at(separatorIndex + 1);
3371 const int availableToSqueeze =
3372 availableToSqueezeOnSide_recursive(item2, Side1, d->m_orientation);
3377 Item *item1 = children.at(separatorIndex);
3378 const int availabletoGrow = availableToGrowOnSide_recursive(item1, Side2, d->m_orientation);
3379 return separator->position() - std::min(availabletoGrow, availableToSqueeze);
3382 return separator->position() - availableToSqueeze;
3385int ItemBoxContainer::maxPosForSeparator_global(LayoutingSeparator *separator,
3386 bool honourMax)
const
3388 const int separatorIndex = indexOf(separator);
3389 assert(separatorIndex != -1);
3391 const Item::List children = visibleChildren();
3392 Item *item1 = children.at(separatorIndex);
3394 const int availableToSqueeze =
3395 availableToSqueezeOnSide_recursive(item1, Side2, d->m_orientation);
3400 Item *item2 = children.at(separatorIndex + 1);
3401 const int availabletoGrow = availableToGrowOnSide_recursive(item2, Side1, d->m_orientation);
3402 return separator->position() + std::min(availabletoGrow, availableToSqueeze);
3405 return separator->position() + availableToSqueeze;
3408void ItemBoxContainer::to_json(nlohmann::json &j)
const
3412 j[
"children"] = m_children;
3413 j[
"orientation"] = d->m_orientation;
3416void ItemBoxContainer::fillFromJson(
const nlohmann::json &j,
3417 const std::unordered_map<QString, LayoutingGuest *> &widgets)
3419 if (!j.is_object()) {
3420 KDDW_ERROR(
"Expected a JSON object");
3424 ScopedValueRollback deserializing(d->m_isDeserializing,
true);
3425 Item::fillFromJson(j, widgets);
3429 for (
const auto &child : j.value(
"children", nlohmann::json::array())) {
3430 const bool isContainer = child.value<
bool>(
"isContainer", {});
3432 isContainer ?
new ItemBoxContainer(host(),
this) : new Item(host(), this);
3433 childItem->fillFromJson(child, widgets);
3434 m_children.push_back(childItem);
3438 updateChildPercentages_recursive();
3440 d->updateSeparators_recursive();
3441 d->updateWidgets_recursive();
3444 d->relayoutIfNeeded();
3445 positionItems_recursive();
3447 minSizeChanged.emit(
this);
3448#ifdef DOCKS_DEVELOPER_MODE
3450 KDDW_ERROR(
"Resulting layout is invalid");
3455bool ItemBoxContainer::Private::isDummy()
const
3457 return q->host() ==
nullptr;
3460#ifdef DOCKS_DEVELOPER_MODE
3461void ItemBoxContainer::relayoutIfNeeded()
3463 d->relayoutIfNeeded();
3466bool ItemBoxContainer::test_suggestedRect()
3468 auto itemToDrop =
new Item(host());
3470 const Item::List children = visibleChildren();
3471 for (Item *relativeTo : children) {
3472 if (
auto c = relativeTo->asBoxContainer()) {
3473 c->test_suggestedRect();
3475 std::unordered_map<Location, Rect> rects;
3478 const Rect rect = suggestedDropRect(itemToDrop, relativeTo, loc);
3480 if (rect.isEmpty()) {
3481 KDDW_ERROR(
"Empty rect");
3483 }
else if (!root()->rect().contains(rect)) {
3484 root()->dumpLayout();
3485 KDDW_ERROR(
"Suggested rect is out of bounds rect={}, loc={}, relativeTo={}", rect, loc, (
void * )relativeTo);
3490 auto rectFor = [&rects](
Location loc) -> Rect {
3491 auto it = rects.find(loc);
3492 return it == rects.cend() ? Rect() : it->second;
3497 root()->dumpLayout();
3498 KDDW_ERROR(
"Invalid suggested rects. this={}, relativeTo={}", (
void * )
this, (
void * )relativeTo);
3511 LayoutingSeparator::List separators = d->m_separators;
3513 for (Item *item :
std::as_const(m_children)) {
3514 if (
auto c = item->asBoxContainer())
3515 separators.
append(c->separators_recursive());
3523 return d->m_separators;
3526LayoutingSeparator *ItemBoxContainer::separatorForChild(Item *child, Side side)
const
3528 if (!child || !child->isVisible()) {
3529 KDDW_ERROR(
"ItemBoxContainer::separatorForChild: Unexpected nullptr or invisible child");
3533 const Item::List children = visibleChildren();
3534 const int childIndex = children.indexOf(child);
3535 if (childIndex == -1) {
3536 KDDW_ERROR(
"ItemBoxContainer::separatorForChild: Could not find child");
3540 int separatorIndex = -1;
3542 if (side == Side1) {
3544 if (childIndex == 0)
3547 separatorIndex = childIndex - 1;
3550 if (childIndex == children.size() - 1)
3553 separatorIndex = childIndex;
3556 if (separatorIndex < 0 || separatorIndex >= d->m_separators.size()) {
3557 KDDW_ERROR(
"ItemBoxContainer::separatorForChild: Not enough separators {} {} {}", d->m_separators.size(), children.size(), childIndex);
3561 return d->m_separators.at(separatorIndex);
3564LayoutingSeparator *ItemBoxContainer::adjacentSeparatorForChild(Item *child, Side side)
const
3566 if (!child || !child->isVisible()) {
3567 KDDW_ERROR(
"ItemBoxContainer::adjacentSeparatorForChild: Unexpected nullptr or invisible child");
3571 int separatorIndex = -1;
3574 if (ItemBoxContainer *ancestor = ancestorBoxContainerWithOrientation(
oppositeOrientation(orientation()))) {
3576 const int childIndex = indexInAncestor(ancestor);
3577 const auto children = ancestor->visibleChildren();
3578 const auto separators = ancestor->separators();
3580 if (childIndex == -1) {
3582 KDDW_ERROR(
"ItemBoxContainer::adjacentSeparatorForChild: Could not find index inside ancestor");
3586 if (side == Side1) {
3587 if (childIndex == 0)
3590 separatorIndex = childIndex - 1;
3592 if (childIndex == children.size() - 1)
3595 separatorIndex = childIndex;
3598 if (separatorIndex < 0 || separatorIndex >= separators.size()) {
3599 KDDW_ERROR(
"ItemBoxContainer::adjacentSeparatorForChild: Not enough separators {} {} {}", separators.size(), children.size(), childIndex);
3603 return separators[separatorIndex];
3610bool ItemBoxContainer::isOverflowing()
const
3615 int contentsLength = 0;
3617 for (Item *item :
std::as_const(m_children)) {
3618 if (item->isVisible()) {
3619 contentsLength += item->length(d->m_orientation);
3624 contentsLength += std::max(0, Item::separatorThickness * (numVisible - 1));
3625 return contentsLength > length();
3628void ItemBoxContainer::Private::relayoutIfNeeded()
3636 const Size missing = q->missingSize();
3637 if (!missing.isNull())
3638 q->setSize_recursive(q->size() + missing);
3642 for (Item *child :
std::as_const(q->m_children)) {
3643 const int missingLength = ::length(child->missingSize(), m_orientation);
3644 if (!child->isVisible() || missingLength == 0)
3647 q->growItem(child, missingLength, GrowthStrategy::BothSidesEqually, NeighbourSqueezeStrategy::AllNeighbours);
3651 if (q->isOverflowing()) {
3652 const Size size = q->size();
3653 q->m_sizingInfo.setSize(size + Size(1, 1));
3654 q->setSize_recursive(size);
3655 q->updateChildPercentages();
3659 for (Item *item :
std::as_const(q->m_children)) {
3660 if (item->isVisible()) {
3661 if (
auto c = item->asBoxContainer())
3662 c->d->relayoutIfNeeded();
3667const Item *ItemBoxContainer::Private::itemFromPath(
const Vector<int> &path)
const
3669 const ItemBoxContainer *container = q;
3671 for (
int i = 0; i < path.
size(); ++i) {
3672 const int index = path[i];
3673 const bool isLast = i == path.
size() - 1;
3674 if (index < 0 || index >= container->m_children.size()) {
3676 q->root()->dumpLayout();
3677 KDDW_ERROR(
"Invalid index {}, this={}, path={}, isRoot={}", index, (
void * )
this, path, q->isRoot());
3682 return container->m_children.at(index);
3684 container = container->m_children.at(index)->asBoxContainer();
3686 KDDW_ERROR(
"Invalid index path={}", path);
3696ItemBoxContainer::Private::neighbourSeparator(
const Item *item, Side side,
3699 Item::List children = q->visibleChildren();
3700 const auto itemIndex = children.indexOf(
const_cast<Item *
>(item));
3701 if (itemIndex == -1) {
3702 KDDW_ERROR(
"Item not found item={}, this={}", (
void * )item, (
void * )
this);
3703 q->root()->dumpLayout();
3707 if (orientation != q->orientation()) {
3712 return q->parentBoxContainer()->d->neighbourSeparator(q, side, orientation);
3716 const auto separatorIndex = side == Side1 ? itemIndex - 1 : itemIndex;
3718 if (separatorIndex < 0 || separatorIndex >= m_separators.size())
3721 return m_separators[separatorIndex];
3725ItemBoxContainer::Private::neighbourSeparator_recursive(
const Item *item, Side side,
3728 LayoutingSeparator *separator = neighbourSeparator(item, side, orientation);
3732 if (!q->parentContainer())
3735 return q->parentBoxContainer()->d->neighbourSeparator_recursive(q, side, orientation);
3738void ItemBoxContainer::Private::updateWidgets_recursive()
3740 for (Item *item :
std::as_const(q->m_children)) {
3741 if (
auto c = item->asBoxContainer()) {
3742 c->d->updateWidgets_recursive();
3744 if (item->isVisible()) {
3745 if (
auto guest = item->guest()) {
3746 guest->setGeometry(q->mapToRoot(item->geometry()));
3747 guest->setVisible(
true);
3749 KDDW_ERROR(
"visible item doesn't have a guest item=", (
void * )item);
3756SizingInfo::SizingInfo()
3757 : minSize(Core::Item::hardcodedMinimumSize)
3758 , maxSizeHint(Core::Item::hardcodedMaximumSize)
3769 return std::max(minLength(o), Core::length(maxSizeHint, o));
3774 return std::max(0, length(o) - minLength(o));
3779 return std::max(0, minLength(o) - length(o));
3784 return std::max(0, length(o) - maxLengthHint(o));
3787void Core::to_json(nlohmann::json &j,
const SizingInfo &info)
3789 j[
"geometry"] = info.geometry;
3790 j[
"minSize"] = info.minSize;
3791 j[
"maxSizeHint"] = info.maxSizeHint;
3792 j[
"percentageWithinParent"] = info.percentageWithinParent;
3795void Core::from_json(
const nlohmann::json &j, SizingInfo &info)
3797 info.geometry = j.value(
"geometry", Rect());
3798 info.minSize = j.value(
"minSize", Size());
3799 info.maxSizeHint = j.value(
"maxSizeHint", Size());
3800 info.percentageWithinParent = j.value<
double>(
"percentageWithinParent", {});
3803void Core::to_json(nlohmann::json &j, Item *item)
3812int ItemBoxContainer::Private::defaultLengthFor(Item *item,
InitialOption option)
const
3817 && option.sizeMode != DefaultSizeMode::NoDefaultSizeMode) {
3820 switch (option.sizeMode) {
3821 case DefaultSizeMode::NoDefaultSizeMode:
3823 case DefaultSizeMode::Fair: {
3824 const int numVisibleChildren =
3825 q->numVisibleChildren() + 1;
3826 const int usableLength =
3827 q->length() - (Item::separatorThickness * (numVisibleChildren - 1));
3828 result = usableLength / numVisibleChildren;
3831 case DefaultSizeMode::FairButFloor: {
3832 int length = defaultLengthFor(item, DefaultSizeMode::Fair);
3833 result = std::min(length, item->length(m_orientation));
3836 case DefaultSizeMode::ItemSize:
3837 result = item->length(m_orientation);
3842 result = std::max(item->minLength(m_orientation), result);
3846struct ItemContainer::Private
3848 explicit Private(ItemContainer *qq)
3856 ItemContainer *
const q;
3859ItemContainer::ItemContainer(LayoutingHost *hostWidget, ItemContainer *parent)
3860 : Item(true, hostWidget, parent)
3861 , d(new Private(this))
3863 xChanged.connect([
this] {
3864 for (Item *item :
std::as_const(m_children)) {
3865 item->xChanged.emit();
3869 yChanged.connect([
this] {
3870 for (Item *item :
std::as_const(m_children)) {
3871 item->yChanged.emit();
3876ItemContainer::ItemContainer(LayoutingHost *hostWidget)
3877 : Item(true, hostWidget, nullptr)
3878 , d(new Private(this))
3882ItemContainer::~ItemContainer()
3887Item::List ItemContainer::childItems()
const
3892int ItemContainer::indexOfChild(
const Item *child)
const
3894 return m_children.indexOf(
const_cast<Item *
>(child));
3897bool ItemContainer::hasChildren()
const
3899 return !m_children.isEmpty();
3902bool ItemContainer::hasVisibleChildren(
bool excludeBeingInserted)
const
3904 for (Item *item :
std::as_const(m_children)) {
3905 if (item->isVisible(excludeBeingInserted))
3912int ItemContainer::numChildren()
const
3914 return m_children.size();
3917int ItemContainer::numVisibleChildren()
const
3920 for (Item *child :
std::as_const(m_children)) {
3921 if (child->isVisible())
3927bool ItemContainer::isEmpty()
const
3929 return m_children.isEmpty();
3932bool ItemContainer::hasSingleVisibleItem()
const
3934 return numVisibleChildren() == 1;
3937bool ItemContainer::contains(
const Item *item)
const
3939 return m_children.contains(
const_cast<Item *
>(item));
3942Item *ItemContainer::itemForView(
const LayoutingGuest *w)
const
3944 for (Item *item :
std::as_const(m_children)) {
3945 if (item->isContainer()) {
3946 if (Item *result = item->asContainer()->itemForView(w))
3948 }
else if (item->guest() == w) {
3956Item::List ItemContainer::visibleChildren(
bool includeBeingInserted)
const
3959 items.reserve(m_children.size());
3960 for (Item *item :
std::as_const(m_children)) {
3961 if (includeBeingInserted) {
3962 if (item->isVisible() || item->isBeingInserted())
3963 items.push_back(item);
3965 if (item->isVisible() && !item->isBeingInserted())
3966 items.push_back(item);
3973Item::List ItemContainer::items_recursive()
const
3977 for (Item *item :
std::as_const(m_children)) {
3978 if (
auto c = item->asContainer()) {
3979 items.append(c->items_recursive());
3981 items.push_back(item);
3988bool ItemContainer::contains_recursive(
const Item *item)
const
3990 for (Item *it :
std::as_const(m_children)) {
3993 }
else if (it->isContainer()) {
3994 if (it->asContainer()->contains_recursive(item))
4003int ItemContainer::visibleCount_recursive()
const
4006 for (Item *item :
std::as_const(m_children)) {
4007 count += item->visibleCount_recursive();
4013int ItemContainer::count_recursive()
const
4016 for (Item *item :
std::as_const(m_children)) {
4017 if (
auto c = item->asContainer()) {
4018 count += c->count_recursive();
4027LayoutingHost::~LayoutingHost() =
default;
4028LayoutingSeparator::~LayoutingSeparator() =
default;
4030LayoutingSeparator::LayoutingSeparator(LayoutingHost *host,
Qt::Orientation orientation, Core::ItemBoxContainer *container)
4032 , m_orientation(orientation)
4033 , m_parentContainer(container)
4037bool LayoutingSeparator::isVertical()
const
4042int LayoutingSeparator::position()
const
4044 const Point topLeft = geometry().topLeft();
4045 return isVertical() ? topLeft.y() : topLeft.x();
4048ItemBoxContainer *LayoutingSeparator::parentContainer()
const
4050 return m_parentContainer;
4055 return m_orientation;
4059void LayoutingSeparator::setGeometry(
int pos,
int pos2,
int length)
4061 Rect newGeo = geometry();
4064 newGeo.setSize(Size(length, Core::Item::separatorThickness));
4065 newGeo.moveTo(pos2, pos);
4068 newGeo.setSize(Size(Core::Item::separatorThickness, length));
4069 newGeo.moveTo(pos, pos2);
4072 setGeometry(newGeo);
4075void LayoutingSeparator::free()
4080bool LayoutingSeparator::isBeingDragged()
const
4082 return LayoutingSeparator::s_separatorBeingDragged !=
nullptr;
4085void LayoutingSeparator::onMousePress()
4087 LayoutingSeparator::s_separatorBeingDragged =
this;
4090void LayoutingSeparator::onMouseRelease()
4092 LayoutingSeparator::s_separatorBeingDragged =
nullptr;
4095int LayoutingSeparator::onMouseMove(Point pos,
bool moveSeparator)
4097 if (!isBeingDragged())
4100 const int positionToGoTo = Core::pos(pos, m_orientation);
4101 const int minPos = m_parentContainer->minPosForSeparator_global(
this);
4102 const int maxPos = m_parentContainer->maxPosForSeparator_global(
this);
4104 if ((positionToGoTo > maxPos && position() <= positionToGoTo)
4105 || (positionToGoTo < minPos && position() >= positionToGoTo)) {
4117 m_parentContainer->requestSeparatorMove(
this, positionToGoTo - position());
4119 return positionToGoTo;
4122class LayoutingGuest::Private
4125 ObjectGuard<Core::Item> layoutItem;
4128Core::Item *LayoutingGuest::layoutItem()
const
4130 return d->layoutItem;
4133void LayoutingGuest::setLayoutItem(Item *item)
4135 if (d->layoutItem == item)
4139 d->layoutItem->unref();
4144 d->layoutItem = item;
4146 setLayoutItem_impl(item);
4149LayoutingGuest::LayoutingGuest()
4154LayoutingGuest::~LayoutingGuest()
4162void LayoutingHost::insertItem(Core::LayoutingGuest *guest,
Location loc,
4165 if (!guest || !guest->layoutItem()) {
4170 if (
auto box = m_rootItem->asBoxContainer())
4171 box->insertItem(guest->layoutItem(), loc, initialOption);
4177void LayoutingHost::insertItemRelativeTo(Core::LayoutingGuest *guest, Core::LayoutingGuest *relativeTo,
Location loc,
4180 if (!guest || !relativeTo || !guest->layoutItem() || !relativeTo->layoutItem()) {
4185 if (
auto box = m_rootItem->asBoxContainer())
4186 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)