diff --git a/src/private/multisplitter/Item.cpp b/src/private/multisplitter/Item.cpp index 6e850b4e..af301dd2 100644 --- a/src/private/multisplitter/Item.cpp +++ b/src/private/multisplitter/Item.cpp @@ -1659,12 +1659,10 @@ void ItemContainer::resizeChildren(QSize oldSize, QSize newSize, SizingInfo::Lis remaining = qAbs(remaining); // Easier to deal in positive numbers const bool isSide1 = strategy == ChildrenResizeStrategy::Side1; - const int start = isSide1 ? 0 : count - 1; - const int end = isSide1 ? count - 1 : 0; - const int increment = isSide1 ? 1 : -1; + for (int i = 0; i < count; i++) { + const int index = isSide1 ? i : count - 1 - i; - for (int i = start; i <= end; i+=increment) { - SizingInfo &size = childSizes[i]; + SizingInfo &size = childSizes[index]; if (isGrowing) { // Since we don't honour item max-size yet, it can just grow all it wants @@ -2189,6 +2187,7 @@ void ItemContainer::growItem(int index, SizingInfo::List &sizes, int missing, int side1Growth = 0; int side2Growth = 0; + auto neighbourSqueezeStrategy = NeighbourSqueezeStrategy::Equally; if (growthStrategy == GrowthStrategy::BothSidesEqually) { const int count = sizes.count(); @@ -2237,12 +2236,14 @@ void ItemContainer::growItem(int index, SizingInfo::List &sizes, int missing, } else if (growthStrategy == GrowthStrategy::Side1Only) { side1Growth = missing; side2Growth = 0; + neighbourSqueezeStrategy = NeighbourSqueezeStrategy::Side2NeighboursFirst; } else if (growthStrategy == GrowthStrategy::Side2Only) { side1Growth = 0; side2Growth = missing; + neighbourSqueezeStrategy = NeighbourSqueezeStrategy::Side1NeighboursFirst; } - shrinkNeighbours(index, sizes, side1Growth, side2Growth); + shrinkNeighbours(index, sizes, side1Growth, side2Growth, neighbourSqueezeStrategy); } void ItemContainer::growItem(Item *item, int amount, GrowthStrategy growthStrategy, bool accountForNewSeparator) @@ -2284,7 +2285,8 @@ SizingInfo::List ItemContainer::sizes(bool ignoreBeingInserted) const } QVector ItemContainer::calculateSqueezes(SizingInfo::List::ConstIterator begin, - SizingInfo::List::ConstIterator end, int needed) const + SizingInfo::List::ConstIterator end, int needed, + NeighbourSqueezeStrategy strategy) const { QVector availabilities; for (auto it = begin; it < end; ++it) { @@ -2295,38 +2297,59 @@ QVector ItemContainer::calculateSqueezes(SizingInfo::List::ConstIterator be QVector squeezes(count, 0); int missing = needed; - while (missing > 0) { - const int numDonors = std::count_if(availabilities.cbegin(), availabilities.cend(), [] (int num) { - return num > 0; - }); - if (numDonors == 0) { - root()->dumpLayout(); - Q_ASSERT(false); - return {}; + if (strategy == NeighbourSqueezeStrategy::Equally) { + while (missing > 0) { + const int numDonors = std::count_if(availabilities.cbegin(), availabilities.cend(), [] (int num) { + return num > 0; + }); + + if (numDonors == 0) { + root()->dumpLayout(); + Q_ASSERT(false); + return {}; + } + + int toTake = missing / numDonors; + if (toTake == 0) + toTake = missing; + + for (int i = 0; i < count; ++i) { + const int available = availabilities.at(i); + if (available == 0) + continue; + const int took = qMin(toTake, available); + availabilities[i] -= took; + missing -= took; + squeezes[i] += took; + if (missing == 0) + break; + } } + } else if (strategy == NeighbourSqueezeStrategy::Side1NeighboursFirst || + strategy == NeighbourSqueezeStrategy::Side2NeighboursFirst) { - int toTake = missing / numDonors; - if (toTake == 0) - toTake = missing; + for (int i = 0; i < count; i++) { + const int index = strategy == NeighbourSqueezeStrategy::Side1NeighboursFirst ? i + : count - 1 - i; + const int available = availabilities.at(index); + if (available > 0) { + const int took = qMin(missing, available); + missing -= took; + squeezes[index] += took; + } - for (int i = 0; i < count; ++i) { - const int available = availabilities.at(i); - if (available == 0) - continue; - const int took = qMin(toTake, available); - availabilities[i] -= took; - missing -= took; - squeezes[i] += took; if (missing == 0) break; } } + return squeezes; } -void ItemContainer::shrinkNeighbours(int index, SizingInfo::List &sizes, int side1Amount, int side2Amount) +void ItemContainer::shrinkNeighbours(int index, SizingInfo::List &sizes, int side1Amount, + int side2Amount, NeighbourSqueezeStrategy strategy) { Q_ASSERT(side1Amount > 0 || side2Amount > 0); Q_ASSERT(side1Amount >= 0 && side2Amount >= 0); // never negative @@ -2335,7 +2358,7 @@ void ItemContainer::shrinkNeighbours(int index, SizingInfo::List &sizes, int sid auto begin = sizes.cbegin(); auto end = sizes.cbegin() + index; - const QVector squeezes = calculateSqueezes(begin, end, side1Amount); + const QVector squeezes = calculateSqueezes(begin, end, side1Amount, strategy); for (int i = 0; i < squeezes.size(); ++i) { const int squeeze = squeezes.at(i); SizingInfo &sizing = sizes[i]; @@ -2348,7 +2371,7 @@ void ItemContainer::shrinkNeighbours(int index, SizingInfo::List &sizes, int sid auto begin = sizes.cbegin() + index + 1; auto end = sizes.cend(); - const QVector squeezes = calculateSqueezes(begin, end, side2Amount); + const QVector squeezes = calculateSqueezes(begin, end, side2Amount, strategy); for (int i = 0; i < squeezes.size(); ++i) { const int squeeze = squeezes.at(i); SizingInfo &sizing = sizes[i + index + 1]; diff --git a/src/private/multisplitter/Item_p.h b/src/private/multisplitter/Item_p.h index 0d28b0b5..d50c43c5 100644 --- a/src/private/multisplitter/Item_p.h +++ b/src/private/multisplitter/Item_p.h @@ -80,6 +80,12 @@ enum class ChildrenResizeStrategy { Side2, ///< When resizing a container, it takes/adds space from Side2 children first }; +enum class NeighbourSqueezeStrategy { + Equally, ///< The squeeze is spread between all neighbours, not just immediate ones first + Side1NeighboursFirst, ///< The first neighbour takes as much squeeze as it cans, only then the next neighbour is squezed, and so forth + Side2NeighboursFirst ///< Same as Side1NeighboursFirst but does reverse order +}; + inline Qt::Orientation oppositeOrientation(Qt::Orientation o) { return o == Qt::Vertical ? Qt::Horizontal : Qt::Vertical; @@ -469,7 +475,8 @@ public: /// The neighbours at the left/top of the item, will be shrunk by @p side1Amount, while the items /// at right/bottom will be shrunk by @p side2Amount. /// Squeezes all the neighbours (not just the immediate ones). - void shrinkNeighbours(int index, SizingInfo::List &sizes, int side1Amount, int side2Amount); + void shrinkNeighbours(int index, SizingInfo::List &sizes, int side1Amount, int side2Amount, + NeighbourSqueezeStrategy = NeighbourSqueezeStrategy::Equally); Item *visibleNeighbourFor(const Item *item, Side side) const; QSize availableSize() const; @@ -484,7 +491,9 @@ public: void onChildVisibleChanged(Item *child, bool visible); void updateSizeConstraints(); SizingInfo::List sizes(bool ignoreBeingInserted = false) const; - QVector calculateSqueezes(SizingInfo::List::ConstIterator begin, SizingInfo::List::ConstIterator end, int needed) const; + QVector calculateSqueezes(SizingInfo::List::ConstIterator begin, + SizingInfo::List::ConstIterator end, + int needed, NeighbourSqueezeStrategy) const; QRect suggestedDropRect(QSize minSize, const Item *relativeTo, Location) const; void positionItems(); void positionItems(SizingInfo::List &sizes); diff --git a/src/private/multisplitter/tests/tst_multisplitter.cpp b/src/private/multisplitter/tests/tst_multisplitter.cpp index efb82ecc..d9c6f411 100644 --- a/src/private/multisplitter/tests/tst_multisplitter.cpp +++ b/src/private/multisplitter/tests/tst_multisplitter.cpp @@ -198,6 +198,7 @@ private Q_SLOTS: void tst_insertHiddenContainer(); void tst_availableOnSide(); void tst_resizeViaSeparator(); + void tst_resizeViaSeparator2(); void tst_mapToRoot(); }; @@ -1178,6 +1179,54 @@ void TestMultiSplitter::tst_resizeViaSeparator() QCOMPARE(separator->position(), oldPos -delta); } +void TestMultiSplitter::tst_resizeViaSeparator2() +{ + // Here we resize one of the separators and make sure onyly the items next to the separator move + // propagation should only start when constraints have been met + + auto root = createRoot(); + auto item1 = createItem(); + auto item2 = createItem(); + auto item3 = createItem(); + auto item4 = createItem(); + + root->insertItem(item1, Location_OnLeft); + root->insertItem(item2, Location_OnRight); + root->insertItem(item3, Location_OnRight); + root->insertItem(item4, Location_OnRight); + + auto resizeChildrenTo1000px = [&root] { + /// Make sure each item has 1000 of width. Cheating here as we don't have API to resize all. + const int numChildren = root->numChildren(); + for (auto item : qAsConst(root->m_children)) { + item->m_sizingInfo.percentageWithinParent = 1.0 / numChildren; + } + root->setSize_recursive(QSize(4000 + Item::separatorThickness*(numChildren-1), 1000)); + }; + + const int delta = 100; + const int originalChildWidth = 1000; + resizeChildrenTo1000px(); + + const auto separators = root->separators_recursive(); + QVERIFY(root->checkSanity()); + QCOMPARE(separators.size(), 3); + + root->requestSeparatorMove(separators[1], delta); + + QCOMPARE(item1->width(), originalChildWidth); // item1 didn't change when we moved the second separator, only item2 and 3 are supposed to move + QCOMPARE(item2->width(), originalChildWidth + delta); + QCOMPARE(item3->width(), originalChildWidth - delta); + QCOMPARE(item4->width(), originalChildWidth); + + // And back + root->requestSeparatorMove(separators[1], -delta); + QCOMPARE(item1->width(), originalChildWidth); // item1 didn't change when we moved the second separator, only item2 and 3 are supposed to move + QCOMPARE(item2->width(), originalChildWidth); + QCOMPARE(item3->width(), originalChildWidth); + QCOMPARE(item4->width(), originalChildWidth); +} + void TestMultiSplitter::tst_mapToRoot() { auto root = createRoot();