Use a better strategy for size propagation when resizing via separator
Behaves like old kddockwidgets now. When dragging a separator, only the immediate neighbours are resized. Only when min-sizes are violated that it propagates.
This commit is contained in:
@@ -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<int> ItemContainer::calculateSqueezes(SizingInfo::List::ConstIterator begin,
|
||||
SizingInfo::List::ConstIterator end, int needed) const
|
||||
SizingInfo::List::ConstIterator end, int needed,
|
||||
NeighbourSqueezeStrategy strategy) const
|
||||
{
|
||||
QVector<int> availabilities;
|
||||
for (auto it = begin; it < end; ++it) {
|
||||
@@ -2295,38 +2297,59 @@ QVector<int> ItemContainer::calculateSqueezes(SizingInfo::List::ConstIterator be
|
||||
|
||||
QVector<int> 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<int> squeezes = calculateSqueezes(begin, end, side1Amount);
|
||||
const QVector<int> 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<int> squeezes = calculateSqueezes(begin, end, side2Amount);
|
||||
const QVector<int> 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];
|
||||
|
||||
@@ -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<int> calculateSqueezes(SizingInfo::List::ConstIterator begin, SizingInfo::List::ConstIterator end, int needed) const;
|
||||
QVector<int> 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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user