diff --git a/src/private/multisplitter/Item.cpp b/src/private/multisplitter/Item.cpp index 74acf067..a0e9188a 100644 --- a/src/private/multisplitter/Item.cpp +++ b/src/private/multisplitter/Item.cpp @@ -222,7 +222,7 @@ void Item::setHostWidget(QWidget *host) } } -void Item::setSize_recursive(QSize newSize) +void Item::setSize_recursive(QSize newSize, ChildrenResizeStrategy) { setSize(newSize); } @@ -1659,14 +1659,32 @@ void ItemContainer::resizeChildren(QSize oldSize, QSize newSize, SizingInfo::Lis itemSize.geometry.setSize({ newItemLength, height() }); } } - } else if (strategy == ChildrenResizeStrategy::Side1 || strategy == ChildrenResizeStrategy::Side2) { + } else if (strategy == ChildrenResizeStrategy::Side1SeparatorMove || + strategy == ChildrenResizeStrategy::Side2SeparatorMove) { int remaining = Layouting::length(newSize - oldSize, m_orientation); // This is how much we need to give to children (when growing the container), or to take from them when shrinking the container const bool isGrowing = remaining > 0; remaining = qAbs(remaining); // Easier to deal in positive numbers - const bool isSide1 = strategy == ChildrenResizeStrategy::Side1; + + // We're resizing the container, and need to decide if we start resizing the 1st children or + //, in reverse order. + // If the separator is being dragged left or top, then isSide1SeparatorMove is true. + // If isSide1SeparatorMove is true and we're growing, then it means this container is on the right/bottom top of the separator, + // so should resize its first children first. Same logic for the other 3 cases + + const bool isSide1SeparatorMove = strategy == ChildrenResizeStrategy::Side1SeparatorMove; + bool resizeHeadFirst; + if (isGrowing && isSide1SeparatorMove) { + resizeHeadFirst = true; + } else if (isGrowing && !isSide1SeparatorMove) { + resizeHeadFirst = false; + } else if (!isGrowing && isSide1SeparatorMove) { + resizeHeadFirst = false; + } else if (!isGrowing && !isSide1SeparatorMove) { + resizeHeadFirst = true; + } for (int i = 0; i < count; i++) { - const int index = isSide1 ? i : count - 1 - i; + const int index = resizeHeadFirst ? i : count - 1 - i; SizingInfo &size = childSizes[index]; @@ -1687,7 +1705,7 @@ void ItemContainer::resizeChildren(QSize oldSize, QSize newSize, SizingInfo::Lis } } -void ItemContainer::setSize_recursive(QSize newSize) +void ItemContainer::setSize_recursive(QSize newSize, ChildrenResizeStrategy strategy) { QScopedValueRollback block(m_blockUpdatePercentages, true); @@ -1717,7 +1735,7 @@ void ItemContainer::setSize_recursive(QSize newSize) // so doing it in 2 steps will reuse much logic. // the sizes: - resizeChildren(oldSize, newSize, /*by-ref*/ childSizes, ChildrenResizeStrategy::Percentage); + resizeChildren(oldSize, newSize, /*by-ref*/ childSizes, strategy); // the positions: positionItems(/*by-ref*/ childSizes); @@ -1731,7 +1749,7 @@ void ItemContainer::setSize_recursive(QSize newSize) } // #3 Sizes are now correct and honour min/max sizes. So apply them to our Items - applyGeometries(childSizes); + applyGeometries(childSizes, strategy); } int ItemContainer::length() const @@ -1927,7 +1945,8 @@ void ItemContainer::requestSeparatorMove(Separator *separator, int delta) tookLocally = qMin(available1, remainingToTake); if (tookLocally != 0) { - growItem(side2Neighbour, tookLocally, GrowthStrategy::Side1Only); + growItem(side2Neighbour, tookLocally, GrowthStrategy::Side1Only, false, + ChildrenResizeStrategy::Side1SeparatorMove); } } else { @@ -1936,7 +1955,8 @@ void ItemContainer::requestSeparatorMove(Separator *separator, int delta) const int available2 = availableOnSide(side1Neighbour, Side2); tookLocally = qMin(available2, remainingToTake); if (tookLocally != 0) { - growItem(side1Neighbour, tookLocally, GrowthStrategy::Side2Only); + growItem(side1Neighbour, tookLocally, GrowthStrategy::Side2Only, false, + ChildrenResizeStrategy::Side2SeparatorMove); } } @@ -2252,17 +2272,19 @@ void ItemContainer::growItem(int index, SizingInfo::List &sizes, int missing, shrinkNeighbours(index, sizes, side1Growth, side2Growth, neighbourSqueezeStrategy); } -void ItemContainer::growItem(Item *item, int amount, GrowthStrategy growthStrategy, bool accountForNewSeparator) +void ItemContainer::growItem(Item *item, int amount, GrowthStrategy growthStrategy, + bool accountForNewSeparator, ChildrenResizeStrategy childResizeStrategy) { const Item::List items = visibleChildren(); const int index = items.indexOf(item); SizingInfo::List sizes = this->sizes(); growItem(index, /*by-ref=*/sizes, amount, growthStrategy, accountForNewSeparator); - applyGeometries(sizes); + + applyGeometries(sizes, childResizeStrategy); } -void ItemContainer::applyGeometries(const SizingInfo::List &sizes) +void ItemContainer::applyGeometries(const SizingInfo::List &sizes, ChildrenResizeStrategy strategy) { const Item::List items = visibleChildren(); const int count = items.size(); @@ -2270,7 +2292,7 @@ void ItemContainer::applyGeometries(const SizingInfo::List &sizes) for (int i = 0; i < count; ++i) { Item *item = items.at(i); - item->setSize_recursive(sizes[i].geometry.size()); + item->setSize_recursive(sizes[i].geometry.size(), strategy); } positionItems(); @@ -2592,6 +2614,11 @@ QVector ItemContainer::separators_recursive() const return separators; } +QVector ItemContainer::separators() const +{ + return m_separators; +} + Separator *ItemContainer::neighbourSeparator(const Item *item, Side side, Qt::Orientation orientation) const { Item::List children = visibleChildren(); diff --git a/src/private/multisplitter/Item_p.h b/src/private/multisplitter/Item_p.h index d50c43c5..54a4f801 100644 --- a/src/private/multisplitter/Item_p.h +++ b/src/private/multisplitter/Item_p.h @@ -76,8 +76,8 @@ Q_DECLARE_FLAGS(SeparatorOptions, SeparatorOption) enum class ChildrenResizeStrategy { Percentage, ///< Resizes the container in a way that all children will keep occupying the same percentage - Side1, ///< When resizing a container, it takes/adds space from Side1 children first - Side2, ///< When resizing a container, it takes/adds space from Side2 children first + Side1SeparatorMove, ///< When resizing a container, it takes/adds space from Side1 children first + Side2SeparatorMove ///< When resizing a container, it takes/adds space from Side2 children first }; enum class NeighbourSqueezeStrategy { @@ -342,7 +342,7 @@ public: virtual QSize minSize() const; virtual QSize maxSize() const; - virtual void setSize_recursive(QSize newSize); + virtual void setSize_recursive(QSize newSize, ChildrenResizeStrategy strategy = ChildrenResizeStrategy::Percentage); virtual bool isVisible(bool excludeBeingInserted = false) const; virtual void setGeometry_recursive(QRect rect); virtual void dumpLayout(int level = 0); @@ -444,7 +444,7 @@ public: void setOrientation(Qt::Orientation); QSize minSize() const override; QSize maxSize() const override; - void setSize_recursive(QSize newSize) override; + void setSize_recursive(QSize newSize, ChildrenResizeStrategy strategy = ChildrenResizeStrategy::Percentage) override; int length() const; QRect rect() const; QVariantList items() const; @@ -467,7 +467,7 @@ public: ///@brief grows an item by @p amount. It calculates how much to grow on side1 and on side2 ///Then calls growItem(item, side1Growth, side2Growth) which will effectively grow it, ///and shrink the neighbours which are donating the size. - void growItem(Item *, int amount, GrowthStrategy, bool accountForNewSeparator = false); + void growItem(Item *, int amount, GrowthStrategy, bool accountForNewSeparator = false, ChildrenResizeStrategy = ChildrenResizeStrategy::Percentage); void growItem(int index, SizingInfo::List &sizes, int missing, GrowthStrategy, bool accountForNewSeparator = false); ///@brief Shrinks the neighbours of the item at @p index @@ -508,7 +508,7 @@ public: void setIsVisible(bool) override; bool isVisible(bool excludeBeingInserted = false) const override; void setLength_recursive(int length, Qt::Orientation) override; - void applyGeometries(const SizingInfo::List &sizes); + void applyGeometries(const SizingInfo::List &sizes, ChildrenResizeStrategy = ChildrenResizeStrategy::Percentage); void applyPositions(const SizingInfo::List &sizes); Qt::Orientation orientation() const; bool isVertical() const; @@ -535,6 +535,7 @@ public: bool m_isResizing = false; bool m_blockUpdatePercentages = false; QVector separators_recursive() const; + QVector separators() const; Qt::Orientation m_orientation = Qt::Vertical; private: void resizeChildren(QSize oldSize, QSize newSize, SizingInfo::List &sizes, ChildrenResizeStrategy); diff --git a/src/private/multisplitter/tests/tst_multisplitter.cpp b/src/private/multisplitter/tests/tst_multisplitter.cpp index d9c6f411..9f0b2176 100644 --- a/src/private/multisplitter/tests/tst_multisplitter.cpp +++ b/src/private/multisplitter/tests/tst_multisplitter.cpp @@ -199,6 +199,7 @@ private Q_SLOTS: void tst_availableOnSide(); void tst_resizeViaSeparator(); void tst_resizeViaSeparator2(); + void tst_resizeViaSeparator3(); void tst_mapToRoot(); }; @@ -1227,6 +1228,61 @@ void TestMultiSplitter::tst_resizeViaSeparator2() QCOMPARE(item4->width(), originalChildWidth); } +void TestMultiSplitter::tst_resizeViaSeparator3() +{ + // Like tst_resizeViaSeparator2 but we have nesting, when a container is shrunk, it too + // should only shrink its children that are near the separator, instead of all of them equally + + // Layout: |1 | 3| + // |4 | | + // ------- + // 2 + + auto root = createRoot(); + auto item1 = createItem(); + auto item2 = createItem(); + auto item3 = createItem(); + auto item4 = createItem(); + + root->insertItem(item1, Location_OnTop); + root->insertItem(item2, Location_OnBottom); + item1->insertItem(item3, Location_OnRight); + item1->insertItem(item4, Location_OnBottom); + + // Make some room, so each item has enough space to shrink without hitting constriants + root->setSize_recursive(QSize(1000, 4000)); + + // Our horizontal separator + const auto separators = root->separators(); + const auto horizontalSeparator = separators[0]; + QCOMPARE(separators.size(), 1); + + const int delta = 10; + const int oldH1 = item1->height(); + const int oldH2 = item2->height(); + const int oldH3 = item3->height(); + const int oldH4 = item4->height(); + + // If the following ever fails, then make sure item4 has space before we move the separator + QVERIFY(item4->availableLength(Qt::Vertical) > delta); + + // Move separator up: + root->requestSeparatorMove(horizontalSeparator, -delta); + + QCOMPARE(item2->height(), oldH2 + delta); + QCOMPARE(item3->height(), oldH3 - delta); + QCOMPARE(item4->height(), oldH4 - delta); + QCOMPARE(item1->height(), oldH1); + + // Move down again + root->requestSeparatorMove(horizontalSeparator, delta); + + QCOMPARE(item2->height(), oldH2); + QCOMPARE(item3->height(), oldH3); + QCOMPARE(item4->height(), oldH4); + QCOMPARE(item1->height(), oldH1); +} + void TestMultiSplitter::tst_mapToRoot() { auto root = createRoot();