Fix propagation of resize when dragging a separator

It was ok but was resizing children via the "percentage method",
and we only want to resize the children next to the separator.
This commit is contained in:
Sergio Martins
2020-05-10 15:07:42 +01:00
parent babed642eb
commit 925df72c0f
3 changed files with 103 additions and 19 deletions

View File

@@ -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<bool> 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<Separator *> ItemContainer::separators_recursive() const
return separators;
}
QVector<Separator *> ItemContainer::separators() const
{
return m_separators;
}
Separator *ItemContainer::neighbourSeparator(const Item *item, Side side, Qt::Orientation orientation) const
{
Item::List children = visibleChildren();

View File

@@ -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<Layouting::Separator*> separators_recursive() const;
QVector<Layouting::Separator*> separators() const;
Qt::Orientation m_orientation = Qt::Vertical;
private:
void resizeChildren(QSize oldSize, QSize newSize, SizingInfo::List &sizes, ChildrenResizeStrategy);

View File

@@ -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();