From 933e74473a23c0090eaaae9c6122a0e0d192df15 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Tue, 22 Sep 2009 16:24:07 -0300 Subject: QGraphicsAnchorLayout: add autotests for QSizePolicy::ExpandFlag Add autotests that use the ExpandFlag via QSizePolicy::Expanding policy. Those tests cover the simple cases and behaviours with sequential and parallel anchor setups. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Jesus Sanchez-Palencia --- .../tst_qgraphicsanchorlayout.cpp | 214 +++++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 9f13aca..e898edb 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -69,6 +69,9 @@ private slots: void delete_anchor(); void conflicts(); void sizePolicy(); + void expandingSequence(); + void expandingSequenceFairDistribution(); + void expandingParallel(); }; class RectWidget : public QGraphicsWidget @@ -586,6 +589,20 @@ void tst_QGraphicsAnchorLayout::snake() QCOMPARE(b->geometry(), QRectF(90.0, 100.0, 10.0, 100.0)); QCOMPARE(c->geometry(), QRectF(90.0, 200.0, 100.0, 100.0)); QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(layoutHasConflict(l) == false); + + // Test QSizePolicy::ExpandFlag, it shouldn't change the extreme + // points of the layout... + b->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + QSizeF newLayoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF newLayoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF newLayoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, newLayoutMinimumSize); + QCOMPARE(layoutMaximumSize, newLayoutMaximumSize); + QCOMPARE(layoutPreferredSize, newLayoutPreferredSize); } void tst_QGraphicsAnchorLayout::snakeOppositeDirections() @@ -1308,5 +1325,202 @@ void tst_QGraphicsAnchorLayout::conflicts() delete p; } +void tst_QGraphicsAnchorLayout::expandingSequence() +{ + QSizeF min(10, 10); + QSizeF pref(50, 10); + QSizeF max(100, 10); + + QGraphicsWidget *a = createItem(min, pref, max, "a"); + QGraphicsWidget *b = createItem(min, pref, max, "b"); + + b->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + // horizontal + setAnchor(l, l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + setAnchor(l, b, Qt::AnchorRight, l, Qt::AnchorRight, 0); + + // vertical + l->addAnchors(l, a, Qt::Vertical); + l->addAnchors(l, b, Qt::Vertical); + + QCOMPARE(l->count(), 2); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QCOMPARE(layoutMinimumSize.width(), qreal(20)); + + QSizeF layoutExpandedSize(pref.width() + max.width(), layoutMinimumSize.height()); + p.resize(layoutExpandedSize); + + QEXPECT_FAIL("", "Support for QSizePolicy::ExpandFlag not yet available", Abort); + QCOMPARE(a->geometry().size(), pref); + QCOMPARE(b->geometry().size(), max); + + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QCOMPARE(layoutMaximumSize.width(), qreal(200)); +} + +void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() +{ + QSizeF min(10, 10); + QSizeF pref(50, 10); + QSizeF max(100, 10); + + QGraphicsWidget *a = createItem(min, pref, max, "a"); + QGraphicsWidget *b = createItem(min, pref, max, "b"); + QGraphicsWidget *c = createItem(min, pref, max, "c"); + QGraphicsWidget *d = createItem(min, pref, max, "d"); + + b->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + d->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + // horizontal + setAnchor(l, l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + setAnchor(l, b, Qt::AnchorRight, c, Qt::AnchorLeft, 0); + setAnchor(l, c, Qt::AnchorRight, d, Qt::AnchorLeft, 0); + setAnchor(l, d, Qt::AnchorRight, l, Qt::AnchorRight, 0); + + // vertical + l->addAnchors(l, a, Qt::Vertical); + l->addAnchors(l, b, Qt::Vertical); + l->addAnchors(l, c, Qt::Vertical); + l->addAnchors(l, d, Qt::Vertical); + + QCOMPARE(l->count(), 4); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QCOMPARE(layoutMinimumSize.width(), qreal(40)); + + QSizeF layoutPartialExpandedSize((2 * pref.width()) + (2 * (pref.width() + 10)), + layoutMinimumSize.height()); + p.resize(layoutPartialExpandedSize); + + QEXPECT_FAIL("", "Support for QSizePolicy::ExpandFlag not yet available", Abort); + QCOMPARE(a->geometry().size(), pref); + QCOMPARE(b->geometry().size(), pref + QSizeF(10, 0)); + QCOMPARE(c->geometry().size(), pref); + QCOMPARE(d->geometry().size(), pref + QSizeF(10, 0)); + + QSizeF layoutExpandedSize((2 * pref.width()) + (2 * max.width()), + layoutMinimumSize.height()); + p.resize(layoutExpandedSize); + + QCOMPARE(a->geometry().size(), pref); + QCOMPARE(b->geometry().size(), max); + QCOMPARE(c->geometry().size(), pref); + QCOMPARE(d->geometry().size(), max); + + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QCOMPARE(layoutMaximumSize.width(), qreal(400)); + + // Now we change D to have more "room for growth" from its preferred size + // to its maximum size. We expect a proportional fair distribution. Note that + // this seems to not conform with what QGraphicsLinearLayout does. + d->setMaximumSize(QSizeF(150, 10)); + + QSizeF newLayoutExpandedSize((2 * pref.width()) + (max.width() + 150), + layoutMinimumSize.height()); + p.resize(newLayoutExpandedSize); + + QCOMPARE(a->geometry().size(), pref); + QCOMPARE(b->geometry().size(), max); + QCOMPARE(c->geometry().size(), pref); + QCOMPARE(d->geometry().size(), QSizeF(150, 10)); + + QSizeF newLayoutPartialExpandedSize((4 * pref.width()) + 75, + layoutMinimumSize.height()); + + QCOMPARE(a->geometry().size(), pref); + QCOMPARE(b->geometry().size(), pref + QSizeF(25, 0)); + QCOMPARE(c->geometry().size(), pref); + QCOMPARE(d->geometry().size(), pref + QSizeF(50, 0)); +} + +void tst_QGraphicsAnchorLayout::expandingParallel() +{ + QSizeF min(10, 10); + QSizeF pref(50, 10); + QSizeF max(100, 50); + + QGraphicsWidget *a = createItem(min, pref, max, "a"); + QGraphicsWidget *b = createItem(min, pref, max, "b"); + QGraphicsWidget *c = createItem(min, pref, max, "c"); + + b->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + // horizontal + setAnchor(l, l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + setAnchor(l, l, Qt::AnchorLeft, b, Qt::AnchorLeft, 0); + + setAnchor(l, a, Qt::AnchorRight, c, Qt::AnchorLeft, 0); + setAnchor(l, b, Qt::AnchorRight, c, Qt::AnchorLeft, 0); + + setAnchor(l, c, Qt::AnchorRight, l, Qt::AnchorRight, 0); + + // vertical + l->addAnchors(l, c, Qt::Vertical); + setAnchor(l, l, Qt::AnchorTop, a, Qt::AnchorTop, 0); + setAnchor(l, a, Qt::AnchorBottom, c, Qt::AnchorVerticalCenter, 0); + setAnchor(l, b, Qt::AnchorTop, c, Qt::AnchorVerticalCenter, 0); + setAnchor(l, b, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + QCOMPARE(l->count(), 3); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QCOMPARE(layoutMinimumSize.width(), qreal(20)); + + QSizeF layoutExpandedSize(pref.width() + max.width(), layoutMinimumSize.height()); + p.resize(layoutExpandedSize); + + QEXPECT_FAIL("", "Support for QSizePolicy::ExpandFlag not yet available", Abort); + QCOMPARE(a->geometry().size(), max); + QCOMPARE(b->geometry().size(), max); + QCOMPARE(c->geometry().size(), pref); + + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QCOMPARE(layoutMaximumSize.width(), qreal(200)); + + // + // Change the parallel connection to a paralell connection of b with a center... + // + QGraphicsAnchor *anchor = l->anchor(b, Qt::AnchorRight, c, Qt::AnchorLeft); + delete anchor; + setAnchor(l, b, Qt::AnchorRight, a, Qt::AnchorHorizontalCenter, 0); + a->setMaximumSize(max + QSizeF(100, 0)); + + QSizeF newLayoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QCOMPARE(newLayoutMinimumSize.width(), qreal(30)); + + QSizeF newLayoutExpandedSize(pref.width() + max.width(), layoutMinimumSize.height()); + p.resize(newLayoutExpandedSize); + + QCOMPARE(a->geometry().size(), max); + QCOMPARE(b->geometry().size(), max); + QCOMPARE(c->geometry().size(), pref); + + QSizeF newLayoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QCOMPARE(newLayoutMaximumSize.width(), qreal(300)); +} + QTEST_MAIN(tst_QGraphicsAnchorLayout) #include "tst_qgraphicsanchorlayout.moc" -- cgit v0.12 From bfc1a75616e1c96a018349084fae844d7efbc177 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Tue, 22 Sep 2009 14:20:10 -0300 Subject: QGraphicsAnchorLayout: Add data structures for Expanding size policy Adding required members for the upcoming support of the QSizePolicy::Expand flag. In this state, running with simplification will probably not work. Signed-off-by: Eduardo M. Fleury Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 44 +++++++++++++++++++----- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 20 ++++++++--- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index f75118b..fc3bc8a 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -94,7 +94,9 @@ qreal QGraphicsAnchorPrivate::spacing() const void AnchorData::refreshSizeHints(qreal effectiveSpacing) { - if (!isLayoutAnchor && from->m_item == to->m_item) { + isExpanding = 0; + + if (!isLayoutAnchor && (from->m_item == to->m_item)) { QGraphicsLayoutItem *item = from->m_item; const QGraphicsAnchorLayoutPrivate::Orientation orient = QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge); @@ -138,6 +140,9 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) if (policy & QSizePolicy::IgnoreFlag) prefSize = minSize; + if (policy & QSizePolicy::ExpandFlag) + isExpanding = 1; + bool hasCenter = (from->m_edge == centerEdge || to->m_edge == centerEdge); if (hasCenter) { @@ -155,6 +160,7 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) // recalculate and override the values we set here. sizeAtMinimum = prefSize; sizeAtPreferred = prefSize; + sizeAtExpanding = prefSize; sizeAtMaximum = prefSize; } else if (!hasSize) { @@ -165,6 +171,7 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) sizeAtMinimum = prefSize; sizeAtPreferred = prefSize; + sizeAtExpanding = prefSize; sizeAtMaximum = prefSize; } } @@ -357,6 +364,12 @@ QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate() : calculateGraphCacheDirty(1) { for (int i = 0; i < NOrientations; ++i) { + for (int j = 0; j < 3; ++j) { + sizeHints[i][j] = -1; + } + sizeAtExpanding[i] = -1; + interpolationProgress[i] = -1; + spacings[i] = -1; graphSimplified[i] = false; graphHasConflicts[i] = false; @@ -1649,6 +1662,10 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( sizeHints[orientation][Qt::PreferredSize] = pref; sizeHints[orientation][Qt::MaximumSize] = max; + // XXX implement Expanding simplex + for (int i = 0; i < trunkVariables.count(); ++i) + trunkVariables.at(i)->sizeAtExpanding = trunkVariables.at(i)->sizeAtPreferred; + sizeAtExpanding[orientation] = pref; } } else { #if 0 @@ -1664,6 +1681,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( AnchorData *ad = trunkPath.positives.toList()[0]; ad->sizeAtMinimum = ad->minSize; ad->sizeAtPreferred = ad->prefSize; + ad->sizeAtExpanding = ad->prefSize; ad->sizeAtMaximum = ad->maxSize; // Propagate @@ -1672,6 +1690,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum; sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred; sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum; + sizeAtExpanding[orientation] = ad->sizeAtPreferred; } // Delete the constraints, we won't use them anymore. @@ -1701,6 +1720,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( AnchorData *ad = partVariables[j]; Q_ASSERT(ad); ad->sizeAtMinimum = ad->sizeAtPreferred; + ad->sizeAtExpanding = ad->sizeAtPreferred; ad->sizeAtMaximum = ad->sizeAtPreferred; ad->updateChildrenSizes(); } @@ -2058,9 +2078,13 @@ void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( interpolationInterval[orientation] = MinToPreferred; lower = sizeHints[orientation][Qt::MinimumSize]; upper = sizeHints[orientation][Qt::PreferredSize]; - } else { - interpolationInterval[orientation] = PreferredToMax; + } else if (current < sizeAtExpanding[orientation]) { + interpolationInterval[orientation] = PreferredToExpanding; lower = sizeHints[orientation][Qt::PreferredSize]; + upper = sizeAtExpanding[orientation]; + } else { + interpolationInterval[orientation] = ExpandingToMax; + lower = sizeAtExpanding[orientation]; upper = sizeHints[orientation][Qt::MaximumSize]; } @@ -2075,11 +2099,12 @@ void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( \internal Calculate the current Edge size based on the current Layout size and the - size the edge is supposed to have when: + size the edge is supposed to have when the layout is at its: - - the layout is at its minimum size. - - the layout is at its preferred size. - - the layout is at its maximum size. + - minimum size, + - preferred size, + - size when all expanding anchors are expanded, + - maximum size. These three key values are calculated in advance using linear programming (more expensive) or the simplification algorithm, then @@ -2099,8 +2124,11 @@ void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, if (interpolationInterval[orientation] == MinToPreferred) { lower = edge->sizeAtMinimum; upper = edge->sizeAtPreferred; - } else { + } else if (interpolationInterval[orientation] == PreferredToExpanding) { lower = edge->sizeAtPreferred; + upper = edge->sizeAtExpanding; + } else { + lower = edge->sizeAtExpanding; upper = edge->sizeAtMaximum; } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index c86bfa3..4c6c2aa 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -153,7 +153,8 @@ struct AnchorData : public QSimplexVariable { : QSimplexVariable(), from(0), to(0), minSize(minimumSize), prefSize(preferredSize), maxSize(maximumSize), sizeAtMinimum(preferredSize), - sizeAtPreferred(preferredSize), sizeAtMaximum(preferredSize), + sizeAtPreferred(preferredSize), sizeAtExpanding(preferredSize), + sizeAtMaximum(preferredSize), graphicsAnchor(0), skipInPreferred(0), type(Normal), hasSize(true), isLayoutAnchor(false) {} @@ -161,7 +162,8 @@ struct AnchorData : public QSimplexVariable { AnchorData(qreal size) : QSimplexVariable(), from(0), to(0), minSize(size), prefSize(size), maxSize(size), - sizeAtMinimum(size), sizeAtPreferred(size), sizeAtMaximum(size), + sizeAtMinimum(size), sizeAtPreferred(size), sizeAtExpanding(size), + sizeAtMaximum(size), graphicsAnchor(0), skipInPreferred(0), type(Normal), hasSize(true), isLayoutAnchor(false) {} @@ -169,7 +171,8 @@ struct AnchorData : public QSimplexVariable { AnchorData() : QSimplexVariable(), from(0), to(0), minSize(0), prefSize(0), maxSize(0), - sizeAtMinimum(0), sizeAtPreferred(0), sizeAtMaximum(0), + sizeAtMinimum(0), sizeAtPreferred(0), sizeAtExpanding(0), + sizeAtMaximum(0), graphicsAnchor(0), skipInPreferred(0), type(Normal), hasSize(false), isLayoutAnchor(false) {} @@ -192,6 +195,7 @@ struct AnchorData : public QSimplexVariable { maxSize = size; sizeAtMinimum = size; sizeAtPreferred = size; + sizeAtExpanding = size; sizeAtMaximum = size; hasSize = true; } @@ -218,6 +222,7 @@ struct AnchorData : public QSimplexVariable { // calculated by the Simplex solver based on the current layout setup. qreal sizeAtMinimum; qreal sizeAtPreferred; + qreal sizeAtExpanding; qreal sizeAtMaximum; QGraphicsAnchor *graphicsAnchor; @@ -225,12 +230,15 @@ struct AnchorData : public QSimplexVariable { uint type : 2; // either Normal, Sequential or Parallel uint hasSize : 1; // if false, get size from style. uint isLayoutAnchor : 1; // if this anchor is connected to a layout 'edge' + uint isExpanding : 1; // if true, expands before other anchors + protected: AnchorData(Type type, qreal size = 0) : QSimplexVariable(), from(0), to(0), minSize(size), prefSize(size), maxSize(size), sizeAtMinimum(size), - sizeAtPreferred(size), sizeAtMaximum(size), + sizeAtPreferred(size), sizeAtExpanding(size), + sizeAtMaximum(size), graphicsAnchor(0), skipInPreferred(0), type(type), hasSize(true), isLayoutAnchor(false) {} @@ -355,7 +363,8 @@ public: // Interval represents which interpolation interval are we operating in. enum Interval { MinToPreferred = 0, - PreferredToMax + PreferredToExpanding, + ExpandingToMax }; // Several structures internal to the layout are duplicated to handle @@ -496,6 +505,7 @@ public: qreal spacings[NOrientations]; // Size hints from simplex engine qreal sizeHints[2][3]; + qreal sizeAtExpanding[2]; // Items QVector items; -- cgit v0.12 From 1728f846d9fb53a699782005d6478c0f23f9b82e Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Wed, 23 Sep 2009 16:02:35 -0300 Subject: QGraphicsAnchorLayout: Enabling Simplex support for Expanding size policy To support the expanding size policy we had to change the way the setup process work. Previously we used to calculate three key-frames using the simplex solver: - sizeAtMinimum: the value an anchor should be in when the layout is at its minimum size. Calculated using simplex solver to minimize the layout size. - sizeAtPreferred: the value an anchor should be in when the layout is at its preferred size. Calculated using simplex solver to minimize the deviation from the items preferred sizes. - sizeAtMaximum: the value an anchor should be in when the layout is at its maximum size. Calculated using simplex solver to maximize the layout size. That worked fine but didn't diferentiate standard items from the "expanding" ones. In other words, all items would grow from their sizeAtPreferred to their sizeAtMaximum at the same rate. To support the idea of "expanding" items, ie. items that should grow faster than the others, we added a fourth state, between preferred and maximum. Now we have the following interpolation order: sizeAtMinimum -> sizeAtPreferred -> sizeAtExpanding -> sizeAtMaximum. The definition of the "expanding" state is that all "expanding" items should have grown all the way to their "sizeAtMaximum" values whereas non expanding items should have kept their preferred sizes. The only exception is that non-expanding items are allowed to grow if that is necessary to allow the "expanding" items to reach their sizeAtMaximum. So, the visual result is that if the layout is resized from its preferred size to its maximum size, the expanding items will grow first and then, in a second phase, the other items will grow. This commit adds QGALPrivate::solveExpanding() and calls it from calculateGraphs(). Signed-off-by: Eduardo M. Fleury Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 98 +++++++++++++++++++++--- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 1 + 2 files changed, 88 insertions(+), 11 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index fc3bc8a..93ccf83 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1635,11 +1635,18 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( qreal min, max; feasible = solveMinMax(trunkConstraints, trunkPath, &min, &max); - // Solve for preferred. The objective function is calculated from the constraints - // and variables internally. - feasible &= solvePreferred(trunkConstraints); - if (feasible) { + // Solve for preferred. The objective function is calculated from the constraints + // and variables internally. + solvePreferred(trunkConstraints); + + // remove sizeHintConstraints from trunkConstraints + trunkConstraints = parts[0]; + + // Solve for expanding. The objective function and the constraints from items + // are calculated internally. + solveExpanding(trunkConstraints); + // Propagate the new sizes down the simplified graph, ie. tell the // group anchors to set their children anchors sizes. @@ -1649,23 +1656,23 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( for (int i = 0; i < trunkVariables.count(); ++i) trunkVariables.at(i)->updateChildrenSizes(); - // Calculate and set the preferred size for the layout from the edge sizes that - // were calculated above. + // Calculate and set the preferred and expanding sizes for the layout, + // from the edge sizes that were calculated above. qreal pref(0.0); + qreal expanding(0.0); foreach (const AnchorData *ad, trunkPath.positives) { pref += ad->sizeAtPreferred; + expanding += ad->sizeAtExpanding; } foreach (const AnchorData *ad, trunkPath.negatives) { pref -= ad->sizeAtPreferred; + expanding -= ad->sizeAtExpanding; } + sizeHints[orientation][Qt::MinimumSize] = min; sizeHints[orientation][Qt::PreferredSize] = pref; sizeHints[orientation][Qt::MaximumSize] = max; - - // XXX implement Expanding simplex - for (int i = 0; i < trunkVariables.count(); ++i) - trunkVariables.at(i)->sizeAtExpanding = trunkVariables.at(i)->sizeAtPreferred; - sizeAtExpanding[orientation] = pref; + sizeAtExpanding[orientation] = expanding; } } else { #if 0 @@ -2315,6 +2322,75 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList co return feasible; } +void QGraphicsAnchorLayoutPrivate::solveExpanding(QList constraints) +{ + QList variables = getVariables(constraints); + QList itemConstraints; + QSimplexConstraint *objective = new QSimplexConstraint; + + // Use all items that belong to trunk to: + // - add solveExpanding-specific item constraints + // - create the objective function + for (int i = 0; i < variables.size(); ++i) { + AnchorData *ad = variables[i]; + if (ad->isExpanding) { + // Add constraint to lock expanding anchor in its sizeAtMaximum + QSimplexConstraint *itemC = new QSimplexConstraint; + itemC->ratio = QSimplexConstraint::Equal; + itemC->variables.insert(ad, 1.0); + itemC->constant = ad->sizeAtMaximum; + itemConstraints << itemC; + } else { + // Add constraints to lock anchor between their sizeAtPreferred and sizeAtMaximum + QSimplexConstraint *itemC = new QSimplexConstraint; + itemC->ratio = QSimplexConstraint::MoreOrEqual; + itemC->variables.insert(ad, 1.0); + itemC->constant = qMin(ad->sizeAtPreferred, ad->sizeAtMaximum); + itemConstraints << itemC; + + itemC = new QSimplexConstraint; + itemC->ratio = QSimplexConstraint::LessOrEqual; + itemC->variables.insert(ad, 1.0); + itemC->constant = qMax(ad->sizeAtPreferred, ad->sizeAtMaximum); + itemConstraints << itemC; + + // Add anchor to objective function + if (ad->sizeAtPreferred < ad->sizeAtMaximum) { + // Try to shrink this variable towards its sizeAtPreferred value + objective->variables.insert(ad, 1.0); + } else { + // Try to grow this variable towards its sizeAtPreferred value + objective->variables.insert(ad, -1.0); + } + } + } + + // Solve + if (objective->variables.size() == variables.size()) { + // If no anchors are expanding, we don't need to run the simplex + // Set all variables to their preferred size + for (int i = 0; i < variables.size(); ++i) { + variables[i]->sizeAtExpanding = variables[i]->sizeAtPreferred; + } + } else { + // Run simplex + QSimplex simplex; + bool feasible = simplex.setConstraints(constraints + itemConstraints); + Q_ASSERT(feasible); + + simplex.setObjective(objective); + simplex.solveMin(); + + // Collect results + for (int i = 0; i < variables.size(); ++i) { + variables[i]->sizeAtExpanding = variables[i]->result; + } + } + + delete objective; + qDeleteAll(itemConstraints); +} + /*! \internal Returns true if there are no arrangement that satisfies all constraints. diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 4c6c2aa..47df786 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -495,6 +495,7 @@ public: bool solveMinMax(QList constraints, GraphPath path, qreal *min, qreal *max); bool solvePreferred(QList constraints); + void solveExpanding(QList constraints); bool hasConflicts() const; #ifdef QT_DEBUG -- cgit v0.12 From e210bea72ef65f99babb4c6f5bd442184c671d3b Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 24 Sep 2009 11:16:16 -0300 Subject: QGraphicsAnchorLayout Tests: Enable expanding tests Removing QEXPECT_FAIL and making minor corrections on expected values. Signed-off-by: Eduardo M. Fleury Reviewed-by: Artur Duque de Souza --- .../qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index e898edb..4ad48c7 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -1359,7 +1359,6 @@ void tst_QGraphicsAnchorLayout::expandingSequence() QSizeF layoutExpandedSize(pref.width() + max.width(), layoutMinimumSize.height()); p.resize(layoutExpandedSize); - QEXPECT_FAIL("", "Support for QSizePolicy::ExpandFlag not yet available", Abort); QCOMPARE(a->geometry().size(), pref); QCOMPARE(b->geometry().size(), max); @@ -1409,7 +1408,6 @@ void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() layoutMinimumSize.height()); p.resize(layoutPartialExpandedSize); - QEXPECT_FAIL("", "Support for QSizePolicy::ExpandFlag not yet available", Abort); QCOMPARE(a->geometry().size(), pref); QCOMPARE(b->geometry().size(), pref + QSizeF(10, 0)); QCOMPARE(c->geometry().size(), pref); @@ -1443,6 +1441,7 @@ void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() QSizeF newLayoutPartialExpandedSize((4 * pref.width()) + 75, layoutMinimumSize.height()); + p.resize(newLayoutPartialExpandedSize); QCOMPARE(a->geometry().size(), pref); QCOMPARE(b->geometry().size(), pref + QSizeF(25, 0)); @@ -1454,11 +1453,12 @@ void tst_QGraphicsAnchorLayout::expandingParallel() { QSizeF min(10, 10); QSizeF pref(50, 10); - QSizeF max(100, 50); + QSizeF max(100, 10); + QSizeF max2(100, 50); QGraphicsWidget *a = createItem(min, pref, max, "a"); QGraphicsWidget *b = createItem(min, pref, max, "b"); - QGraphicsWidget *c = createItem(min, pref, max, "c"); + QGraphicsWidget *c = createItem(min, pref, max2, "c"); b->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); @@ -1492,10 +1492,9 @@ void tst_QGraphicsAnchorLayout::expandingParallel() QSizeF layoutExpandedSize(pref.width() + max.width(), layoutMinimumSize.height()); p.resize(layoutExpandedSize); - QEXPECT_FAIL("", "Support for QSizePolicy::ExpandFlag not yet available", Abort); QCOMPARE(a->geometry().size(), max); QCOMPARE(b->geometry().size(), max); - QCOMPARE(c->geometry().size(), pref); + QCOMPARE(c->geometry().size(), QSizeF(pref.width(), 20)); QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize.width(), qreal(200)); @@ -1511,12 +1510,12 @@ void tst_QGraphicsAnchorLayout::expandingParallel() QSizeF newLayoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); QCOMPARE(newLayoutMinimumSize.width(), qreal(30)); - QSizeF newLayoutExpandedSize(pref.width() + max.width(), layoutMinimumSize.height()); + QSizeF newLayoutExpandedSize = layoutExpandedSize + QSizeF(100, 0); p.resize(newLayoutExpandedSize); - QCOMPARE(a->geometry().size(), max); + QCOMPARE(a->geometry().size(), max + QSizeF(100, 0)); QCOMPARE(b->geometry().size(), max); - QCOMPARE(c->geometry().size(), pref); + QCOMPARE(c->geometry().size(), QSizeF(pref.width(), 20)); QSizeF newLayoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(newLayoutMaximumSize.width(), qreal(300)); -- cgit v0.12 From 5dd77be3acfb53b44d7d6b0015af0c33124ab84a Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 24 Sep 2009 17:09:16 -0300 Subject: QGraphicsAnchorLayout: cleaning up AnchorData Use just one constructor for AnchorData and tweak the struct when necessary. Also use the function refreshSizeHints() for Normal anchors to get the item information instead of passing to the constructor. This has the advantage that we don't need to replicate the code for checking and updating the size hint and size policies. Note that the code before only considered the size policies after the first refreshSizeHints() -- which we know will be called after the simplification, so the assumption seems to be correct, but we don't depend on that anymore.. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 89 ++++++++++++------------ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 53 ++++---------- 2 files changed, 59 insertions(+), 83 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 93ccf83..dc8d211 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -94,8 +94,6 @@ qreal QGraphicsAnchorPrivate::spacing() const void AnchorData::refreshSizeHints(qreal effectiveSpacing) { - isExpanding = 0; - if (!isLayoutAnchor && (from->m_item == to->m_item)) { QGraphicsLayoutItem *item = from->m_item; @@ -140,9 +138,6 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) if (policy & QSizePolicy::IgnoreFlag) prefSize = minSize; - if (policy & QSizePolicy::ExpandFlag) - isExpanding = 1; - bool hasCenter = (from->m_edge == centerEdge || to->m_edge == centerEdge); if (hasCenter) { @@ -151,6 +146,14 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) maxSize /= 2; } + if (policy & QSizePolicy::ExpandFlag) { + isExpanding = 1; + expSize = maxSize; + } else { + isExpanding = 0; + expSize = prefSize; + } + // Set the anchor effective sizes to preferred. // // Note: The idea here is that all items should remain at their @@ -167,8 +170,11 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) // Anchor has no size defined, use given default information minSize = effectiveSpacing; prefSize = effectiveSpacing; + expSize = effectiveSpacing; maxSize = effectiveSpacing; + isExpanding = 0; + sizeAtMinimum = prefSize; sizeAtPreferred = prefSize; sizeAtExpanding = prefSize; @@ -830,9 +836,10 @@ void QGraphicsAnchorLayoutPrivate::createLayoutEdges() QGraphicsLayoutItem *layout = q; // Horizontal - AnchorData *data = new AnchorData(0, 0, QWIDGETSIZE_MAX); + AnchorData *data = new AnchorData; addAnchor_helper(layout, Qt::AnchorLeft, layout, - Qt::AnchorRight, data); + Qt::AnchorRight, data); + data->maxSize = QWIDGETSIZE_MAX; data->skipInPreferred = 1; // Set the Layout Left edge as the root of the horizontal graph. @@ -840,9 +847,10 @@ void QGraphicsAnchorLayoutPrivate::createLayoutEdges() graph[Horizontal].setRootVertex(v); // Vertical - data = new AnchorData(0, 0, QWIDGETSIZE_MAX); + data = new AnchorData; addAnchor_helper(layout, Qt::AnchorTop, layout, - Qt::AnchorBottom, data); + Qt::AnchorBottom, data); + data->maxSize = QWIDGETSIZE_MAX; data->skipInPreferred = 1; // Set the Layout Top edge as the root of the vertical graph. @@ -869,19 +877,15 @@ void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) items.append(item); - QSizeF minSize = item->effectiveSizeHint(Qt::MinimumSize); - QSizeF prefSize = item->effectiveSizeHint(Qt::PreferredSize); - QSizeF maxSize = item->effectiveSizeHint(Qt::MaximumSize); - - // Horizontal - AnchorData *data = new AnchorData(minSize.width(), prefSize.width(), maxSize.width()); - addAnchor_helper(item, Qt::AnchorLeft, item, - Qt::AnchorRight, data); + // Create horizontal and vertical internal anchors for the item and + // refresh its size hint / policy values. + AnchorData *data = new AnchorData; + addAnchor_helper(item, Qt::AnchorLeft, item, Qt::AnchorRight, data); + data->refreshSizeHints(0); // 0 = effectiveSpacing, will not be used - // Vertical - data = new AnchorData(minSize.height(), prefSize.height(), maxSize.height()); - addAnchor_helper(item, Qt::AnchorTop, item, - Qt::AnchorBottom, data); + data = new AnchorData; + addAnchor_helper(item, Qt::AnchorTop, item, Qt::AnchorBottom, data); + data->refreshSizeHints(0); // 0 = effectiveSpacing, will not be used } /*! @@ -934,20 +938,17 @@ void QGraphicsAnchorLayoutPrivate::createCenterAnchors( Q_ASSERT(first && last); // Create new anchors - AnchorData *oldData = graph[orientation].edgeData(first, last); - - qreal minimumSize = oldData->minSize / 2; - qreal preferredSize = oldData->prefSize / 2; - qreal maximumSize = oldData->maxSize / 2; - QSimplexConstraint *c = new QSimplexConstraint; - AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); + + AnchorData *data = new AnchorData; c->variables.insert(data, 1.0); addAnchor_helper(item, firstEdge, item, centerEdge, data); + data->refreshSizeHints(0); - data = new AnchorData(minimumSize, preferredSize, maximumSize); + data = new AnchorData; c->variables.insert(data, -1.0); addAnchor_helper(item, centerEdge, item, lastEdge, data); + data->refreshSizeHints(0); itemCenterConstraints[orientation].append(c); @@ -1008,14 +1009,9 @@ void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( if (substitute) { // Create the new anchor that should substitute the left-center-right anchors. - AnchorData *oldData = g.edgeData(first, center); - - qreal minimumSize = oldData->minSize * 2; - qreal preferredSize = oldData->prefSize * 2; - qreal maximumSize = oldData->maxSize * 2; - - AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); + AnchorData *data = new AnchorData; addAnchor_helper(item, firstEdge, item, lastEdge, data); + data->refreshSizeHints(0); // Remove old anchors removeAnchor_helper(first, center); @@ -1133,7 +1129,7 @@ QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *fi // Use heuristics to find out what the user meant with this anchor. correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge); - AnchorData *data; + AnchorData *data = new AnchorData; if (!spacing) { // If firstItem or secondItem is the layout itself, the spacing will default to 0. // Otherwise, the following matrix is used (questionmark means that the spacing @@ -1143,22 +1139,25 @@ QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *fi // Left 0 0 ? // HCenter 0 0 0 // Right ? 0 0 - if (firstItem != q - && secondItem != q - && pickEdge(firstEdge, Horizontal) != Qt::AnchorHorizontalCenter - && oppositeEdge(firstEdge) == secondEdge) { - data = new AnchorData; // ask the style later + if (firstItem == q + || secondItem == q + || pickEdge(firstEdge, Horizontal) == Qt::AnchorHorizontalCenter + || oppositeEdge(firstEdge) != secondEdge) { + data->setFixedSize(0); } else { - data = new AnchorData(0); // spacing should be 0 + data->unsetSize(); } addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data); + } else if (*spacing >= 0) { - data = new AnchorData(*spacing); + data->setFixedSize(*spacing); addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data); + } else { - data = new AnchorData(-*spacing); + data->setFixedSize(-*spacing); addAnchor_helper(secondItem, secondEdge, firstItem, firstEdge, data); } + return acquireGraphicsAnchor(data); } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 47df786..047be39 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -149,33 +149,15 @@ struct AnchorData : public QSimplexVariable { Sequential, Parallel }; - AnchorData(qreal minimumSize, qreal preferredSize, qreal maximumSize) - : QSimplexVariable(), from(0), to(0), - minSize(minimumSize), prefSize(preferredSize), - maxSize(maximumSize), sizeAtMinimum(preferredSize), - sizeAtPreferred(preferredSize), sizeAtExpanding(preferredSize), - sizeAtMaximum(preferredSize), - graphicsAnchor(0), - skipInPreferred(0), type(Normal), hasSize(true), - isLayoutAnchor(false) {} - - AnchorData(qreal size) - : QSimplexVariable(), from(0), to(0), - minSize(size), prefSize(size), maxSize(size), - sizeAtMinimum(size), sizeAtPreferred(size), sizeAtExpanding(size), - sizeAtMaximum(size), - graphicsAnchor(0), - skipInPreferred(0), type(Normal), hasSize(true), - isLayoutAnchor(false) {} AnchorData() : QSimplexVariable(), from(0), to(0), - minSize(0), prefSize(0), maxSize(0), - sizeAtMinimum(0), sizeAtPreferred(0), sizeAtExpanding(0), - sizeAtMaximum(0), - graphicsAnchor(0), - skipInPreferred(0), type(Normal), hasSize(false), - isLayoutAnchor(false) {} + minSize(0), prefSize(0), expSize(0), maxSize(0), + sizeAtMinimum(0), sizeAtPreferred(0), + sizeAtExpanding(0), sizeAtMaximum(0), + graphicsAnchor(0), skipInPreferred(0), + type(Normal), hasSize(true), isLayoutAnchor(false), + isExpanding(false) {} virtual void updateChildrenSizes() {} virtual void refreshSizeHints(qreal effectiveSpacing); @@ -192,6 +174,7 @@ struct AnchorData : public QSimplexVariable { { minSize = size; prefSize = size; + expSize = size; maxSize = size; sizeAtMinimum = size; sizeAtPreferred = size; @@ -215,6 +198,7 @@ struct AnchorData : public QSimplexVariable { // size. qreal minSize; qreal prefSize; + qreal expSize; qreal maxSize; // These attributes define which sizes should that anchor be in when the @@ -231,17 +215,6 @@ struct AnchorData : public QSimplexVariable { uint hasSize : 1; // if false, get size from style. uint isLayoutAnchor : 1; // if this anchor is connected to a layout 'edge' uint isExpanding : 1; // if true, expands before other anchors - -protected: - AnchorData(Type type, qreal size = 0) - : QSimplexVariable(), from(0), to(0), - minSize(size), prefSize(size), - maxSize(size), sizeAtMinimum(size), - sizeAtPreferred(size), sizeAtExpanding(size), - sizeAtMaximum(size), - graphicsAnchor(0), - skipInPreferred(0), type(type), hasSize(true), - isLayoutAnchor(false) {} }; #ifdef QT_DEBUG @@ -253,8 +226,10 @@ inline QString AnchorData::toString() const struct SequentialAnchorData : public AnchorData { - SequentialAnchorData() : AnchorData(AnchorData::Sequential) + SequentialAnchorData() : AnchorData() { + type = AnchorData::Sequential; + hasSize = true; #ifdef QT_DEBUG name = QLatin1String("SequentialAnchorData"); #endif @@ -278,9 +253,11 @@ struct SequentialAnchorData : public AnchorData struct ParallelAnchorData : public AnchorData { ParallelAnchorData(AnchorData *first, AnchorData *second) - : AnchorData(AnchorData::Parallel), - firstEdge(first), secondEdge(second) + : AnchorData(), firstEdge(first), secondEdge(second) { + type = AnchorData::Parallel; + hasSize = true; + // ### Those asserts force that both child anchors have the same direction, // but can't we simplify a pair of anchors in opposite directions? Q_ASSERT(first->from == second->from); -- cgit v0.12 From bcd08ab82b5df13f7687a504f868cb8b6ff8b75e Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Fri, 25 Sep 2009 16:13:18 -0300 Subject: QGraphicsAnchorLayout: avoid code duplication when initializing complex anchors Now we can use refreshSizeHints_helper() to initialize complex anchors (parallel and sequential). This avoids duplication of code in the function simplifySequentialChunk(). This commit also 'disable' ExpandFlag temporarily, stabilizing the tests for the simplification. The next commit should re-enable it implementing the necessary code for simplification as well. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 102 +++++++++++------------ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 4 + 2 files changed, 54 insertions(+), 52 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index dc8d211..dd46722 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -146,13 +146,15 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) maxSize /= 2; } - if (policy & QSizePolicy::ExpandFlag) { - isExpanding = 1; - expSize = maxSize; - } else { - isExpanding = 0; - expSize = prefSize; - } + // ### + expSize = prefSize; + // if (policy & QSizePolicy::ExpandFlag) { + // isExpanding = 1; + // expSize = maxSize; + // } else { + // isExpanding = 0; + // expSize = prefSize; + // } // Set the anchor effective sizes to preferred. // @@ -187,6 +189,7 @@ void ParallelAnchorData::updateChildrenSizes() firstEdge->sizeAtMinimum = secondEdge->sizeAtMinimum = sizeAtMinimum; firstEdge->sizeAtPreferred = secondEdge->sizeAtPreferred = sizeAtPreferred; firstEdge->sizeAtMaximum = secondEdge->sizeAtMaximum = sizeAtMaximum; + firstEdge->sizeAtExpanding = secondEdge->sizeAtExpanding = sizeAtPreferred; firstEdge->updateChildrenSizes(); secondEdge->updateChildrenSizes(); @@ -194,9 +197,16 @@ void ParallelAnchorData::updateChildrenSizes() void ParallelAnchorData::refreshSizeHints(qreal effectiveSpacing) { - // First refresh children information - firstEdge->refreshSizeHints(effectiveSpacing); - secondEdge->refreshSizeHints(effectiveSpacing); + refreshSizeHints_helper(effectiveSpacing); +} + +void ParallelAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, + bool refreshChildren) +{ + if (refreshChildren) { + firstEdge->refreshSizeHints(effectiveSpacing); + secondEdge->refreshSizeHints(effectiveSpacing); + } // ### should we warn if the parallel connection is invalid? // e.g. 1-2-3 with 10-20-30, the minimum of the latter is @@ -208,9 +218,13 @@ void ParallelAnchorData::refreshSizeHints(qreal effectiveSpacing) prefSize = qMax(firstEdge->prefSize, secondEdge->prefSize); prefSize = qMin(prefSize, maxSize); + // ### + expSize = prefSize; + // See comment in AnchorData::refreshSizeHints() about sizeAt* values sizeAtMinimum = prefSize; sizeAtPreferred = prefSize; + sizeAtExpanding = prefSize; sizeAtMaximum = prefSize; } @@ -277,30 +291,46 @@ void SequentialAnchorData::updateChildrenSizes() bandSize = maxFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; e->sizeAtMaximum = e->prefSize + bandSize * maxFactor; + // ### + e->sizeAtExpanding = e->sizeAtPreferred; + e->updateChildrenSizes(); } } void SequentialAnchorData::refreshSizeHints(qreal effectiveSpacing) { + refreshSizeHints_helper(effectiveSpacing); +} + +void SequentialAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, + bool refreshChildren) +{ minSize = 0; prefSize = 0; + expSize = 0; maxSize = 0; for (int i = 0; i < m_edges.count(); ++i) { AnchorData *edge = m_edges.at(i); - // First refresh children information - edge->refreshSizeHints(effectiveSpacing); + // If it's the case refresh children information first + if (refreshChildren) + edge->refreshSizeHints(effectiveSpacing); minSize += edge->minSize; prefSize += edge->prefSize; + expSize += edge->expSize; maxSize += edge->maxSize; } + // ### + expSize = prefSize; + // See comment in AnchorData::refreshSizeHints() about sizeAt* values sizeAtMinimum = prefSize; sizeAtPreferred = prefSize; + sizeAtExpanding = prefSize; sizeAtMaximum = prefSize; } @@ -441,39 +471,21 @@ static bool simplifySequentialChunk(Graph *graph, qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString())); #endif - qreal min = 0; - qreal pref = 0; - qreal max = 0; - SequentialAnchorData *sequence = new SequentialAnchorData; AnchorVertex *prev = before; AnchorData *data; for (i = 0; i <= vertices.count(); ++i) { AnchorVertex *next = (i < vertices.count()) ? vertices.at(i) : after; data = graph->takeEdge(prev, next); - min += data->minSize; - pref += data->prefSize; - max = checkAdd(max, data->maxSize); sequence->m_edges.append(data); prev = next; } - - // insert new - sequence->minSize = min; - sequence->prefSize = pref; - sequence->maxSize = max; - - // Unless these values are overhidden by the simplex solver later-on, - // anchors will keep their own preferred size. - sequence->sizeAtMinimum = pref; - sequence->sizeAtPreferred = pref; - sequence->sizeAtMaximum = pref; - sequence->setVertices(vertices); - sequence->from = before; sequence->to = after; + sequence->refreshSizeHints_helper(0, false); + // data here is the last edge in the sequence // ### this seems to be here for supporting reverse order sequences, // but doesnt seem to be used right now @@ -488,25 +500,11 @@ static bool simplifySequentialChunk(Graph *graph, AnchorData *newAnchor = sequence; if (AnchorData *oldAnchor = graph->takeEdge(before, after)) { - newAnchor = new ParallelAnchorData(oldAnchor, sequence); - - newAnchor->isLayoutAnchor = (oldAnchor->isLayoutAnchor + ParallelAnchorData *parallel = new ParallelAnchorData(oldAnchor, sequence); + parallel->isLayoutAnchor = (oldAnchor->isLayoutAnchor || sequence->isLayoutAnchor); - - min = qMax(oldAnchor->minSize, sequence->minSize); - max = qMin(oldAnchor->maxSize, sequence->maxSize); - - pref = qMax(oldAnchor->prefSize, sequence->prefSize); - pref = qMin(pref, max); - - newAnchor->minSize = min; - newAnchor->prefSize = pref; - newAnchor->maxSize = max; - - // Same as above, by default, keep preferred size. - newAnchor->sizeAtMinimum = pref; - newAnchor->sizeAtPreferred = pref; - newAnchor->sizeAtMaximum = pref; + parallel->refreshSizeHints_helper(0, false); + newAnchor = parallel; } graph->createEdge(before, after, newAnchor); @@ -1687,7 +1685,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( AnchorData *ad = trunkPath.positives.toList()[0]; ad->sizeAtMinimum = ad->minSize; ad->sizeAtPreferred = ad->prefSize; - ad->sizeAtExpanding = ad->prefSize; + ad->sizeAtExpanding = ad->expSize; ad->sizeAtMaximum = ad->maxSize; // Propagate @@ -1696,7 +1694,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum; sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred; sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum; - sizeAtExpanding[orientation] = ad->sizeAtPreferred; + sizeAtExpanding[orientation] = ad->sizeAtExpanding; } // Delete the constraints, we won't use them anymore. diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 047be39..8f67b2c 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -238,6 +238,8 @@ struct SequentialAnchorData : public AnchorData virtual void updateChildrenSizes(); virtual void refreshSizeHints(qreal effectiveSpacing); + void refreshSizeHints_helper(qreal effectiveSpacing, bool refreshChildren = true); + void setVertices(const QVector &vertices) { m_children = vertices; @@ -272,6 +274,8 @@ struct ParallelAnchorData : public AnchorData virtual void updateChildrenSizes(); virtual void refreshSizeHints(qreal effectiveSpacing); + void refreshSizeHints_helper(qreal effectiveSpacing, bool refreshChildren = true); + AnchorData* firstEdge; AnchorData* secondEdge; }; -- cgit v0.12 From ad69bb73754534f61658a83e85dadb75e603c90a Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Fri, 25 Sep 2009 19:02:21 -0300 Subject: QGraphicsAnchorLayout: Update solveExpanding to handle simplified graphs The previous implementation of solveExpanding would assume that anchors were either completely expanding (would grow to their sizeAtMaximum) or non-expanding (would try to remain at their sizeAtPreferred). What happens however is that with simplification enabled, we may have anchors that are "partially" expanding. An example is a sequential anchor that groups together two leaf anchors where only one of them is expanding. In this case, the expected size of that group anchor would be the sum of the max size of the expanding child plus the preferred size of the non-expanding child. To handle the above case we added the "expSize" variable. It holds the expected value at Expanding for each anchor. Based on this, and the already calculated values of sizeAtMaximum and sizeAtPreferred, the solver calculates the actual sizeAtExpanding for them. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 84 ++++++++++++++++++++---- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index dd46722..cf1429c 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -2319,40 +2319,97 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList co return feasible; } +/*! Calculate the "expanding" keyframe + + This new keyframe sits between the already existing sizeAtPreferred and + sizeAtMaximum keyframes. Its goal is to modify the interpolation between + the latter as to respect the "expanding" size policy of some anchors. + + Previously all items would be subject to a linear interpolation between + sizeAtPreferred and sizeAtMaximum values. This will change now, the + expanding anchors will change their size before the others. To calculate + this keyframe we use the following logic: + + 1) Ask each anchor for their desired expanding size (ad->expSize), this + value depends on the anchor expanding property in the following way: + + - Expanding anchors want to grow towards their maximum size + - Non-expanding anchors want to remain at their preferred size. + - Composite anchors want to grow towards somewhere between their + preferred sizes. (*) + + 2) Clamp their desired values to the value they assume in the neighbour + keyframes (sizeAtPreferred and sizeAtExpanding) + + 3) Run simplex with a setup that ensures the following: + + a. Anchors will change their value from their sizeAtPreferred towards + their sizeAtMaximum as much as required to ensure that ALL anchors + reach their respective "desired" expanding sizes. + + b. No anchors will change their value beyond what is NEEDED to satisfy + the requirement above. + + The final result is that, at the "expanding" keyframe expanding anchors + will grow and take with them all anchors that are parallel to them. + However, non-expanding anchors will remain at their preferred size unless + they are forced to grow by a parallel expanding anchor. + + Note: For anchors where the sizeAtPreferred is bigger than sizeAtPreferred, + the visual effect when the layout grows from its preferred size is + the following: Expanding anchors will keep their size while non + expanding ones will shrink. Only after non-expanding anchors have + shrinked all the way, the expanding anchors will start to shrink too. +*/ void QGraphicsAnchorLayoutPrivate::solveExpanding(QList constraints) { QList variables = getVariables(constraints); QList itemConstraints; QSimplexConstraint *objective = new QSimplexConstraint; + bool hasExpanding = false; - // Use all items that belong to trunk to: - // - add solveExpanding-specific item constraints - // - create the objective function + // Construct the simplex constraints and objective for (int i = 0; i < variables.size(); ++i) { + // For each anchor AnchorData *ad = variables[i]; - if (ad->isExpanding) { - // Add constraint to lock expanding anchor in its sizeAtMaximum + + // Clamp the desired expanding size + qreal upperBoundary = qMax(ad->sizeAtPreferred, ad->sizeAtMaximum); + qreal lowerBoundary = qMin(ad->sizeAtPreferred, ad->sizeAtMaximum); + qreal boundedExpSize = qBound(lowerBoundary, ad->expSize, upperBoundary); + + // Expanding anchors are those that want to move from their preferred size + if (boundedExpSize != ad->sizeAtPreferred) + hasExpanding = true; + + // Lock anchor between boundedExpSize and sizeAtMaximum (ensure 3.a) + if (boundedExpSize == ad->sizeAtMaximum) { + // The interval has only one possible value, we can use an "Equal" + // constraint and don't need to add this variable to the objective. QSimplexConstraint *itemC = new QSimplexConstraint; itemC->ratio = QSimplexConstraint::Equal; itemC->variables.insert(ad, 1.0); - itemC->constant = ad->sizeAtMaximum; + itemC->constant = boundedExpSize; itemConstraints << itemC; } else { - // Add constraints to lock anchor between their sizeAtPreferred and sizeAtMaximum + // Add MoreOrEqual and LessOrEqual constraints. QSimplexConstraint *itemC = new QSimplexConstraint; itemC->ratio = QSimplexConstraint::MoreOrEqual; itemC->variables.insert(ad, 1.0); - itemC->constant = qMin(ad->sizeAtPreferred, ad->sizeAtMaximum); + itemC->constant = qMin(boundedExpSize, ad->sizeAtMaximum); itemConstraints << itemC; itemC = new QSimplexConstraint; itemC->ratio = QSimplexConstraint::LessOrEqual; itemC->variables.insert(ad, 1.0); - itemC->constant = qMax(ad->sizeAtPreferred, ad->sizeAtMaximum); + itemC->constant = qMax(boundedExpSize, ad->sizeAtMaximum); itemConstraints << itemC; - // Add anchor to objective function - if (ad->sizeAtPreferred < ad->sizeAtMaximum) { + // Create objective to avoid the anchos from moving away from + // the preferred size more than the needed amount. (ensure 3.b) + // The objective function is the distance between sizeAtPreferred + // and sizeAtExpanding, it will be minimized. + if (ad->sizeAtExpanding < ad->sizeAtMaximum) { // Try to shrink this variable towards its sizeAtPreferred value objective->variables.insert(ad, 1.0); } else { @@ -2363,7 +2420,7 @@ void QGraphicsAnchorLayoutPrivate::solveExpanding(QList co } // Solve - if (objective->variables.size() == variables.size()) { + if (hasExpanding == false) { // If no anchors are expanding, we don't need to run the simplex // Set all variables to their preferred size for (int i = 0; i < variables.size(); ++i) { @@ -2372,9 +2429,12 @@ void QGraphicsAnchorLayoutPrivate::solveExpanding(QList co } else { // Run simplex QSimplex simplex; + + // Satisfy expanding (3.a) bool feasible = simplex.setConstraints(constraints + itemConstraints); Q_ASSERT(feasible); + // Reduce damage (3.b) simplex.setObjective(objective); simplex.solveMin(); -- cgit v0.12 From ba66cc0bde91e1143ac0a34f249f98e4573a5737 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Mon, 28 Sep 2009 11:49:42 -0300 Subject: QSimplex: Remove overly conservative assertion This assertion started failing after the addition of expanding state. After some investigation we felt that the assertion itself was too strong and would fail in some valid cases. The new assertion is formally right as it tests whether the simplex solver was able to satisfy the simplex constraints, _exactly_ what it is expected to do. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qsimplex_p.cpp | 24 ++++++------------------ src/gui/graphicsview/qsimplex_p.h | 26 +++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp index 00fc204..95003d2 100644 --- a/src/gui/graphicsview/qsimplex_p.cpp +++ b/src/gui/graphicsview/qsimplex_p.cpp @@ -285,24 +285,6 @@ bool QSimplex::setConstraints(const QList newConstraints) // anymore. clearColumns(firstArtificial, columns - 2); - #ifdef QT_DEBUG - // Ensure that at the end of the simplex each row should either: - // - Have a positive value on the column associated to its variable, or - // - Have zero values in all columns. - // - // This avoids a regression where restrictions would be lost - // due to randomness in the pivotRowForColumn method. - for (int i = 1; i < rows; ++i) { - int variableIndex = valueAt(i, 0); - if (valueAt(i, variableIndex) > 0) - continue; - - for (int j = 1; j < columns; ++j) { - Q_ASSERT(valueAt(i, j) == 0); - } - } - #endif - return true; } @@ -537,6 +519,12 @@ qreal QSimplex::solver(solverFactor factor) solveMaxHelper(); collectResults(); + #ifdef QT_DEBUG + for (int i = 0; i < constraints.size(); ++i) { + Q_ASSERT(constraints[i]->isSatisfied()); + } + #endif + return factor * valueAt(0, columns - 1); } diff --git a/src/gui/graphicsview/qsimplex_p.h b/src/gui/graphicsview/qsimplex_p.h index 54b080d..b517cb9 100644 --- a/src/gui/graphicsview/qsimplex_p.h +++ b/src/gui/graphicsview/qsimplex_p.h @@ -94,8 +94,32 @@ struct QSimplexConstraint QPair helper; QSimplexVariable * artificial; -}; + #ifdef QT_DEBUG + bool isSatisfied() { + qreal leftHandSide(0); + + QHash::const_iterator iter; + for (iter = variables.constBegin(); iter != variables.constEnd(); ++iter) { + leftHandSide += iter.value() * iter.key()->result; + } + + Q_ASSERT(constant > 0 || qFuzzyCompare(1, 1 + constant)); + + if (qFuzzyCompare(1000 + leftHandSide, 1000 + constant)) + return true; + + switch (ratio) { + case LessOrEqual: + return leftHandSide < constant; + case MoreOrEqual: + return leftHandSide > constant; + default: + return false; + } + } + #endif +}; class QSimplex { -- cgit v0.12 From 0536b996759ef367e784310f472089eec583213f Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Fri, 25 Sep 2009 19:35:25 -0300 Subject: QGraphicsAnchorLayout: Add support for expanding SizePolicy with simplification Updating "refreshSizeHints" and "updateChildrenSizes" methods for standard, parallel and sequential anchors, in order to support the expanding SizePolicy flag. This adds the logic required to calculate equivalent expected/nominal expanding size (AnchorData::expSize) as well as the logic to propagate the effective size when in the expanding state (AnchorData::sizeAtExpected). Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 59 ++++++++++++++---------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 4 +- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index cf1429c..20ff03b 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -146,15 +146,11 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) maxSize /= 2; } - // ### - expSize = prefSize; - // if (policy & QSizePolicy::ExpandFlag) { - // isExpanding = 1; - // expSize = maxSize; - // } else { - // isExpanding = 0; - // expSize = prefSize; - // } + if (policy & QSizePolicy::ExpandFlag) { + expSize = maxSize; + } else { + expSize = prefSize; + } // Set the anchor effective sizes to preferred. // @@ -175,8 +171,6 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) expSize = effectiveSpacing; maxSize = effectiveSpacing; - isExpanding = 0; - sizeAtMinimum = prefSize; sizeAtPreferred = prefSize; sizeAtExpanding = prefSize; @@ -188,8 +182,8 @@ void ParallelAnchorData::updateChildrenSizes() { firstEdge->sizeAtMinimum = secondEdge->sizeAtMinimum = sizeAtMinimum; firstEdge->sizeAtPreferred = secondEdge->sizeAtPreferred = sizeAtPreferred; + firstEdge->sizeAtExpanding = secondEdge->sizeAtExpanding = sizeAtExpanding; firstEdge->sizeAtMaximum = secondEdge->sizeAtMaximum = sizeAtMaximum; - firstEdge->sizeAtExpanding = secondEdge->sizeAtExpanding = sizeAtPreferred; firstEdge->updateChildrenSizes(); secondEdge->updateChildrenSizes(); @@ -215,11 +209,11 @@ void ParallelAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, minSize = qMax(firstEdge->minSize, secondEdge->minSize); maxSize = qMin(firstEdge->maxSize, secondEdge->maxSize); - prefSize = qMax(firstEdge->prefSize, secondEdge->prefSize); - prefSize = qMin(prefSize, maxSize); + expSize = qMax(firstEdge->expSize, secondEdge->expSize); + expSize = qMin(expSize, maxSize); - // ### - expSize = prefSize; + prefSize = qMax(firstEdge->prefSize, secondEdge->prefSize); + prefSize = qMin(prefSize, expSize); // See comment in AnchorData::refreshSizeHints() about sizeAt* values sizeAtMinimum = prefSize; @@ -260,6 +254,21 @@ static qreal getFactor(qreal value, qreal min, qreal pref, qreal max) } } +static qreal getExpandingFactor(const qreal &expSize, const qreal &sizeAtPreferred, + const qreal &sizeAtExpanding, const qreal &sizeAtMaximum) +{ + const qreal lower = qMin(sizeAtPreferred, sizeAtMaximum); + const qreal upper = qMax(sizeAtPreferred, sizeAtMaximum); + const qreal boundedExpSize = qBound(lower, expSize, upper); + + const qreal bandSize = sizeAtMaximum - boundedExpSize; + if (bandSize == 0) { + return 0; + } else { + return (sizeAtExpanding - boundedExpSize) / bandSize; + } +} + void SequentialAnchorData::updateChildrenSizes() { // ### REMOVE ME @@ -269,15 +278,18 @@ void SequentialAnchorData::updateChildrenSizes() Q_ASSERT(sizeAtMinimum < maxSize || qFuzzyCompare(sizeAtMinimum, maxSize)); Q_ASSERT(sizeAtPreferred > minSize || qFuzzyCompare(sizeAtPreferred, minSize)); Q_ASSERT(sizeAtPreferred < maxSize || qFuzzyCompare(sizeAtPreferred, maxSize)); + Q_ASSERT(sizeAtExpanding > minSize || qFuzzyCompare(sizeAtExpanding, minSize)); + Q_ASSERT(sizeAtExpanding < maxSize || qFuzzyCompare(sizeAtExpanding, maxSize)); Q_ASSERT(sizeAtMaximum > minSize || qFuzzyCompare(sizeAtMaximum, minSize)); Q_ASSERT(sizeAtMaximum < maxSize || qFuzzyCompare(sizeAtMaximum, maxSize)); // Band here refers if the value is in the Minimum To Preferred // band (the lower band) or the Preferred To Maximum (the upper band). - qreal minFactor = getFactor(sizeAtMinimum, minSize, prefSize, maxSize); - qreal prefFactor = getFactor(sizeAtPreferred, minSize, prefSize, maxSize); - qreal maxFactor = getFactor(sizeAtMaximum, minSize, prefSize, maxSize); + const qreal minFactor = getFactor(sizeAtMinimum, minSize, prefSize, maxSize); + const qreal prefFactor = getFactor(sizeAtPreferred, minSize, prefSize, maxSize); + const qreal maxFactor = getFactor(sizeAtMaximum, minSize, prefSize, maxSize); + const qreal expFactor = getExpandingFactor(expSize, sizeAtPreferred, sizeAtExpanding, sizeAtMaximum); for (int i = 0; i < m_edges.count(); ++i) { AnchorData *e = m_edges.at(i); @@ -291,8 +303,10 @@ void SequentialAnchorData::updateChildrenSizes() bandSize = maxFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; e->sizeAtMaximum = e->prefSize + bandSize * maxFactor; - // ### - e->sizeAtExpanding = e->sizeAtPreferred; + const qreal lower = qMin(e->sizeAtPreferred, e->sizeAtMaximum); + const qreal upper = qMax(e->sizeAtPreferred, e->sizeAtMaximum); + const qreal edgeBoundedExpSize = qBound(lower, e->expSize, upper); + e->sizeAtExpanding = edgeBoundedExpSize + expFactor * (e->sizeAtMaximum - edgeBoundedExpSize); e->updateChildrenSizes(); } @@ -324,9 +338,6 @@ void SequentialAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, maxSize += edge->maxSize; } - // ### - expSize = prefSize; - // See comment in AnchorData::refreshSizeHints() about sizeAt* values sizeAtMinimum = prefSize; sizeAtPreferred = prefSize; diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 8f67b2c..aac4f96 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -156,8 +156,7 @@ struct AnchorData : public QSimplexVariable { sizeAtMinimum(0), sizeAtPreferred(0), sizeAtExpanding(0), sizeAtMaximum(0), graphicsAnchor(0), skipInPreferred(0), - type(Normal), hasSize(true), isLayoutAnchor(false), - isExpanding(false) {} + type(Normal), hasSize(true), isLayoutAnchor(false) {} virtual void updateChildrenSizes() {} virtual void refreshSizeHints(qreal effectiveSpacing); @@ -214,7 +213,6 @@ struct AnchorData : public QSimplexVariable { uint type : 2; // either Normal, Sequential or Parallel uint hasSize : 1; // if false, get size from style. uint isLayoutAnchor : 1; // if this anchor is connected to a layout 'edge' - uint isExpanding : 1; // if true, expands before other anchors }; #ifdef QT_DEBUG -- cgit v0.12 From 89ea5d0cc0b00a1d4e8f3e700b86f168cf4437ed Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Wed, 30 Sep 2009 12:25:34 -0300 Subject: QGraphicsAnchorLayout: Fix creation of internal layout anchors Since the AnchorData cleanup commit (c974aca8) all anchor initialization is being done by refreshSizeHints. However that method was not able to properly initialize the internal layout anchors. This commit refactors that method both to add the functionality and improve readability. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 145 ++++++++++++--------- .../tst_qgraphicsanchorlayout.cpp | 2 +- 2 files changed, 84 insertions(+), 63 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 20ff03b..8139b2b 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -92,90 +92,111 @@ qreal QGraphicsAnchorPrivate::spacing() const } +static void sizeHintsFromItem(QGraphicsLayoutItem *item, + const QGraphicsAnchorLayoutPrivate::Orientation orient, + qreal *minSize, qreal *prefSize, + qreal *expSize, qreal *maxSize) +{ + QSizePolicy::Policy policy; + qreal minSizeHint; + qreal prefSizeHint; + qreal maxSizeHint; + + if (orient == QGraphicsAnchorLayoutPrivate::Horizontal) { + policy = item->sizePolicy().horizontalPolicy(); + minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).width(); + prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).width(); + maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).width(); + } else { + policy = item->sizePolicy().verticalPolicy(); + minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).height(); + prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).height(); + maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).height(); + } + + // minSize, prefSize and maxSize are initialized + // with item's preferred Size: this is QSizePolicy::Fixed. + // + // Then we check each flag to find the resultant QSizePolicy, + // according to the following table: + // + // constant value + // QSizePolicy::Fixed 0 + // QSizePolicy::Minimum GrowFlag + // QSizePolicy::Maximum ShrinkFlag + // QSizePolicy::Preferred GrowFlag | ShrinkFlag + // QSizePolicy::Ignored GrowFlag | ShrinkFlag | IgnoreFlag + + if (policy & QSizePolicy::ShrinkFlag) + *minSize = minSizeHint; + else + *minSize = prefSizeHint; + + if (policy & QSizePolicy::GrowFlag) + *maxSize = maxSizeHint; + else + *maxSize = prefSizeHint; + + // Note that these two initializations are affected by the previous flags + if (policy & QSizePolicy::IgnoreFlag) + *prefSize = *maxSize; + else + *prefSize = prefSizeHint; + + if (policy & QSizePolicy::ExpandFlag) + *expSize = *maxSize; + else + *expSize = *prefSize; +} + void AnchorData::refreshSizeHints(qreal effectiveSpacing) { - if (!isLayoutAnchor && (from->m_item == to->m_item)) { - QGraphicsLayoutItem *item = from->m_item; + const bool isInternalAnchor = from->m_item == to->m_item; - const QGraphicsAnchorLayoutPrivate::Orientation orient = QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge); - const Qt::AnchorPoint centerEdge = QGraphicsAnchorLayoutPrivate::pickEdge(Qt::AnchorHorizontalCenter, orient); + if (isInternalAnchor) { + const QGraphicsAnchorLayoutPrivate::Orientation orient = + QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge); - QSizePolicy::Policy policy; - qreal minSizeHint, prefSizeHint, maxSizeHint; - if (orient == QGraphicsAnchorLayoutPrivate::Horizontal) { - policy = item->sizePolicy().horizontalPolicy(); - minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).width(); - prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).width(); - maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).width(); + if (isLayoutAnchor) { + minSize = 0; + prefSize = 0; + expSize = 0; + maxSize = QWIDGETSIZE_MAX; } else { - policy = item->sizePolicy().verticalPolicy(); - minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).height(); - prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).height(); - maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).height(); + QGraphicsLayoutItem *item = from->m_item; + sizeHintsFromItem(item, orient, &minSize, &prefSize, &expSize, &maxSize); } - // minSize, prefSize and maxSize are initialized - // with item's preferred Size: this is QSizePolicy::Fixed. - // - // Then we check each flag to find the resultant QSizePolicy, - // according to the following table: - // - // constant value - // QSizePolicy::Fixed 0 - // QSizePolicy::Minimum GrowFlag - // QSizePolicy::Maximum ShrinkFlag - // QSizePolicy::Preferred GrowFlag | ShrinkFlag - // QSizePolicy::Ignored GrowFlag | ShrinkFlag | IgnoreFlag - prefSize = prefSizeHint; - minSize = prefSize; - maxSize = prefSize; - - if (policy & QSizePolicy::GrowFlag) - maxSize = maxSizeHint; - - if (policy & QSizePolicy::ShrinkFlag) - minSize = minSizeHint; - - if (policy & QSizePolicy::IgnoreFlag) - prefSize = minSize; + const Qt::AnchorPoint centerEdge = + QGraphicsAnchorLayoutPrivate::pickEdge(Qt::AnchorHorizontalCenter, orient); bool hasCenter = (from->m_edge == centerEdge || to->m_edge == centerEdge); if (hasCenter) { minSize /= 2; prefSize /= 2; + expSize /= 2; maxSize /= 2; } - if (policy & QSizePolicy::ExpandFlag) { - expSize = maxSize; - } else { - expSize = prefSize; - } - - // Set the anchor effective sizes to preferred. - // - // Note: The idea here is that all items should remain at their - // preferred size unless where that's impossible. In cases where - // the item is subject to restrictions (anchored to the layout - // edges, for instance), the simplex solver will be run to - // recalculate and override the values we set here. - sizeAtMinimum = prefSize; - sizeAtPreferred = prefSize; - sizeAtExpanding = prefSize; - sizeAtMaximum = prefSize; - } else if (!hasSize) { // Anchor has no size defined, use given default information minSize = effectiveSpacing; prefSize = effectiveSpacing; expSize = effectiveSpacing; maxSize = effectiveSpacing; - - sizeAtMinimum = prefSize; - sizeAtPreferred = prefSize; - sizeAtExpanding = prefSize; - sizeAtMaximum = prefSize; } + + // Set the anchor effective sizes to preferred. + // + // Note: The idea here is that all items should remain at their + // preferred size unless where that's impossible. In cases where + // the item is subject to restrictions (anchored to the layout + // edges, for instance), the simplex solver will be run to + // recalculate and override the values we set here. + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtExpanding = prefSize; + sizeAtMaximum = prefSize; } void ParallelAnchorData::updateChildrenSizes() diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 4ad48c7..91e5bb3 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -1271,7 +1271,7 @@ void tst_QGraphicsAnchorLayout::sizePolicy() w1->adjustSize(); QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(0, 0)); - QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(0, 0)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(100, 100)); QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(100, 100)); delete p; -- cgit v0.12 From 0d1e86fdfb7958a1affb93c7760c22fca9a0fcb2 Mon Sep 17 00:00:00 2001 From: Jesus Sanchez-Palencia Date: Fri, 25 Sep 2009 18:53:15 -0300 Subject: QGraphicsAnchorLayout: Adding a Float Conflict test case Now we don't support floating items and so we should consider that the layout has a conflict when handling this situation. Signed-off-by: Jesus Sanchez-Palencia Reviewed-by: Caio Marcelo de Oliveira Filho --- .../tst_qgraphicsanchorlayout.cpp | 69 ++++++++++++++++++++++ .../tst_qgraphicsanchorlayout1.cpp | 4 +- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 91e5bb3..b076631 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -72,6 +72,7 @@ private slots: void expandingSequence(); void expandingSequenceFairDistribution(); void expandingParallel(); + void floatConflict(); }; class RectWidget : public QGraphicsWidget @@ -158,7 +159,17 @@ void tst_QGraphicsAnchorLayout::simple() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); + + // Horizontal + l->addAnchor(w1, Qt::AnchorLeft, l, Qt::AnchorLeft); l->addAnchor(w1, Qt::AnchorRight, w2, Qt::AnchorLeft); + l->addAnchor(w2, Qt::AnchorRight, l, Qt::AnchorRight); + + // Vertical + l->addAnchor(w1, Qt::AnchorTop, l, Qt::AnchorTop); + l->addAnchor(w2, Qt::AnchorTop, l, Qt::AnchorTop); + l->addAnchor(w1, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->addAnchor(w2, Qt::AnchorBottom, l, Qt::AnchorBottom); QGraphicsWidget p; p.setLayout(l); @@ -1152,12 +1163,22 @@ void tst_QGraphicsAnchorLayout::delete_anchor() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setSpacing(0); l->setContentsMargins(0, 0, 0, 0); + + // Horizontal l->addAnchor(l, Qt::AnchorLeft, w1, Qt::AnchorLeft); l->addAnchor(w1, Qt::AnchorRight, w2, Qt::AnchorLeft); l->addAnchor(w2, Qt::AnchorRight, l, Qt::AnchorRight); l->addAnchor(w1, Qt::AnchorRight, w3, Qt::AnchorLeft); l->addAnchor(w3, Qt::AnchorRight, l, Qt::AnchorRight); + // Vertical + l->addAnchor(w1, Qt::AnchorTop, l, Qt::AnchorTop); + l->addAnchor(w1, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->addAnchor(w2, Qt::AnchorTop, l, Qt::AnchorTop); + l->addAnchor(w2, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->addAnchor(w3, Qt::AnchorTop, l, Qt::AnchorTop); + l->addAnchor(w3, Qt::AnchorBottom, l, Qt::AnchorBottom); + QGraphicsAnchor *anchor = l->anchor(w3, Qt::AnchorRight, l, Qt::AnchorRight); anchor->setSpacing(10); @@ -1521,5 +1542,53 @@ void tst_QGraphicsAnchorLayout::expandingParallel() QCOMPARE(newLayoutMaximumSize.width(), qreal(300)); } +void tst_QGraphicsAnchorLayout::floatConflict() +{ + QGraphicsWidget *a = createItem(QSizeF(80,10), QSizeF(90,10), QSizeF(100,10), "a"); + QGraphicsWidget *b = createItem(QSizeF(80,10), QSizeF(90,10), QSizeF(100,10), "b"); + + QGraphicsAnchorLayout *l; + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + + l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + p->setLayout(l); + + // horizontal + // with this anchor we have two floating items + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft); + + // Just checking if the layout is handling well the removal of floating items + delete l->anchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + QCOMPARE(l->count(), 0); + QCOMPARE(layoutHasConflict(l), false); + + // setting back the same anchor + setAnchor(l, a, Qt::AnchorRight, b, Qt::AnchorLeft); + + // We don't support floating items but they should be counted as if they are in the layout + QCOMPARE(l->count(), 2); + // Although, we have an invalid situation + QCOMPARE(layoutHasConflict(l), true); + + // Semi-floats are supported + setAnchor(l, a, Qt::AnchorLeft, l, Qt::AnchorLeft); + QCOMPARE(l->count(), 2); + + // Vertically the layout has floating items. Therefore, we have a conflict + QCOMPARE(layoutHasConflict(l), true); + + // No more floating items + setAnchor(l, b, Qt::AnchorRight, l, Qt::AnchorRight); + setAnchor(l, a, Qt::AnchorTop, l, Qt::AnchorTop); + setAnchor(l, a, Qt::AnchorBottom, l, Qt::AnchorBottom); + setAnchor(l, b, Qt::AnchorTop, l, Qt::AnchorTop); + setAnchor(l, b, Qt::AnchorBottom, l, Qt::AnchorBottom); + QCOMPARE(layoutHasConflict(l), false); + + delete p; +} + QTEST_MAIN(tst_QGraphicsAnchorLayout) #include "tst_qgraphicsanchorlayout.moc" diff --git a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp index a521b78..caf078e 100644 --- a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp +++ b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp @@ -530,16 +530,18 @@ void tst_QGraphicsAnchorLayout1::testIsValid() TestWidget *widget1 = new TestWidget(); TestWidget *widget2 = new TestWidget(); + // Vertically the layout has floating items. Therefore, we have a conflict layout->setAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft, 0.1); layout->setAnchor(layout, Qt::AnchorRight, widget1, Qt::AnchorRight, -0.1); + // Horizontally the layout has floating items. Therefore, we have a conflict layout->setAnchor(layout, Qt::AnchorTop, widget2, Qt::AnchorTop, 0.1); layout->setAnchor(layout, Qt::AnchorBottom, widget2, Qt::AnchorBottom, -0.1); widget->setLayout(layout); widget->setGeometry(QRectF(0,0,100,100)); - QCOMPARE(layout->isValid(), true); + QCOMPARE(layout->isValid(), false); delete widget; } } -- cgit v0.12 From 18da388497b5f3cbc77298a70bc83f037f06c013 Mon Sep 17 00:00:00 2001 From: Jesus Sanchez-Palencia Date: Tue, 29 Sep 2009 14:02:27 -0300 Subject: QGraphicsAnchorLayoutPrivate: Handling floating items as an invalid layout situation We make use of the anchor visited on findPaths() to keep track of how many items are visited when walking on the graph. We can use this information of how many items are reachable (non-float) in order to check if we have floating items (not reachable from any graph vertex). If so, we have a situation not supported by now. Signed-off-by: Jesus Sanchez-Palencia Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 51 +++++++++++++++++++++++- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 3 ++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 8139b2b..e760642 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1841,6 +1841,11 @@ void QGraphicsAnchorLayoutPrivate::findPaths(Orientation orientation) queue.enqueue(qMakePair(pair.second, v)); } } + + // We will walk through every reachable items (non-float) and mark them + // by keeping their references on m_nonFloatItems. With this we can easily + // identify non-float and float items. + identifyNonFloatItems(visited, orientation); } /*! @@ -1999,6 +2004,46 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) } /*! + \internal + + Use all visited Anchors on findPaths() so we can identify non-float Items. +*/ +void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems(QSet visited, Orientation orientation) +{ + m_nonFloatItems[orientation].clear(); + + foreach (const AnchorData *ad, visited) + identifyNonFloatItems_helper(ad, orientation); +} + +/*! + \internal + + Given an anchor, if it is an internal anchor and Normal we must mark it's item as non-float. + If the anchor is Sequential or Parallel, we must iterate on its children recursively until we reach + internal anchors (items). +*/ +void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems_helper(const AnchorData *ad, Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + + switch(ad->type) { + case AnchorData::Normal: + if (ad->from->m_item == ad->to->m_item && ad->to->m_item != q) + m_nonFloatItems[orientation].insert(ad->to->m_item); + break; + case AnchorData::Sequential: + foreach (const AnchorData *d, static_cast(ad)->m_edges) + identifyNonFloatItems_helper(d, orientation); + break; + case AnchorData::Parallel: + identifyNonFloatItems_helper(static_cast(ad)->firstEdge, orientation); + identifyNonFloatItems_helper(static_cast(ad)->secondEdge, orientation); + break; + } +} + +/*! \internal Use the current vertices distance to calculate and set the geometry of @@ -2491,7 +2536,11 @@ bool QGraphicsAnchorLayoutPrivate::hasConflicts() const { QGraphicsAnchorLayoutPrivate *that = const_cast(this); that->calculateGraphs(); - return graphHasConflicts[0] || graphHasConflicts[1]; + + bool floatConflict = (m_nonFloatItems[0].size() < items.size()) + || (m_nonFloatItems[1].size() < items.size()); + + return graphHasConflicts[0] || graphHasConflicts[1] || floatConflict; } #ifdef QT_DEBUG diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index aac4f96..1a74d85 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -445,6 +445,8 @@ public: void constraintsFromPaths(Orientation orientation); QList constraintsFromSizeHints(const QList &anchors); QList > getGraphParts(Orientation orientation); + void identifyNonFloatItems(QSet visited, Orientation orientation); + void identifyNonFloatItems_helper(const AnchorData *ad, Orientation orientation); inline AnchorVertex *internalVertex(const QPair &itemEdge) const { @@ -511,6 +513,7 @@ public: // ### bool graphSimplified[2]; bool graphHasConflicts[2]; + QSet m_nonFloatItems[2]; uint calculateGraphCacheDirty : 1; }; -- cgit v0.12 From 6367a702b13a440557d4e0a405ae6d34ef0b778f Mon Sep 17 00:00:00 2001 From: Jesus Sanchez-Palencia Date: Tue, 29 Sep 2009 15:22:26 -0300 Subject: QGraphicsAnchorLayoutPrivate: Fixing initial geometry values for new layout items Now every item starts with its geometry on ((0,0), preferredSize()). By doing this we guarantee that every item will have a known initial value, which can be specially useful on float cases for instance. Signed-off-by: Jesus Sanchez-Palencia Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 37 ++++++++++++++++-------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index e760642..87f5aa0 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -2068,22 +2068,35 @@ void QGraphicsAnchorLayoutPrivate::setItemsGeometries(const QRectF &geom) right = geom.right() - right; foreach (QGraphicsLayoutItem *item, items) { - firstH = internalVertex(item, Qt::AnchorLeft); - secondH = internalVertex(item, Qt::AnchorRight); - firstV = internalVertex(item, Qt::AnchorTop); - secondV = internalVertex(item, Qt::AnchorBottom); - QRectF newGeom; - newGeom.setTop(top + firstV->distance); - newGeom.setBottom(top + secondV->distance); + QSizeF itemPreferredSize = item->effectiveSizeHint(Qt::PreferredSize); + if (m_nonFloatItems[Horizontal].contains(item)) { + firstH = internalVertex(item, Qt::AnchorLeft); + secondH = internalVertex(item, Qt::AnchorRight); + + if (visualDir == Qt::LeftToRight) { + newGeom.setLeft(left + firstH->distance); + newGeom.setRight(left + secondH->distance); + } else { + newGeom.setLeft(right - secondH->distance); + newGeom.setRight(right - firstH->distance); + } + } else { + newGeom.setLeft(0); + newGeom.setRight(itemPreferredSize.width()); + } + + if (m_nonFloatItems[Vertical].contains(item)) { + firstV = internalVertex(item, Qt::AnchorTop); + secondV = internalVertex(item, Qt::AnchorBottom); - if (visualDir == Qt::LeftToRight) { - newGeom.setLeft(left + firstH->distance); - newGeom.setRight(left + secondH->distance); + newGeom.setTop(top + firstV->distance); + newGeom.setBottom(top + secondV->distance); } else { - newGeom.setLeft(right - secondH->distance); - newGeom.setRight(right - firstH->distance); + newGeom.setTop(0); + newGeom.setBottom(itemPreferredSize.height()); } + item->setGeometry(newGeom); } } -- cgit v0.12 From 709cc9140407280aa5c871d4650c123002dfdd19 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 1 Oct 2009 15:04:20 -0300 Subject: QGraphicsAnchorLayout: Enable "float" Orbit test Now supported after float patches. Signed-off-by: Eduardo M. Fleury Reviewed-by: Jesus Sanchez-Palencia --- tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp index caf078e..148b2c8 100644 --- a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp +++ b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp @@ -1415,9 +1415,6 @@ void tst_QGraphicsAnchorLayout1::testMixedSpacing_data() QTest::newRow("One widget, unsolvable") << QSizeF(10, 10) << theData << theResult; } - // ### BUG. We are not handling "floating" elements properly. Ie. elements that - // have no anchors in a given orientation. - if (0) // Two widgets, one has fixed size { BasicLayoutTestDataList theData; -- cgit v0.12 From d6d0b2ef5223b4a40e8907104eb1d9e827507a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 7 Oct 2009 10:51:26 +0200 Subject: Cosmetic fixes to the previous patches. There is only one change in the actual code here, and it simply removes an unnecessary initialization of hasSize in the ctors of the composite anchors. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 68 +++++++++++++----------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 2 - src/gui/graphicsview/qsimplex_p.cpp | 4 +- src/gui/graphicsview/qsimplex_p.h | 4 +- 4 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 87f5aa0..34071cc 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -2409,47 +2409,51 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList co return feasible; } -/*! Calculate the "expanding" keyframe +/*! + \internal + Calculate the "expanding" keyframe - This new keyframe sits between the already existing sizeAtPreferred and - sizeAtMaximum keyframes. Its goal is to modify the interpolation between - the latter as to respect the "expanding" size policy of some anchors. + This new keyframe sits between the already existing sizeAtPreferred and + sizeAtMaximum keyframes. Its goal is to modify the interpolation between + the latter as to respect the "expanding" size policy of some anchors. - Previously all items would be subject to a linear interpolation between - sizeAtPreferred and sizeAtMaximum values. This will change now, the - expanding anchors will change their size before the others. To calculate - this keyframe we use the following logic: + Previously all items would be subject to a linear interpolation between + sizeAtPreferred and sizeAtMaximum values. This will change now, the + expanding anchors will change their size before the others. To calculate + this keyframe we use the following logic: - 1) Ask each anchor for their desired expanding size (ad->expSize), this - value depends on the anchor expanding property in the following way: + 1) Ask each anchor for their desired expanding size (ad->expSize), this + value depends on the anchor expanding property in the following way: - - Expanding anchors want to grow towards their maximum size - - Non-expanding anchors want to remain at their preferred size. - - Composite anchors want to grow towards somewhere between their - preferred sizes. (*) + - Expanding normal anchors want to grow towards their maximum size + - Non-expanding normal anchors want to remain at their preferred size. + - Sequential anchors wants to grow towards a size that is calculated by: + summarizing it's child anchors, where it will use preferred size for non-expanding anchors + and maximum size for expanding anchors. + - Parallel anchors want to grow towards the smallest maximum size of all the expanding anchors. - 2) Clamp their desired values to the value they assume in the neighbour - keyframes (sizeAtPreferred and sizeAtExpanding) + 2) Clamp their desired values to the value they assume in the neighbour + keyframes (sizeAtPreferred and sizeAtExpanding) - 3) Run simplex with a setup that ensures the following: + 3) Run simplex with a setup that ensures the following: - a. Anchors will change their value from their sizeAtPreferred towards - their sizeAtMaximum as much as required to ensure that ALL anchors - reach their respective "desired" expanding sizes. + a. Anchors will change their value from their sizeAtPreferred towards + their sizeAtMaximum as much as required to ensure that ALL anchors + reach their respective "desired" expanding sizes. - b. No anchors will change their value beyond what is NEEDED to satisfy - the requirement above. + b. No anchors will change their value beyond what is NEEDED to satisfy + the requirement above. - The final result is that, at the "expanding" keyframe expanding anchors - will grow and take with them all anchors that are parallel to them. - However, non-expanding anchors will remain at their preferred size unless - they are forced to grow by a parallel expanding anchor. + The final result is that, at the "expanding" keyframe expanding anchors + will grow and take with them all anchors that are parallel to them. + However, non-expanding anchors will remain at their preferred size unless + they are forced to grow by a parallel expanding anchor. - Note: For anchors where the sizeAtPreferred is bigger than sizeAtPreferred, - the visual effect when the layout grows from its preferred size is - the following: Expanding anchors will keep their size while non - expanding ones will shrink. Only after non-expanding anchors have - shrinked all the way, the expanding anchors will start to shrink too. + Note: For anchors where the sizeAtPreferred is bigger than sizeAtMaximum, + the visual effect when the layout grows from its preferred size is + the following: Expanding anchors will keep their size while non + expanding ones will shrink. Only after non-expanding anchors have + shrinked all the way, the expanding anchors will start to shrink too. */ void QGraphicsAnchorLayoutPrivate::solveExpanding(QList constraints) { @@ -2495,7 +2499,7 @@ void QGraphicsAnchorLayoutPrivate::solveExpanding(QList co itemC->constant = qMax(boundedExpSize, ad->sizeAtMaximum); itemConstraints << itemC; - // Create objective to avoid the anchos from moving away from + // Create objective to avoid the anchors from moving away from // the preferred size more than the needed amount. (ensure 3.b) // The objective function is the distance between sizeAtPreferred // and sizeAtExpanding, it will be minimized. diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 1a74d85..24b25de 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -227,7 +227,6 @@ struct SequentialAnchorData : public AnchorData SequentialAnchorData() : AnchorData() { type = AnchorData::Sequential; - hasSize = true; #ifdef QT_DEBUG name = QLatin1String("SequentialAnchorData"); #endif @@ -256,7 +255,6 @@ struct ParallelAnchorData : public AnchorData : AnchorData(), firstEdge(first), secondEdge(second) { type = AnchorData::Parallel; - hasSize = true; // ### Those asserts force that both child anchors have the same direction, // but can't we simplify a pair of anchors in opposite directions? diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp index 95003d2..b8f8fb4 100644 --- a/src/gui/graphicsview/qsimplex_p.cpp +++ b/src/gui/graphicsview/qsimplex_p.cpp @@ -519,11 +519,11 @@ qreal QSimplex::solver(solverFactor factor) solveMaxHelper(); collectResults(); - #ifdef QT_DEBUG +#ifdef QT_DEBUG for (int i = 0; i < constraints.size(); ++i) { Q_ASSERT(constraints[i]->isSatisfied()); } - #endif +#endif return factor * valueAt(0, columns - 1); } diff --git a/src/gui/graphicsview/qsimplex_p.h b/src/gui/graphicsview/qsimplex_p.h index b517cb9..51991a9 100644 --- a/src/gui/graphicsview/qsimplex_p.h +++ b/src/gui/graphicsview/qsimplex_p.h @@ -95,7 +95,7 @@ struct QSimplexConstraint QPair helper; QSimplexVariable * artificial; - #ifdef QT_DEBUG +#ifdef QT_DEBUG bool isSatisfied() { qreal leftHandSide(0); @@ -118,7 +118,7 @@ struct QSimplexConstraint return false; } } - #endif +#endif }; class QSimplex -- cgit v0.12 From dde11ce47c2d83bae685f3ff032ee9e6e372058f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 7 Oct 2009 11:30:30 +0200 Subject: Make some lines in the autotest more readable. --- .../qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index b076631..286ea2d 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -161,15 +161,13 @@ void tst_QGraphicsAnchorLayout::simple() l->setContentsMargins(0, 0, 0, 0); // Horizontal - l->addAnchor(w1, Qt::AnchorLeft, l, Qt::AnchorLeft); + l->addAnchor(l, Qt::AnchorLeft, w1, Qt::AnchorLeft); l->addAnchor(w1, Qt::AnchorRight, w2, Qt::AnchorLeft); l->addAnchor(w2, Qt::AnchorRight, l, Qt::AnchorRight); // Vertical - l->addAnchor(w1, Qt::AnchorTop, l, Qt::AnchorTop); - l->addAnchor(w2, Qt::AnchorTop, l, Qt::AnchorTop); - l->addAnchor(w1, Qt::AnchorBottom, l, Qt::AnchorBottom); - l->addAnchor(w2, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->addAnchors(l, w1, Qt::Vertical); + l->addAnchors(l, w2, Qt::Vertical); QGraphicsWidget p; p.setLayout(l); @@ -1172,12 +1170,9 @@ void tst_QGraphicsAnchorLayout::delete_anchor() l->addAnchor(w3, Qt::AnchorRight, l, Qt::AnchorRight); // Vertical - l->addAnchor(w1, Qt::AnchorTop, l, Qt::AnchorTop); - l->addAnchor(w1, Qt::AnchorBottom, l, Qt::AnchorBottom); - l->addAnchor(w2, Qt::AnchorTop, l, Qt::AnchorTop); - l->addAnchor(w2, Qt::AnchorBottom, l, Qt::AnchorBottom); - l->addAnchor(w3, Qt::AnchorTop, l, Qt::AnchorTop); - l->addAnchor(w3, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->addAnchors(l, w1, Qt::Vertical); + l->addAnchors(l, w2, Qt::Vertical); + l->addAnchors(l, w3, Qt::Vertical); QGraphicsAnchor *anchor = l->anchor(w3, Qt::AnchorRight, l, Qt::AnchorRight); anchor->setSpacing(10); -- cgit v0.12 From fd99d512fdb3864fe750015916b8e6a2a27401c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 7 Oct 2009 13:20:39 +0200 Subject: Store the floating items instead of the non-floating items. Makes the code a bit easier to read and speeds up setItemsGeometries() in the normal use-case. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 41 +++++++++++++----------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 6 ++-- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 34071cc..296930c 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -2008,14 +2008,20 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) Use all visited Anchors on findPaths() so we can identify non-float Items. */ -void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems(QSet visited, Orientation orientation) +void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems(const QSet &visited, Orientation orientation) { - m_nonFloatItems[orientation].clear(); + QSet nonFloating; foreach (const AnchorData *ad, visited) - identifyNonFloatItems_helper(ad, orientation); + identifyNonFloatItems_helper(ad, &nonFloating); + + QSet allItems; + foreach (QGraphicsLayoutItem *item, items) + allItems.insert(item); + m_floatItems[orientation] = allItems - nonFloating; } + /*! \internal @@ -2023,22 +2029,22 @@ void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems(QSet visi If the anchor is Sequential or Parallel, we must iterate on its children recursively until we reach internal anchors (items). */ -void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems_helper(const AnchorData *ad, Orientation orientation) +void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems_helper(const AnchorData *ad, QSet *nonFloatingItemsIdentifiedSoFar) { Q_Q(QGraphicsAnchorLayout); switch(ad->type) { case AnchorData::Normal: if (ad->from->m_item == ad->to->m_item && ad->to->m_item != q) - m_nonFloatItems[orientation].insert(ad->to->m_item); + nonFloatingItemsIdentifiedSoFar->insert(ad->to->m_item); break; case AnchorData::Sequential: foreach (const AnchorData *d, static_cast(ad)->m_edges) - identifyNonFloatItems_helper(d, orientation); + identifyNonFloatItems_helper(d, nonFloatingItemsIdentifiedSoFar); break; case AnchorData::Parallel: - identifyNonFloatItems_helper(static_cast(ad)->firstEdge, orientation); - identifyNonFloatItems_helper(static_cast(ad)->secondEdge, orientation); + identifyNonFloatItems_helper(static_cast(ad)->firstEdge, nonFloatingItemsIdentifiedSoFar); + identifyNonFloatItems_helper(static_cast(ad)->secondEdge, nonFloatingItemsIdentifiedSoFar); break; } } @@ -2070,7 +2076,10 @@ void QGraphicsAnchorLayoutPrivate::setItemsGeometries(const QRectF &geom) foreach (QGraphicsLayoutItem *item, items) { QRectF newGeom; QSizeF itemPreferredSize = item->effectiveSizeHint(Qt::PreferredSize); - if (m_nonFloatItems[Horizontal].contains(item)) { + if (m_floatItems[Horizontal].contains(item)) { + newGeom.setLeft(0); + newGeom.setRight(itemPreferredSize.width()); + } else { firstH = internalVertex(item, Qt::AnchorLeft); secondH = internalVertex(item, Qt::AnchorRight); @@ -2081,20 +2090,17 @@ void QGraphicsAnchorLayoutPrivate::setItemsGeometries(const QRectF &geom) newGeom.setLeft(right - secondH->distance); newGeom.setRight(right - firstH->distance); } - } else { - newGeom.setLeft(0); - newGeom.setRight(itemPreferredSize.width()); } - if (m_nonFloatItems[Vertical].contains(item)) { + if (m_floatItems[Vertical].contains(item)) { + newGeom.setTop(0); + newGeom.setBottom(itemPreferredSize.height()); + } else { firstV = internalVertex(item, Qt::AnchorTop); secondV = internalVertex(item, Qt::AnchorBottom); newGeom.setTop(top + firstV->distance); newGeom.setBottom(top + secondV->distance); - } else { - newGeom.setTop(0); - newGeom.setBottom(itemPreferredSize.height()); } item->setGeometry(newGeom); @@ -2554,8 +2560,7 @@ bool QGraphicsAnchorLayoutPrivate::hasConflicts() const QGraphicsAnchorLayoutPrivate *that = const_cast(this); that->calculateGraphs(); - bool floatConflict = (m_nonFloatItems[0].size() < items.size()) - || (m_nonFloatItems[1].size() < items.size()); + bool floatConflict = !m_floatItems[0].isEmpty() || !m_floatItems[1].isEmpty(); return graphHasConflicts[0] || graphHasConflicts[1] || floatConflict; } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 24b25de..0f045e5 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -443,8 +443,8 @@ public: void constraintsFromPaths(Orientation orientation); QList constraintsFromSizeHints(const QList &anchors); QList > getGraphParts(Orientation orientation); - void identifyNonFloatItems(QSet visited, Orientation orientation); - void identifyNonFloatItems_helper(const AnchorData *ad, Orientation orientation); + void identifyNonFloatItems(const QSet &visited, Orientation orientation); + void identifyNonFloatItems_helper(const AnchorData *ad, QSet *nonFloatingItemsIdentifiedSoFar); inline AnchorVertex *internalVertex(const QPair &itemEdge) const { @@ -511,7 +511,7 @@ public: // ### bool graphSimplified[2]; bool graphHasConflicts[2]; - QSet m_nonFloatItems[2]; + QSet m_floatItems[2]; uint calculateGraphCacheDirty : 1; }; -- cgit v0.12