From d1c2225c647e01f84c9aa80fc1d5d034754b94c5 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 8 Oct 2009 10:44:08 -0300 Subject: QGraphicsAnchorLayout: Ensure spacing is not negative Currently we do not support negative-sized anchors in the graph, instead we invert such anchors and make their value positive. We consider changing this sometime in the future but until then, spacing must be non-negative at all times. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 26 ++++++++++++++++++++++++ src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 7 +++++++ 2 files changed, 33 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index ffbb67c..9f4a19b 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -306,6 +306,13 @@ void QGraphicsAnchorLayout::addAnchors(QGraphicsLayoutItem *firstItem, void QGraphicsAnchorLayout::setHorizontalSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); + + // ### We don't support negative spacing yet + if (spacing < 0) { + spacing = 0; + qWarning() << "QGraphicsAnchorLayout does not support negative spacing."; + } + d->spacings[0] = spacing; invalidate(); } @@ -318,6 +325,13 @@ void QGraphicsAnchorLayout::setHorizontalSpacing(qreal spacing) void QGraphicsAnchorLayout::setVerticalSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); + + // ### We don't support negative spacing yet + if (spacing < 0) { + spacing = 0; + qWarning() << "QGraphicsAnchorLayout does not support negative spacing."; + } + d->spacings[1] = spacing; invalidate(); } @@ -327,11 +341,23 @@ void QGraphicsAnchorLayout::setVerticalSpacing(qreal spacing) If an item is anchored with no spacing associated with the anchor, it will use the default spacing. + + Currently QGraphicsAnchorLayout does not support negative default spacings. + \sa setHorizontalSpacing(), setVerticalSpacing() */ void QGraphicsAnchorLayout::setSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); + + // ### Currently we do not support negative anchors inside the graph. + // To avoid those being created by a negative spacing, we must + // make this test. + if (spacing < 0) { + spacing = 0; + qWarning() << "QGraphicsAnchorLayout does not support negative spacing."; + } + d->spacings[0] = d->spacings[1] = spacing; invalidate(); } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index e3cd4f9..9524289 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1550,6 +1550,13 @@ qreal QGraphicsAnchorLayoutPrivate::effectiveSpacing(Orientation orientation) co } } } + + // ### Currently we do not support negative anchors inside the graph. + // To avoid those being created by a negative style spacing, we must + // make this test. + if (s < 0) + s = 0; + return s; } -- cgit v0.12 From 6dc050639aeac0021bcb7864533779f2156e9d24 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 8 Oct 2009 14:30:57 -0300 Subject: QGraphicsAnchorLayout: Add test for 'infinite' max size widgets With four items anchored side by side, each of them being allowed to grow to QWIDGETSIZE_MAX, we have a situation of fair distribution because the layout itself can grow only to QWIDGETSIZE_MAX (and not four times this amount). This test identified an error on the expanding distribution logic. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- .../tst_qgraphicsanchorlayout.cpp | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 286ea2d..cf4e9b8 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -73,6 +73,7 @@ private slots: void expandingSequenceFairDistribution(); void expandingParallel(); void floatConflict(); + void infiniteMaxSizes(); }; class RectWidget : public QGraphicsWidget @@ -1585,5 +1586,53 @@ void tst_QGraphicsAnchorLayout::floatConflict() delete p; } +void tst_QGraphicsAnchorLayout::infiniteMaxSizes() +{ + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + QSizeF min(10, 10); + QSizeF pref(50, 10); + QSizeF max(QWIDGETSIZE_MAX, 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"); + + // + 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); + + a->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + c->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + QGraphicsWidget p; + p.setLayout(l); + + p.resize(200, 10); + QCOMPARE(a->geometry(), QRectF(0, 0, 50, 10)); + QCOMPARE(b->geometry(), QRectF(50, 0, 50, 10)); + QCOMPARE(c->geometry(), QRectF(100, 0, 50, 10)); + QCOMPARE(d->geometry(), QRectF(150, 0, 50, 10)); + + p.resize(1000, 10); + QCOMPARE(a->geometry(), QRectF(0, 0, 450, 10)); + QCOMPARE(b->geometry(), QRectF(450, 0, 50, 10)); + QCOMPARE(c->geometry(), QRectF(500, 0, 450, 10)); + QCOMPARE(d->geometry(), QRectF(950, 0, 50, 10)); + + qreal expMaxSize = (QWIDGETSIZE_MAX - 100.0) / 2; + p.resize(QWIDGETSIZE_MAX, 10); + QCOMPARE(a->geometry(), QRectF(0, 0, expMaxSize, 10)); + QCOMPARE(b->geometry(), QRectF(expMaxSize, 0, 50, 10)); + QCOMPARE(c->geometry(), QRectF(expMaxSize + 50, 0, expMaxSize, 10)); + QCOMPARE(d->geometry(), QRectF(QWIDGETSIZE_MAX - 50, 0, 50, 10)); +} + QTEST_MAIN(tst_QGraphicsAnchorLayout) #include "tst_qgraphicsanchorlayout.moc" -- cgit v0.12 From efb50541b57191e3fcfc8dac5a8e8a21658b75b1 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 8 Oct 2009 15:47:42 -0300 Subject: QGraphicsAnchorLayout: Fix sequential anchor distribution w/ expanding sizes After the addition of expanding SizePolicy, the distribution of sequential anchors became wrong. We must now account for three intervals of distribution instead of only two. This commit also unifies the logic used by the sequential group anchor and the edge interpolator. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 162 +++++++++++------------ 1 file changed, 76 insertions(+), 86 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 9524289..1935c27 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -250,44 +250,61 @@ void ParallelAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, 0 is at Preferred 1 is at Maximum */ -static qreal getFactor(qreal value, qreal min, qreal pref, qreal max) -{ - // ### Maybe remove some of the assertions? (since outside is asserting us) - Q_ASSERT(value > min || qFuzzyCompare(value, min)); - Q_ASSERT(value < max || qFuzzyCompare(value, max)); - - if (qFuzzyCompare(value, min)) { - return -1.0; - } else if (qFuzzyCompare(value, pref)) { - return 0.0; - } else if (qFuzzyCompare(value, max)) { - return 1.0; - } else if (value < pref) { - // Since value < pref and value != pref and min <= value, - // we can assert that min < pref. - Q_ASSERT(min < pref); - return (value - min) / (pref - min) - 1; +static QPair getFactor(qreal value, qreal min, + qreal pref, qreal exp, + qreal max) +{ + QGraphicsAnchorLayoutPrivate::Interval interval; + qreal lower; + qreal upper; + + if (value < pref) { + interval = QGraphicsAnchorLayoutPrivate::MinToPreferred; + lower = min; + upper = pref; + } else if (value < exp) { + interval = QGraphicsAnchorLayoutPrivate::PreferredToExpanding; + lower = pref; + upper = exp; } else { - // Since value > pref and value != pref and max >= value, - // we can assert that max > pref. - Q_ASSERT(max > pref); - return (value - pref) / (max - pref); + interval = QGraphicsAnchorLayoutPrivate::ExpandingToMax; + lower = exp; + upper = max; } + + qreal progress; + if (upper == lower) { + progress = 0; + } else { + progress = (value - lower) / (upper - lower); + } + + return qMakePair(interval, progress); } -static qreal getExpandingFactor(qreal expSize, qreal sizeAtPreferred, - qreal sizeAtExpanding, qreal sizeAtMaximum) +static qreal interpolate(const QPair &factor, + qreal min, qreal pref, + qreal exp, qreal max) { - const qreal lower = qMin(sizeAtPreferred, sizeAtMaximum); - const qreal upper = qMax(sizeAtPreferred, sizeAtMaximum); - const qreal boundedExpSize = qBound(lower, expSize, upper); + qreal lower; + qreal upper; - const qreal bandSize = sizeAtMaximum - boundedExpSize; - if (bandSize == 0) { - return 0; - } else { - return (sizeAtExpanding - boundedExpSize) / bandSize; + switch (factor.first) { + case QGraphicsAnchorLayoutPrivate::MinToPreferred: + lower = min; + upper = pref; + break; + case QGraphicsAnchorLayoutPrivate::PreferredToExpanding: + lower = pref; + upper = exp; + break; + case QGraphicsAnchorLayoutPrivate::ExpandingToMax: + lower = exp; + upper = max; + break; } + + return lower + factor.second * (upper - lower); } void SequentialAnchorData::updateChildrenSizes() @@ -307,27 +324,22 @@ void SequentialAnchorData::updateChildrenSizes() // Band here refers if the value is in the Minimum To Preferred // band (the lower band) or the Preferred To Maximum (the upper band). - 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); + const QPair minFactor = + getFactor(sizeAtMinimum, minSize, prefSize, expSize, maxSize); + const QPair prefFactor = + getFactor(sizeAtPreferred, minSize, prefSize, expSize, maxSize); + const QPair expFactor = + getFactor(sizeAtExpanding, minSize, prefSize, expSize, maxSize); + const QPair maxFactor = + getFactor(sizeAtMaximum, minSize, prefSize, expSize, maxSize); for (int i = 0; i < m_edges.count(); ++i) { AnchorData *e = m_edges.at(i); - qreal bandSize = minFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; - e->sizeAtMinimum = e->prefSize + bandSize * minFactor; - - bandSize = prefFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; - e->sizeAtPreferred = e->prefSize + bandSize * prefFactor; - - bandSize = maxFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; - e->sizeAtMaximum = e->prefSize + bandSize * maxFactor; - - 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->sizeAtMinimum = interpolate(minFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); + e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); + e->sizeAtExpanding = interpolate(expFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); + e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); e->updateChildrenSizes(); } @@ -2161,39 +2173,26 @@ void QGraphicsAnchorLayoutPrivate::calculateVertexPositions( \internal Calculate interpolation parameters based on current Layout Size. - Must once before calling "interpolateEdgeSize()" for each edge. + Must be called once before calling "interpolateEdgeSize()" for + the edges. */ void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( Orientation orientation) { Q_Q(QGraphicsAnchorLayout); - qreal lower, upper, current; - if (orientation == Horizontal) { - current = q->contentsRect().width(); - } else { - current = q->contentsRect().height(); - } + qreal current; + current = (orientation == Horizontal) ? q->contentsRect().width() : q->contentsRect().height(); - if (current < sizeHints[orientation][Qt::PreferredSize]) { - interpolationInterval[orientation] = MinToPreferred; - lower = sizeHints[orientation][Qt::MinimumSize]; - upper = sizeHints[orientation][Qt::PreferredSize]; - } 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]; - } + QPair result; + result = getFactor(current, + sizeHints[orientation][Qt::MinimumSize], + sizeHints[orientation][Qt::PreferredSize], + sizeAtExpanding[orientation], + sizeHints[orientation][Qt::MaximumSize]); - if (upper == lower) { - interpolationProgress[orientation] = 0; - } else { - interpolationProgress[orientation] = (current - lower) / (upper - lower); - } + interpolationInterval[orientation] = result.first; + interpolationProgress[orientation] = result.second; } /*! @@ -2220,20 +2219,11 @@ void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, AnchorData *edge, Orientation orientation) { - qreal lower, upper; - - if (interpolationInterval[orientation] == MinToPreferred) { - lower = edge->sizeAtMinimum; - upper = edge->sizeAtPreferred; - } else if (interpolationInterval[orientation] == PreferredToExpanding) { - lower = edge->sizeAtPreferred; - upper = edge->sizeAtExpanding; - } else { - lower = edge->sizeAtExpanding; - upper = edge->sizeAtMaximum; - } + const QPair factor(interpolationInterval[orientation], + interpolationProgress[orientation]); - qreal edgeDistance = (interpolationProgress[orientation] * (upper - lower)) + lower; + qreal edgeDistance = interpolate(factor, edge->sizeAtMinimum, edge->sizeAtPreferred, + edge->sizeAtExpanding, edge->sizeAtMaximum); Q_ASSERT(edge->from == base || edge->to == base); -- cgit v0.12 From cce279d015ccf19200153b43b7f378e25d0913d3 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Wed, 7 Oct 2009 19:34:09 -0300 Subject: QGAL: improve dump graph and add helper code The graph dumper function take a name, and added debug code (that need to be enabled manually) to generate dumps from the graph before and after calculation. This is useful to debug simplification. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 14 ++++++++++++-- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 1935c27..c921b60 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1584,9 +1584,19 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs() if (!calculateGraphCacheDirty) return; +#if defined(QT_DEBUG) && 0 + static int count = 0; + count++; + dumpGraph(QString::fromAscii("%1-before").arg(count)); +#endif + calculateGraphs(Horizontal); calculateGraphs(Vertical); +#if defined(QT_DEBUG) && 0 + dumpGraph(QString::fromAscii("%1-after").arg(count)); +#endif + calculateGraphCacheDirty = 0; } @@ -2558,9 +2568,9 @@ bool QGraphicsAnchorLayoutPrivate::hasConflicts() const } #ifdef QT_DEBUG -void QGraphicsAnchorLayoutPrivate::dumpGraph() +void QGraphicsAnchorLayoutPrivate::dumpGraph(const QString &name) { - QFile file(QString::fromAscii("anchorlayout.dot")); + QFile file(QString::fromAscii("anchorlayout.%1.dot").arg(name)); if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) qWarning("Could not write to %s", file.fileName().toLocal8Bit().constData()); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 24b25de..ea19ecb 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -478,7 +478,7 @@ public: bool hasConflicts() const; #ifdef QT_DEBUG - void dumpGraph(); + void dumpGraph(const QString &name = QString()); #endif -- cgit v0.12 From b6be5eb62a79f7c0d3718a05b18dcf69d2c21550 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Wed, 7 Oct 2009 19:41:32 -0300 Subject: QGAL: add a way to test whether simplex was used in a calculation This is one good way to track whether simplification is doing all its job or not. However it can only track cases where the graph simplify to only one anchor. For more complex cases the graph dumps are the way to go. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 8 ++++ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 4 ++ .../tst_qgraphicsanchorlayout.cpp | 54 +++++++++++++++++++++- 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index c921b60..b92d1c3 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1689,6 +1689,10 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( orientation == Horizontal ? "Horizontal" : "Vertical"); #endif +#ifdef QT_DEBUG + lastCalculationUsedSimplex[orientation] = true; +#endif + // Solve min and max size hints for trunk qreal min, max; feasible = solveMinMax(trunkConstraints, trunkPath, &min, &max); @@ -1738,6 +1742,10 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( orientation == Horizontal ? "Horizontal" : "Vertical"); #endif +#ifdef QT_DEBUG + lastCalculationUsedSimplex[orientation] = false; +#endif + // No Simplex is necessary because the path was simplified all the way to a single // anchor. Q_ASSERT(trunkPath.positives.count() == 1); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index ea19ecb..7d38e40 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -513,6 +513,10 @@ public: bool graphHasConflicts[2]; QSet m_nonFloatItems[2]; +#ifdef QT_DEBUG + bool lastCalculationUsedSimplex[2]; +#endif + uint calculateGraphCacheDirty : 1; }; diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index cf4e9b8..cef56b9 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -153,6 +153,15 @@ static bool layoutHasConflict(QGraphicsAnchorLayout *l) return QGraphicsAnchorLayoutPrivate::get(l)->hasConflicts(); } +static bool usedSimplex(QGraphicsAnchorLayout *l, Qt::Orientation o) +{ + QGraphicsAnchorLayoutPrivate::Orientation oo = (o == Qt::Horizontal) ? + QGraphicsAnchorLayoutPrivate::Horizontal : + QGraphicsAnchorLayoutPrivate::Vertical; + + return QGraphicsAnchorLayoutPrivate::get(l)->lastCalculationUsedSimplex[oo]; +} + void tst_QGraphicsAnchorLayout::simple() { QGraphicsWidget *w1 = createItem(); @@ -170,10 +179,14 @@ void tst_QGraphicsAnchorLayout::simple() l->addAnchors(l, w1, Qt::Vertical); l->addAnchors(l, w2, Qt::Vertical); + QCOMPARE(l->count(), 2); + QGraphicsWidget p; p.setLayout(l); + p.adjustSize(); - QCOMPARE(l->count(), 2); + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::simple_center() @@ -213,6 +226,9 @@ void tst_QGraphicsAnchorLayout::simple_center() QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize, QSizeF(200, 20)); + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + delete p; } @@ -310,6 +326,9 @@ void tst_QGraphicsAnchorLayout::layoutDirection() QCOMPARE(checkReverseDirection(p), true); + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + delete p; delete view; } @@ -398,6 +417,9 @@ void tst_QGraphicsAnchorLayout::diagonal() QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 75.0, 100.0)); QCOMPARE(p.size(), testA); + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + QCOMPARE(checkReverseDirection(&p), true); c->setMinimumWidth(300); @@ -494,6 +516,9 @@ void tst_QGraphicsAnchorLayout::parallel() QCOMPARE(e->geometry(), QRectF(375, 400, 175, 100)); QCOMPARE(f->geometry(), QRectF(550, 500, 200, 100)); QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::parallel2() @@ -539,6 +564,9 @@ void tst_QGraphicsAnchorLayout::parallel2() p.resize(layoutMaximumSize); QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::snake() @@ -749,6 +777,9 @@ void tst_QGraphicsAnchorLayout::fairDistribution() QCOMPARE(c->geometry(), QRectF(200.0, 200.0, 100.0, 100.0)); QCOMPARE(d->geometry(), QRectF(0.0, 300.0, 300.0, 100.0)); QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() @@ -825,6 +856,9 @@ void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() QCOMPARE(a->size(), d->size()); QCOMPARE(e->size().width(), 4 * a->size().width()); QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::proportionalPreferred() @@ -887,6 +921,9 @@ void tst_QGraphicsAnchorLayout::proportionalPreferred() QCOMPARE(a->size().width(), 10 * factor); QCOMPARE(c->size().width(), 14 * factor); QCOMPARE(p.size(), QSizeF(12, 400)); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::example() @@ -970,6 +1007,9 @@ void tst_QGraphicsAnchorLayout::example() QCOMPARE(a->size(), e->size()); QCOMPARE(b->size(), d->size()); QCOMPARE(f->size(), g->size()); + + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::setSpacing() @@ -1291,6 +1331,9 @@ void tst_QGraphicsAnchorLayout::sizePolicy() QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(100, 100)); QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(100, 100)); + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + delete p; delete view; } @@ -1381,6 +1424,9 @@ void tst_QGraphicsAnchorLayout::expandingSequence() QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize.width(), qreal(200)); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() @@ -1442,6 +1488,9 @@ void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize.width(), qreal(400)); + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + // 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. @@ -1464,6 +1513,9 @@ void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() QCOMPARE(b->geometry().size(), pref + QSizeF(25, 0)); QCOMPARE(c->geometry().size(), pref); QCOMPARE(d->geometry().size(), pref + QSizeF(50, 0)); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::expandingParallel() -- cgit v0.12 From bd47b9215a40ec4d9e290e18ac156f5e0cb5bbd2 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 8 Oct 2009 15:52:11 -0300 Subject: QGAL: avoid having to re-calculate lists of variables Change the functions solvePreferred() and solveExpanding() to take a list of variables, so they don't need to calculate them based on the list of constraints. That way, the trunk variables are calculated only once. This commit also reduce the scope of 'sizeHintConstraints' variable instead of clearing and reusing it. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 50 +++++++++++------------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 8 ++-- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index b92d1c3..fa53b66 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1671,9 +1671,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // of the "trunk" set of constraints and variables. // ### does trunk always exist? empty = trunk is the layout left->center->right QList trunkConstraints = parts[0]; - QList sizeHintConstraints; - sizeHintConstraints = constraintsFromSizeHints(getVariables(trunkConstraints)); - trunkConstraints += sizeHintConstraints; + QList trunkVariables = getVariables(trunkConstraints); // For minimum and maximum, use the path between the two layout sides as the // objective function. @@ -1695,26 +1693,24 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // Solve min and max size hints for trunk qreal min, max; - feasible = solveMinMax(trunkConstraints, trunkPath, &min, &max); + + QList sizeHintConstraints = constraintsFromSizeHints(trunkVariables); + QList allTrunkConstraints = trunkConstraints + sizeHintConstraints; + + feasible = solveMinMax(allTrunkConstraints, trunkPath, &min, &max); 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]; + solvePreferred(allTrunkConstraints, trunkVariables); // Solve for expanding. The objective function and the constraints from items - // are calculated internally. - solveExpanding(trunkConstraints); + // are calculated internally. Note that we don't include the sizeHintConstraints, since + // they have a different logic for solveExpanding(). + solveExpanding(trunkConstraints, trunkVariables); // Propagate the new sizes down the simplified graph, ie. tell the // group anchors to set their children anchors sizes. - - // ### we calculated variables already a few times, can't we reuse that? - QList trunkVariables = getVariables(trunkConstraints); - for (int i = 0; i < trunkVariables.count(); ++i) trunkVariables.at(i)->updateChildrenSizes(); @@ -1736,6 +1732,9 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( sizeHints[orientation][Qt::MaximumSize] = max; sizeAtExpanding[orientation] = expanding; } + + qDeleteAll(sizeHintConstraints); + } else { #if 0 qDebug("Simplex NOT used for trunk of %s", @@ -1766,10 +1765,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( sizeAtExpanding[orientation] = ad->sizeAtExpanding; } - // Delete the constraints, we won't use them anymore. - qDeleteAll(sizeHintConstraints); - sizeHintConstraints.clear(); - // For the other parts that not the trunk, solve only for the preferred size // that is the size they will remain at, since they are not stretched by the // layout. @@ -1781,9 +1776,9 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( QList partVariables = getVariables(partConstraints); Q_ASSERT(!partVariables.isEmpty()); - sizeHintConstraints = constraintsFromSizeHints(partVariables); + QList sizeHintConstraints = constraintsFromSizeHints(partVariables); partConstraints += sizeHintConstraints; - feasible &= solvePreferred(partConstraints); + feasible &= solvePreferred(partConstraints, partVariables); if (!feasible) break; @@ -1800,7 +1795,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // Delete the constraints, we won't use them anymore. qDeleteAll(sizeHintConstraints); - sizeHintConstraints.clear(); } } graphHasConflicts[orientation] = !feasible; @@ -2312,7 +2306,7 @@ void QGraphicsAnchorLayoutPrivate::interpolateSequentialEdges( interpolateEdge(prev, data->m_edges.last(), orientation); } -bool QGraphicsAnchorLayoutPrivate::solveMinMax(QList constraints, +bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList &constraints, GraphPath path, qreal *min, qreal *max) { QSimplex simplex; @@ -2353,9 +2347,9 @@ bool QGraphicsAnchorLayoutPrivate::solveMinMax(QList const return feasible; } -bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList constraints) +bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList &constraints, + const QList &variables) { - QList variables = getVariables(constraints); QList preferredConstraints; QList preferredVariables; QSimplexConstraint objective; @@ -2378,7 +2372,7 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList co // A + A_shrinker - A_grower = A_pref // for (int i = 0; i < variables.size(); ++i) { - AnchorData *ad = static_cast(variables[i]); + AnchorData *ad = variables[i]; if (ad->skipInPreferred) continue; @@ -2409,7 +2403,7 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList co // Save sizeAtPreferred results for (int i = 0; i < variables.size(); ++i) { - AnchorData *ad = static_cast(variables[i]); + AnchorData *ad = variables[i]; ad->sizeAtPreferred = ad->result; } @@ -2470,9 +2464,9 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList co 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) +void QGraphicsAnchorLayoutPrivate::solveExpanding(const QList &constraints, + const QList &variables) { - QList variables = getVariables(constraints); QList itemConstraints; QSimplexConstraint *objective = new QSimplexConstraint; bool hasExpanding = false; diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 7d38e40..474055a 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -471,10 +471,12 @@ public: Orientation orientation); // Linear Programming solver methods - bool solveMinMax(QList constraints, + bool solveMinMax(const QList &constraints, GraphPath path, qreal *min, qreal *max); - bool solvePreferred(QList constraints); - void solveExpanding(QList constraints); + bool solvePreferred(const QList &constraints, + const QList &variables); + void solveExpanding(const QList &constraints, + const QList &variables); bool hasConflicts() const; #ifdef QT_DEBUG -- cgit v0.12 From a9b1d44fdc9fac0be0016114afcad56e9d372c26 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 8 Oct 2009 16:55:42 -0300 Subject: QGAL: update the sizes of all anchors instead of doing it separatedly After calculations, update the size of all anchors in the simplified graph. Those updates were happening locally after each calculation (trunk and semifloats), however some anchors that were not involved in simplex calculation were missing. One concrete consequence of the previous behaviour is that semifloat parts that were simplified into just one anchor, didn't have the chance to set their sizeAt* values. One consequence of the new behaviour is one more test passing. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 28 +++++++++++++++------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 1 + .../tst_qgraphicsanchorlayout1.cpp | 2 -- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index fa53b66..017ddbf 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1709,11 +1709,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // they have a different logic for solveExpanding(). solveExpanding(trunkConstraints, trunkVariables); - // Propagate the new sizes down the simplified graph, ie. tell the - // group anchors to set their children anchors sizes. - for (int i = 0; i < trunkVariables.count(); ++i) - trunkVariables.at(i)->updateChildrenSizes(); - // Calculate and set the preferred and expanding sizes for the layout, // from the edge sizes that were calculated above. qreal pref(0.0); @@ -1756,9 +1751,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( ad->sizeAtExpanding = ad->expSize; ad->sizeAtMaximum = ad->maxSize; - // Propagate - ad->updateChildrenSizes(); - sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum; sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred; sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum; @@ -1790,13 +1782,17 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( ad->sizeAtMinimum = ad->sizeAtPreferred; ad->sizeAtExpanding = ad->sizeAtPreferred; ad->sizeAtMaximum = ad->sizeAtPreferred; - ad->updateChildrenSizes(); } // Delete the constraints, we won't use them anymore. qDeleteAll(sizeHintConstraints); } } + + // Propagate the new sizes down the simplified graph, ie. tell the + // group anchors to set their children anchors sizes. + updateAnchorSizes(orientation); + graphHasConflicts[orientation] = !feasible; // Clean up our data structures. They are not needed anymore since @@ -1907,6 +1903,20 @@ void QGraphicsAnchorLayoutPrivate::constraintsFromPaths(Orientation orientation) /*! \internal +*/ +void QGraphicsAnchorLayoutPrivate::updateAnchorSizes(Orientation orientation) +{ + Graph &g = graph[orientation]; + const QList > &vertices = g.connections(); + + for (int i = 0; i < vertices.count(); ++i) { + AnchorData *ad = g.edgeData(vertices.at(i).first, vertices.at(i).second); + ad->updateChildrenSizes(); + } +} + +/*! + \internal Create LP constraints for each anchor based on its minimum and maximum sizes, as specified in its size hints diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 474055a..940f143 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -441,6 +441,7 @@ public: void setAnchorSizeHintsFromItems(Orientation orientation); void findPaths(Orientation orientation); void constraintsFromPaths(Orientation orientation); + void updateAnchorSizes(Orientation orientation); QList constraintsFromSizeHints(const QList &anchors); QList > getGraphParts(Orientation orientation); void identifyNonFloatItems(QSet visited, Orientation orientation); diff --git a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp index 148b2c8..755d866 100644 --- a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp +++ b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp @@ -1711,8 +1711,6 @@ void tst_QGraphicsAnchorLayout1::testBasicLayout() // Validate for (int i = 0; i < result.count(); ++i) { - if (i == 1) - QEXPECT_FAIL("Two, mixed", "Works with simplification disabled.", Continue); const BasicLayoutTestResult item = result[i]; QCOMPARE(widgets[item.index]->geometry(), item.rect); } -- cgit v0.12 From cebaf35e3d79a7cde84a9ef5b0023e841075d5c1 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 8 Oct 2009 20:09:21 -0300 Subject: QGAL: refactor calculateGraphs() method Create calculateTrunk() and calculateNonTrunk() methods, that calculate sizes for anchors in different parts of the simplified graph (using simplex when needed). Also fixes a minor leak when the nontrunk part is non-feasible. The old code left the loop leaving the contents of 'sizeHintConstraints' not allocated. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 161 ++++++++++++----------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 7 + 2 files changed, 93 insertions(+), 75 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 017ddbf..1e9b579 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1600,7 +1600,8 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs() calculateGraphCacheDirty = 0; } -// ### remove me: +// ### Maybe getGraphParts could return the variables when traversing, at least +// for trunk... QList getVariables(QList constraints) { QSet variableSet; @@ -1664,8 +1665,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // 2) The floating or semi-floating anchors (items) that are those which // are connected to only one (or none) of the layout sides, thus are not // influenced by the layout size. - QList > parts; - parts = getGraphParts(orientation); + QList > parts = getGraphParts(orientation); // Now run the simplex solver to calculate Minimum, Preferred and Maximum sizes // of the "trunk" set of constraints and variables. @@ -1675,49 +1675,82 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // For minimum and maximum, use the path between the two layout sides as the // objective function. - - // Retrieve that path AnchorVertex *v = internalVertex(q, pickEdge(Qt::AnchorRight, orientation)); GraphPath trunkPath = graphPaths[orientation].value(v); + bool feasible = calculateTrunk(orientation, trunkPath, trunkConstraints, trunkVariables); + + // For the other parts that not the trunk, solve only for the preferred size + // that is the size they will remain at, since they are not stretched by the + // layout. + + // Skipping the first (trunk) + for (int i = 1; i < parts.count(); ++i) { + if (!feasible) + break; + + QList partConstraints = parts[i]; + QList partVariables = getVariables(partConstraints); + Q_ASSERT(!partVariables.isEmpty()); + feasible &= calculateNonTrunk(partConstraints, partVariables); + } + + // Propagate the new sizes down the simplified graph, ie. tell the + // group anchors to set their children anchors sizes. + updateAnchorSizes(orientation); + + graphHasConflicts[orientation] = !feasible; + + // Clean up our data structures. They are not needed anymore since + // distribution uses just interpolation. + qDeleteAll(constraints[orientation]); + constraints[orientation].clear(); + graphPaths[orientation].clear(); // ### +} + +/*! + \internal + + Calculate the sizes for all anchors which are part of the trunk. This works + on top of a (possibly) simplified graph. +*/ +bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const GraphPath &path, + const QList &constraints, + const QList &variables) +{ bool feasible = true; - if (!trunkConstraints.isEmpty()) { -#if 0 - qDebug("Simplex used for trunk of %s", - orientation == Horizontal ? "Horizontal" : "Vertical"); -#endif + bool needsSimplex = !constraints.isEmpty(); -#ifdef QT_DEBUG - lastCalculationUsedSimplex[orientation] = true; +#if 0 + qDebug("Simplex %s for trunk of %s", needsSimplex ? "used" : "NOT used", + orientation == Horizontal ? "Horizontal" : "Vertical"); #endif - // Solve min and max size hints for trunk - qreal min, max; + if (needsSimplex) { - QList sizeHintConstraints = constraintsFromSizeHints(trunkVariables); - QList allTrunkConstraints = trunkConstraints + sizeHintConstraints; + QList sizeHintConstraints = constraintsFromSizeHints(variables); + QList allConstraints = constraints + sizeHintConstraints; - feasible = solveMinMax(allTrunkConstraints, trunkPath, &min, &max); + // Solve min and max size hints + qreal min, max; + feasible = solveMinMax(allConstraints, path, &min, &max); if (feasible) { - // Solve for preferred. The objective function is calculated from the constraints - // and variables internally. - solvePreferred(allTrunkConstraints, trunkVariables); + solvePreferred(allConstraints, variables); - // Solve for expanding. The objective function and the constraints from items - // are calculated internally. Note that we don't include the sizeHintConstraints, since - // they have a different logic for solveExpanding(). - solveExpanding(trunkConstraints, trunkVariables); + // Note that we don't include the sizeHintConstraints, since they + // have a different logic for solveExpanding(). + solveExpanding(constraints, variables); // 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) { + foreach (const AnchorData *ad, path.positives) { pref += ad->sizeAtPreferred; expanding += ad->sizeAtExpanding; } - foreach (const AnchorData *ad, trunkPath.negatives) { + foreach (const AnchorData *ad, path.negatives) { pref -= ad->sizeAtPreferred; expanding -= ad->sizeAtExpanding; } @@ -1731,21 +1764,12 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( qDeleteAll(sizeHintConstraints); } else { -#if 0 - qDebug("Simplex NOT used for trunk of %s", - orientation == Horizontal ? "Horizontal" : "Vertical"); -#endif - -#ifdef QT_DEBUG - lastCalculationUsedSimplex[orientation] = false; -#endif - // No Simplex is necessary because the path was simplified all the way to a single // anchor. - Q_ASSERT(trunkPath.positives.count() == 1); - Q_ASSERT(trunkPath.negatives.count() == 0); + Q_ASSERT(path.positives.count() == 1); + Q_ASSERT(path.negatives.count() == 0); - AnchorData *ad = trunkPath.positives.toList()[0]; + AnchorData *ad = path.positives.toList()[0]; ad->sizeAtMinimum = ad->minSize; ad->sizeAtPreferred = ad->prefSize; ad->sizeAtExpanding = ad->expSize; @@ -1757,49 +1781,36 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( sizeAtExpanding[orientation] = ad->sizeAtExpanding; } - // For the other parts that not the trunk, solve only for the preferred size - // that is the size they will remain at, since they are not stretched by the - // layout. +#ifdef QT_DEBUG + lastCalculationUsedSimplex[orientation] = needsSimplex; +#endif - // Solve the other only for preferred, skip trunk - if (feasible) { - for (int i = 1; i < parts.count(); ++i) { - QList partConstraints = parts[i]; - QList partVariables = getVariables(partConstraints); - Q_ASSERT(!partVariables.isEmpty()); - - QList sizeHintConstraints = constraintsFromSizeHints(partVariables); - partConstraints += sizeHintConstraints; - feasible &= solvePreferred(partConstraints, partVariables); - if (!feasible) - break; + return feasible; +} - // Propagate size at preferred to other sizes. Semi-floats - // always will be in their sizeAtPreferred. - for (int j = 0; j < partVariables.count(); ++j) { - AnchorData *ad = partVariables[j]; - Q_ASSERT(ad); - ad->sizeAtMinimum = ad->sizeAtPreferred; - ad->sizeAtExpanding = ad->sizeAtPreferred; - ad->sizeAtMaximum = ad->sizeAtPreferred; - } +/*! + \internal +*/ +bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList &constraints, + const QList &variables) +{ + QList sizeHintConstraints = constraintsFromSizeHints(variables); + bool feasible = solvePreferred(constraints + sizeHintConstraints, variables); - // Delete the constraints, we won't use them anymore. - qDeleteAll(sizeHintConstraints); + if (feasible) { + // Propagate size at preferred to other sizes. Semi-floats always will be + // in their sizeAtPreferred. + for (int j = 0; j < variables.count(); ++j) { + AnchorData *ad = variables[j]; + Q_ASSERT(ad); + ad->sizeAtMinimum = ad->sizeAtPreferred; + ad->sizeAtExpanding = ad->sizeAtPreferred; + ad->sizeAtMaximum = ad->sizeAtPreferred; } } - // Propagate the new sizes down the simplified graph, ie. tell the - // group anchors to set their children anchors sizes. - updateAnchorSizes(orientation); - - graphHasConflicts[orientation] = !feasible; - - // Clean up our data structures. They are not needed anymore since - // distribution uses just interpolation. - qDeleteAll(constraints[orientation]); - constraints[orientation].clear(); - graphPaths[orientation].clear(); // ### + qDeleteAll(sizeHintConstraints); + return feasible; } /*! diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 940f143..2ced72d 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -438,6 +438,13 @@ public: void calculateGraphs(); void calculateGraphs(Orientation orientation); + + bool calculateTrunk(Orientation orientation, const GraphPath &trunkPath, + const QList &constraints, + const QList &variables); + bool calculateNonTrunk(const QList &constraints, + const QList &variables); + void setAnchorSizeHintsFromItems(Orientation orientation); void findPaths(Orientation orientation); void constraintsFromPaths(Orientation orientation); -- cgit v0.12 From 9ea66fb4d1e1ad36bcd1a0e4b7e7de348273e8ff Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 15 Oct 2009 10:34:34 +0200 Subject: Spreadsheet demo: fix the footer color. One of the footer cell lost his gray color in commit 7c12258485db8d Task-number: QTBUG-4821 Reviewed-by: Jan-Arve --- demos/spreadsheet/spreadsheet.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/demos/spreadsheet/spreadsheet.cpp b/demos/spreadsheet/spreadsheet.cpp index 7f057a2..00045c6 100644 --- a/demos/spreadsheet/spreadsheet.cpp +++ b/demos/spreadsheet/spreadsheet.cpp @@ -523,6 +523,7 @@ void SpreadSheet::setupContents() table->setItem(8, 2, new SpreadSheetItem("1240")); table->setItem(9, 2, new SpreadSheetItem()); + table->item(9, 2)->setBackgroundColor(Qt::lightGray); // column 3 table->setItem(0, 3, new SpreadSheetItem("Currency")); -- cgit v0.12 From 2055fe9eb514b79a34a0c216164cd1a11561be4f Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 15 Oct 2009 11:44:01 +0200 Subject: Stabilize QLineEdit test on X11 --- tests/auto/qlineedit/tst_qlineedit.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/auto/qlineedit/tst_qlineedit.cpp b/tests/auto/qlineedit/tst_qlineedit.cpp index 8368114..c676959 100644 --- a/tests/auto/qlineedit/tst_qlineedit.cpp +++ b/tests/auto/qlineedit/tst_qlineedit.cpp @@ -3304,41 +3304,45 @@ void tst_QLineEdit::task174640_editingFinished() layout->addWidget(le2); mw.show(); - QTest::qWait(200); + QApplication::setActiveWindow(&mw); mw.activateWindow(); + QTest::qWaitForWindowShown(&mw); QSignalSpy editingFinishedSpy(le1, SIGNAL(editingFinished())); le1->setFocus(); - QTest::qWait(200); - QVERIFY(le1->hasFocus()); + QTest::qWait(20); + QTRY_VERIFY(le1->hasFocus()); QCOMPARE(editingFinishedSpy.count(), 0); le2->setFocus(); - QTest::qWait(200); - QVERIFY(le2->hasFocus()); + QTest::qWait(20); + QTRY_VERIFY(le2->hasFocus()); QCOMPARE(editingFinishedSpy.count(), 1); editingFinishedSpy.clear(); le1->setFocus(); - QTest::qWait(200); - QVERIFY(le1->hasFocus()); + QTest::qWait(20); + QTRY_VERIFY(le1->hasFocus()); QMenu *testMenu1 = new QMenu(le1); testMenu1->addAction("foo"); testMenu1->addAction("bar"); testMenu1->show(); - QTest::qWait(200); + QTest::qWaitForWindowShown(testMenu1); + QTest::qWait(20); mw.activateWindow(); + delete testMenu1; QCOMPARE(editingFinishedSpy.count(), 0); - QVERIFY(le1->hasFocus()); + QTRY_VERIFY(le1->hasFocus()); QMenu *testMenu2 = new QMenu(le2); testMenu2->addAction("foo2"); testMenu2->addAction("bar2"); testMenu2->show(); - QTest::qWait(200); + QTest::qWaitForWindowShown(testMenu2); + QTest::qWait(20); mw.activateWindow(); delete testMenu2; QCOMPARE(editingFinishedSpy.count(), 1); -- cgit v0.12 From d5be015ddf268f195568d313dc2c5491a6a2db93 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 15 Oct 2009 13:55:32 +0200 Subject: Stabilize test on x11 --- tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp | 7 ++++--- tests/auto/qwidget/tst_qwidget.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp b/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp index 013a028..2426ce9 100644 --- a/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp +++ b/tests/auto/qgraphicsproxywidget/tst_qgraphicsproxywidget.cpp @@ -1287,6 +1287,7 @@ void tst_QGraphicsProxyWidget::paintEvent() QGraphicsView view(&scene); view.show(); QTest::qWaitForWindowShown(&view); + QTest::qWait(70); SubQGraphicsProxyWidget proxy; @@ -1297,14 +1298,14 @@ void tst_QGraphicsProxyWidget::paintEvent() w->show(); QTest::qWaitForWindowShown(w); QApplication::processEvents(); - QTest::qWait(50); + QTest::qWait(100); proxy.setWidget(w); scene.addItem(&proxy); //make sure we flush all the paint events QTest::qWait(70); QTRY_VERIFY(proxy.paintCount > 1); - QTest::qWait(70); + QTest::qWait(110); proxy.paintCount = 0; w->update(); @@ -2705,7 +2706,7 @@ void tst_QGraphicsProxyWidget::windowOpacity() view.show(); QTest::qWaitForWindowShown(&view); QApplication::sendPostedEvents(); - QTest::qWait(50); + QTest::qWait(150); qRegisterMetaType >("QList"); QSignalSpy signalSpy(&scene, SIGNAL(changed(const QList &))); diff --git a/tests/auto/qwidget/tst_qwidget.cpp b/tests/auto/qwidget/tst_qwidget.cpp index c000a12..daeee3d 100644 --- a/tests/auto/qwidget/tst_qwidget.cpp +++ b/tests/auto/qwidget/tst_qwidget.cpp @@ -7459,11 +7459,11 @@ void tst_QWidget::updateWhileMinimized() QTest::qWaitForWindowShown(&widget); QApplication::processEvents(); QTRY_VERIFY(widget.numPaintEvents > 0); - QTest::qWait(50); + QTest::qWait(150); // Minimize window. widget.showMinimized(); - QTest::qWait(70); + QTest::qWait(110); widget.reset(); @@ -8164,7 +8164,7 @@ public: static bool firstTime = true; if (firstTime) - QTimer::singleShot(150, this, SLOT(resizeMe())); + QTimer::singleShot(250, this, SLOT(resizeMe())); firstTime = false; } @@ -8181,7 +8181,7 @@ void tst_QWidget::moveInResizeEvent() testWidget.setGeometry(50, 50, 200, 200); testWidget.show(); QTest::qWaitForWindowShown(&testWidget); - QTest::qWait(160); + QTest::qWait(300); QRect expectedGeometry(100,100, 100, 100); QTRY_COMPARE(testWidget.geometry(), expectedGeometry); -- cgit v0.12 From 038434b66a473c08e78dc40376c5890dd1af938f Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Thu, 15 Oct 2009 14:14:19 +0200 Subject: Fixed updating problems QListView with IconMode The updated rectangle coordinates didn't use the viewport offsets. Reviewed-by: Olivier Task-number: QTBUG-4819 --- src/gui/itemviews/qlistview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp index 9b0b00f..243f542 100644 --- a/src/gui/itemviews/qlistview.cpp +++ b/src/gui/itemviews/qlistview.cpp @@ -2490,7 +2490,7 @@ bool QIconModeViewBase::filterDropEvent(QDropEvent *e) for (int i = 0; i < indexes.count(); ++i) { QModelIndex index = indexes.at(i); QRect rect = dd->rectForIndex(index); - viewport()->update(mapToViewport(rect)); + viewport()->update(dd->mapToViewport(rect, false)); QPoint dest = rect.topLeft() + delta; if (qq->isRightToLeft()) dest.setX(dd->flipX(dest.x()) - rect.width()); -- cgit v0.12 From 2eee73e843ba7b4c6e83bd6bfaa08994520c417d Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 15 Oct 2009 15:45:14 +0200 Subject: Compilation of the QGraphicsAnchorLayout test on 64bit + stabilize the linear layout test --- tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp | 2 +- tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp index 148b2c8..e69a718 100644 --- a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp +++ b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp @@ -435,7 +435,7 @@ void tst_QGraphicsAnchorLayout1::testAddAndRemoveAnchor() layout->removeAnchor(layout, Qt::AnchorLeft, widget1, Qt::AnchorLeft); QCOMPARE( layout->count(), 4 ); - QCOMPARE( (int)widget1->parentLayoutItem(), 0 ); + QVERIFY( !widget1->parentLayoutItem() ); // test that item is not removed from layout if other anchors remain set layout->setAnchor(widget2, Qt::AnchorLeft, widget3, Qt::AnchorRight, 10); diff --git a/tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp b/tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp index 4f28df4..8e7bb45 100644 --- a/tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp +++ b/tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp @@ -146,7 +146,7 @@ void tst_QGraphicsLinearLayout::initTestCase() // since the style will influence the results, we have to ensure // that the tests are run using the same style on all platforms #ifdef Q_WS_S60 - QApplication::setStyle(new QWindowsStyle); + QApplication::setStyle(new QWindowsStyle); #else QApplication::setStyle(new QPlastiqueStyle); #endif @@ -332,6 +332,7 @@ void tst_QGraphicsLinearLayout::alignment() widget->resize(newSize); view.show(); widget->show(); + QTest::qWaitForWindowShown(&view); QApplication::processEvents(); int x = 0; -- cgit v0.12 From f81a563c74eece173a0cf1d6c3243604c0db0a5b Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 8 Oct 2009 10:44:08 -0300 Subject: QGraphicsAnchorLayout: Ensure spacing is not negative Currently we do not support negative-sized anchors in the graph, instead we invert such anchors and make their value positive. We consider changing this sometime in the future but until then, spacing must be non-negative at all times. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 26 ++++++++++++++++++++++++ src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 7 +++++++ 2 files changed, 33 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index ffbb67c..9f4a19b 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -306,6 +306,13 @@ void QGraphicsAnchorLayout::addAnchors(QGraphicsLayoutItem *firstItem, void QGraphicsAnchorLayout::setHorizontalSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); + + // ### We don't support negative spacing yet + if (spacing < 0) { + spacing = 0; + qWarning() << "QGraphicsAnchorLayout does not support negative spacing."; + } + d->spacings[0] = spacing; invalidate(); } @@ -318,6 +325,13 @@ void QGraphicsAnchorLayout::setHorizontalSpacing(qreal spacing) void QGraphicsAnchorLayout::setVerticalSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); + + // ### We don't support negative spacing yet + if (spacing < 0) { + spacing = 0; + qWarning() << "QGraphicsAnchorLayout does not support negative spacing."; + } + d->spacings[1] = spacing; invalidate(); } @@ -327,11 +341,23 @@ void QGraphicsAnchorLayout::setVerticalSpacing(qreal spacing) If an item is anchored with no spacing associated with the anchor, it will use the default spacing. + + Currently QGraphicsAnchorLayout does not support negative default spacings. + \sa setHorizontalSpacing(), setVerticalSpacing() */ void QGraphicsAnchorLayout::setSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); + + // ### Currently we do not support negative anchors inside the graph. + // To avoid those being created by a negative spacing, we must + // make this test. + if (spacing < 0) { + spacing = 0; + qWarning() << "QGraphicsAnchorLayout does not support negative spacing."; + } + d->spacings[0] = d->spacings[1] = spacing; invalidate(); } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index f9b5c8c..1cbea8e 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1550,6 +1550,13 @@ qreal QGraphicsAnchorLayoutPrivate::effectiveSpacing(Orientation orientation) co } } } + + // ### Currently we do not support negative anchors inside the graph. + // To avoid those being created by a negative style spacing, we must + // make this test. + if (s < 0) + s = 0; + return s; } -- cgit v0.12 From 80ffc2fa1766aa8009a30de495fc11ab6690bd87 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 8 Oct 2009 14:30:57 -0300 Subject: QGraphicsAnchorLayout: Add test for 'infinite' max size widgets With four items anchored side by side, each of them being allowed to grow to QWIDGETSIZE_MAX, we have a situation of fair distribution because the layout itself can grow only to QWIDGETSIZE_MAX (and not four times this amount). This test identified an error on the expanding distribution logic. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- .../tst_qgraphicsanchorlayout.cpp | 49 ++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 286ea2d..cf4e9b8 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -73,6 +73,7 @@ private slots: void expandingSequenceFairDistribution(); void expandingParallel(); void floatConflict(); + void infiniteMaxSizes(); }; class RectWidget : public QGraphicsWidget @@ -1585,5 +1586,53 @@ void tst_QGraphicsAnchorLayout::floatConflict() delete p; } +void tst_QGraphicsAnchorLayout::infiniteMaxSizes() +{ + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->setSpacing(0); + + QSizeF min(10, 10); + QSizeF pref(50, 10); + QSizeF max(QWIDGETSIZE_MAX, 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"); + + // + 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); + + a->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + c->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + QGraphicsWidget p; + p.setLayout(l); + + p.resize(200, 10); + QCOMPARE(a->geometry(), QRectF(0, 0, 50, 10)); + QCOMPARE(b->geometry(), QRectF(50, 0, 50, 10)); + QCOMPARE(c->geometry(), QRectF(100, 0, 50, 10)); + QCOMPARE(d->geometry(), QRectF(150, 0, 50, 10)); + + p.resize(1000, 10); + QCOMPARE(a->geometry(), QRectF(0, 0, 450, 10)); + QCOMPARE(b->geometry(), QRectF(450, 0, 50, 10)); + QCOMPARE(c->geometry(), QRectF(500, 0, 450, 10)); + QCOMPARE(d->geometry(), QRectF(950, 0, 50, 10)); + + qreal expMaxSize = (QWIDGETSIZE_MAX - 100.0) / 2; + p.resize(QWIDGETSIZE_MAX, 10); + QCOMPARE(a->geometry(), QRectF(0, 0, expMaxSize, 10)); + QCOMPARE(b->geometry(), QRectF(expMaxSize, 0, 50, 10)); + QCOMPARE(c->geometry(), QRectF(expMaxSize + 50, 0, expMaxSize, 10)); + QCOMPARE(d->geometry(), QRectF(QWIDGETSIZE_MAX - 50, 0, 50, 10)); +} + QTEST_MAIN(tst_QGraphicsAnchorLayout) #include "tst_qgraphicsanchorlayout.moc" -- cgit v0.12 From 70bfdcee2e09cb491a5dbcc37caaf17076893339 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 8 Oct 2009 15:47:42 -0300 Subject: QGraphicsAnchorLayout: Fix sequential anchor distribution w/ expanding sizes After the addition of expanding SizePolicy, the distribution of sequential anchors became wrong. We must now account for three intervals of distribution instead of only two. This commit also unifies the logic used by the sequential group anchor and the edge interpolator. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 162 +++++++++++------------ 1 file changed, 76 insertions(+), 86 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 1cbea8e..ff42cb5 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -250,44 +250,61 @@ void ParallelAnchorData::refreshSizeHints_helper(qreal effectiveSpacing, 0 is at Preferred 1 is at Maximum */ -static qreal getFactor(qreal value, qreal min, qreal pref, qreal max) -{ - // ### Maybe remove some of the assertions? (since outside is asserting us) - Q_ASSERT(value > min || qFuzzyCompare(value, min)); - Q_ASSERT(value < max || qFuzzyCompare(value, max)); - - if (qFuzzyCompare(value, min)) { - return -1.0; - } else if (qFuzzyCompare(value, pref)) { - return 0.0; - } else if (qFuzzyCompare(value, max)) { - return 1.0; - } else if (value < pref) { - // Since value < pref and value != pref and min <= value, - // we can assert that min < pref. - Q_ASSERT(min < pref); - return (value - min) / (pref - min) - 1; +static QPair getFactor(qreal value, qreal min, + qreal pref, qreal exp, + qreal max) +{ + QGraphicsAnchorLayoutPrivate::Interval interval; + qreal lower; + qreal upper; + + if (value < pref) { + interval = QGraphicsAnchorLayoutPrivate::MinToPreferred; + lower = min; + upper = pref; + } else if (value < exp) { + interval = QGraphicsAnchorLayoutPrivate::PreferredToExpanding; + lower = pref; + upper = exp; } else { - // Since value > pref and value != pref and max >= value, - // we can assert that max > pref. - Q_ASSERT(max > pref); - return (value - pref) / (max - pref); + interval = QGraphicsAnchorLayoutPrivate::ExpandingToMax; + lower = exp; + upper = max; } + + qreal progress; + if (upper == lower) { + progress = 0; + } else { + progress = (value - lower) / (upper - lower); + } + + return qMakePair(interval, progress); } -static qreal getExpandingFactor(const qreal &expSize, const qreal &sizeAtPreferred, - const qreal &sizeAtExpanding, const qreal &sizeAtMaximum) +static qreal interpolate(const QPair &factor, + qreal min, qreal pref, + qreal exp, qreal max) { - const qreal lower = qMin(sizeAtPreferred, sizeAtMaximum); - const qreal upper = qMax(sizeAtPreferred, sizeAtMaximum); - const qreal boundedExpSize = qBound(lower, expSize, upper); + qreal lower; + qreal upper; - const qreal bandSize = sizeAtMaximum - boundedExpSize; - if (bandSize == 0) { - return 0; - } else { - return (sizeAtExpanding - boundedExpSize) / bandSize; + switch (factor.first) { + case QGraphicsAnchorLayoutPrivate::MinToPreferred: + lower = min; + upper = pref; + break; + case QGraphicsAnchorLayoutPrivate::PreferredToExpanding: + lower = pref; + upper = exp; + break; + case QGraphicsAnchorLayoutPrivate::ExpandingToMax: + lower = exp; + upper = max; + break; } + + return lower + factor.second * (upper - lower); } void SequentialAnchorData::updateChildrenSizes() @@ -307,27 +324,22 @@ void SequentialAnchorData::updateChildrenSizes() // Band here refers if the value is in the Minimum To Preferred // band (the lower band) or the Preferred To Maximum (the upper band). - 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); + const QPair minFactor = + getFactor(sizeAtMinimum, minSize, prefSize, expSize, maxSize); + const QPair prefFactor = + getFactor(sizeAtPreferred, minSize, prefSize, expSize, maxSize); + const QPair expFactor = + getFactor(sizeAtExpanding, minSize, prefSize, expSize, maxSize); + const QPair maxFactor = + getFactor(sizeAtMaximum, minSize, prefSize, expSize, maxSize); for (int i = 0; i < m_edges.count(); ++i) { AnchorData *e = m_edges.at(i); - qreal bandSize = minFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; - e->sizeAtMinimum = e->prefSize + bandSize * minFactor; - - bandSize = prefFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; - e->sizeAtPreferred = e->prefSize + bandSize * prefFactor; - - bandSize = maxFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; - e->sizeAtMaximum = e->prefSize + bandSize * maxFactor; - - 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->sizeAtMinimum = interpolate(minFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); + e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); + e->sizeAtExpanding = interpolate(expFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); + e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->prefSize, e->expSize, e->maxSize); e->updateChildrenSizes(); } @@ -2167,39 +2179,26 @@ void QGraphicsAnchorLayoutPrivate::calculateVertexPositions( \internal Calculate interpolation parameters based on current Layout Size. - Must once before calling "interpolateEdgeSize()" for each edge. + Must be called once before calling "interpolateEdgeSize()" for + the edges. */ void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( Orientation orientation) { Q_Q(QGraphicsAnchorLayout); - qreal lower, upper, current; - if (orientation == Horizontal) { - current = q->contentsRect().width(); - } else { - current = q->contentsRect().height(); - } + qreal current; + current = (orientation == Horizontal) ? q->contentsRect().width() : q->contentsRect().height(); - if (current < sizeHints[orientation][Qt::PreferredSize]) { - interpolationInterval[orientation] = MinToPreferred; - lower = sizeHints[orientation][Qt::MinimumSize]; - upper = sizeHints[orientation][Qt::PreferredSize]; - } 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]; - } + QPair result; + result = getFactor(current, + sizeHints[orientation][Qt::MinimumSize], + sizeHints[orientation][Qt::PreferredSize], + sizeAtExpanding[orientation], + sizeHints[orientation][Qt::MaximumSize]); - if (upper == lower) { - interpolationProgress[orientation] = 0; - } else { - interpolationProgress[orientation] = (current - lower) / (upper - lower); - } + interpolationInterval[orientation] = result.first; + interpolationProgress[orientation] = result.second; } /*! @@ -2226,20 +2225,11 @@ void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, AnchorData *edge, Orientation orientation) { - qreal lower, upper; - - if (interpolationInterval[orientation] == MinToPreferred) { - lower = edge->sizeAtMinimum; - upper = edge->sizeAtPreferred; - } else if (interpolationInterval[orientation] == PreferredToExpanding) { - lower = edge->sizeAtPreferred; - upper = edge->sizeAtExpanding; - } else { - lower = edge->sizeAtExpanding; - upper = edge->sizeAtMaximum; - } + const QPair factor(interpolationInterval[orientation], + interpolationProgress[orientation]); - qreal edgeDistance = (interpolationProgress[orientation] * (upper - lower)) + lower; + qreal edgeDistance = interpolate(factor, edge->sizeAtMinimum, edge->sizeAtPreferred, + edge->sizeAtExpanding, edge->sizeAtMaximum); Q_ASSERT(edge->from == base || edge->to == base); -- cgit v0.12 From a0b0e6b086b493aa833596a7d228ca7eb27be47e Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Wed, 7 Oct 2009 19:34:09 -0300 Subject: QGAL: improve dump graph and add helper code The graph dumper function take a name, and added debug code (that need to be enabled manually) to generate dumps from the graph before and after calculation. This is useful to debug simplification. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 14 ++++++++++++-- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index ff42cb5..f090aa3 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1584,9 +1584,19 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs() if (!calculateGraphCacheDirty) return; +#if defined(QT_DEBUG) && 0 + static int count = 0; + count++; + dumpGraph(QString::fromAscii("%1-before").arg(count)); +#endif + calculateGraphs(Horizontal); calculateGraphs(Vertical); +#if defined(QT_DEBUG) && 0 + dumpGraph(QString::fromAscii("%1-after").arg(count)); +#endif + calculateGraphCacheDirty = 0; } @@ -2563,9 +2573,9 @@ bool QGraphicsAnchorLayoutPrivate::hasConflicts() const } #ifdef QT_DEBUG -void QGraphicsAnchorLayoutPrivate::dumpGraph() +void QGraphicsAnchorLayoutPrivate::dumpGraph(const QString &name) { - QFile file(QString::fromAscii("anchorlayout.dot")); + QFile file(QString::fromAscii("anchorlayout.%1.dot").arg(name)); if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) qWarning("Could not write to %s", file.fileName().toLocal8Bit().constData()); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 9ac0e19..9b50661 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -478,7 +478,7 @@ public: bool hasConflicts() const; #ifdef QT_DEBUG - void dumpGraph(); + void dumpGraph(const QString &name = QString()); #endif -- cgit v0.12 From 1501429a78d647f922dfdb4432fbfac333143c91 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Wed, 7 Oct 2009 19:41:32 -0300 Subject: QGAL: add a way to test whether simplex was used in a calculation This is one good way to track whether simplification is doing all its job or not. However it can only track cases where the graph simplify to only one anchor. For more complex cases the graph dumps are the way to go. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 8 ++++ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 4 ++ .../tst_qgraphicsanchorlayout.cpp | 54 +++++++++++++++++++++- 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index f090aa3..b61405f 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1689,6 +1689,10 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( orientation == Horizontal ? "Horizontal" : "Vertical"); #endif +#ifdef QT_DEBUG + lastCalculationUsedSimplex[orientation] = true; +#endif + // Solve min and max size hints for trunk qreal min, max; feasible = solveMinMax(trunkConstraints, trunkPath, &min, &max); @@ -1738,6 +1742,10 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( orientation == Horizontal ? "Horizontal" : "Vertical"); #endif +#ifdef QT_DEBUG + lastCalculationUsedSimplex[orientation] = false; +#endif + // No Simplex is necessary because the path was simplified all the way to a single // anchor. Q_ASSERT(trunkPath.positives.count() == 1); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 9b50661..b14bc62 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -513,6 +513,10 @@ public: bool graphHasConflicts[2]; QSet m_floatItems[2]; +#ifdef QT_DEBUG + bool lastCalculationUsedSimplex[2]; +#endif + uint calculateGraphCacheDirty : 1; }; diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index cf4e9b8..cef56b9 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -153,6 +153,15 @@ static bool layoutHasConflict(QGraphicsAnchorLayout *l) return QGraphicsAnchorLayoutPrivate::get(l)->hasConflicts(); } +static bool usedSimplex(QGraphicsAnchorLayout *l, Qt::Orientation o) +{ + QGraphicsAnchorLayoutPrivate::Orientation oo = (o == Qt::Horizontal) ? + QGraphicsAnchorLayoutPrivate::Horizontal : + QGraphicsAnchorLayoutPrivate::Vertical; + + return QGraphicsAnchorLayoutPrivate::get(l)->lastCalculationUsedSimplex[oo]; +} + void tst_QGraphicsAnchorLayout::simple() { QGraphicsWidget *w1 = createItem(); @@ -170,10 +179,14 @@ void tst_QGraphicsAnchorLayout::simple() l->addAnchors(l, w1, Qt::Vertical); l->addAnchors(l, w2, Qt::Vertical); + QCOMPARE(l->count(), 2); + QGraphicsWidget p; p.setLayout(l); + p.adjustSize(); - QCOMPARE(l->count(), 2); + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::simple_center() @@ -213,6 +226,9 @@ void tst_QGraphicsAnchorLayout::simple_center() QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize, QSizeF(200, 20)); + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + delete p; } @@ -310,6 +326,9 @@ void tst_QGraphicsAnchorLayout::layoutDirection() QCOMPARE(checkReverseDirection(p), true); + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + delete p; delete view; } @@ -398,6 +417,9 @@ void tst_QGraphicsAnchorLayout::diagonal() QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 75.0, 100.0)); QCOMPARE(p.size(), testA); + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + QCOMPARE(checkReverseDirection(&p), true); c->setMinimumWidth(300); @@ -494,6 +516,9 @@ void tst_QGraphicsAnchorLayout::parallel() QCOMPARE(e->geometry(), QRectF(375, 400, 175, 100)); QCOMPARE(f->geometry(), QRectF(550, 500, 200, 100)); QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::parallel2() @@ -539,6 +564,9 @@ void tst_QGraphicsAnchorLayout::parallel2() p.resize(layoutMaximumSize); QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::snake() @@ -749,6 +777,9 @@ void tst_QGraphicsAnchorLayout::fairDistribution() QCOMPARE(c->geometry(), QRectF(200.0, 200.0, 100.0, 100.0)); QCOMPARE(d->geometry(), QRectF(0.0, 300.0, 300.0, 100.0)); QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() @@ -825,6 +856,9 @@ void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() QCOMPARE(a->size(), d->size()); QCOMPARE(e->size().width(), 4 * a->size().width()); QCOMPARE(p.size(), layoutMaximumSize); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::proportionalPreferred() @@ -887,6 +921,9 @@ void tst_QGraphicsAnchorLayout::proportionalPreferred() QCOMPARE(a->size().width(), 10 * factor); QCOMPARE(c->size().width(), 14 * factor); QCOMPARE(p.size(), QSizeF(12, 400)); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::example() @@ -970,6 +1007,9 @@ void tst_QGraphicsAnchorLayout::example() QCOMPARE(a->size(), e->size()); QCOMPARE(b->size(), d->size()); QCOMPARE(f->size(), g->size()); + + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::setSpacing() @@ -1291,6 +1331,9 @@ void tst_QGraphicsAnchorLayout::sizePolicy() QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(100, 100)); QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(100, 100)); + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + delete p; delete view; } @@ -1381,6 +1424,9 @@ void tst_QGraphicsAnchorLayout::expandingSequence() QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize.width(), qreal(200)); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() @@ -1442,6 +1488,9 @@ void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize.width(), qreal(400)); + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + // 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. @@ -1464,6 +1513,9 @@ void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() QCOMPARE(b->geometry().size(), pref + QSizeF(25, 0)); QCOMPARE(c->geometry().size(), pref); QCOMPARE(d->geometry().size(), pref + QSizeF(50, 0)); + + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); } void tst_QGraphicsAnchorLayout::expandingParallel() -- cgit v0.12 From eb2717ccbb20334b52aea269303258fe6236c117 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 8 Oct 2009 15:52:11 -0300 Subject: QGAL: avoid having to re-calculate lists of variables Change the functions solvePreferred() and solveExpanding() to take a list of variables, so they don't need to calculate them based on the list of constraints. That way, the trunk variables are calculated only once. This commit also reduce the scope of 'sizeHintConstraints' variable instead of clearing and reusing it. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 50 +++++++++++------------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 8 ++-- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index b61405f..485743e 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1671,9 +1671,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // of the "trunk" set of constraints and variables. // ### does trunk always exist? empty = trunk is the layout left->center->right QList trunkConstraints = parts[0]; - QList sizeHintConstraints; - sizeHintConstraints = constraintsFromSizeHints(getVariables(trunkConstraints)); - trunkConstraints += sizeHintConstraints; + QList trunkVariables = getVariables(trunkConstraints); // For minimum and maximum, use the path between the two layout sides as the // objective function. @@ -1695,26 +1693,24 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // Solve min and max size hints for trunk qreal min, max; - feasible = solveMinMax(trunkConstraints, trunkPath, &min, &max); + + QList sizeHintConstraints = constraintsFromSizeHints(trunkVariables); + QList allTrunkConstraints = trunkConstraints + sizeHintConstraints; + + feasible = solveMinMax(allTrunkConstraints, trunkPath, &min, &max); 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]; + solvePreferred(allTrunkConstraints, trunkVariables); // Solve for expanding. The objective function and the constraints from items - // are calculated internally. - solveExpanding(trunkConstraints); + // are calculated internally. Note that we don't include the sizeHintConstraints, since + // they have a different logic for solveExpanding(). + solveExpanding(trunkConstraints, trunkVariables); // Propagate the new sizes down the simplified graph, ie. tell the // group anchors to set their children anchors sizes. - - // ### we calculated variables already a few times, can't we reuse that? - QList trunkVariables = getVariables(trunkConstraints); - for (int i = 0; i < trunkVariables.count(); ++i) trunkVariables.at(i)->updateChildrenSizes(); @@ -1736,6 +1732,9 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( sizeHints[orientation][Qt::MaximumSize] = max; sizeAtExpanding[orientation] = expanding; } + + qDeleteAll(sizeHintConstraints); + } else { #if 0 qDebug("Simplex NOT used for trunk of %s", @@ -1766,10 +1765,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( sizeAtExpanding[orientation] = ad->sizeAtExpanding; } - // Delete the constraints, we won't use them anymore. - qDeleteAll(sizeHintConstraints); - sizeHintConstraints.clear(); - // For the other parts that not the trunk, solve only for the preferred size // that is the size they will remain at, since they are not stretched by the // layout. @@ -1781,9 +1776,9 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( QList partVariables = getVariables(partConstraints); Q_ASSERT(!partVariables.isEmpty()); - sizeHintConstraints = constraintsFromSizeHints(partVariables); + QList sizeHintConstraints = constraintsFromSizeHints(partVariables); partConstraints += sizeHintConstraints; - feasible &= solvePreferred(partConstraints); + feasible &= solvePreferred(partConstraints, partVariables); if (!feasible) break; @@ -1800,7 +1795,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // Delete the constraints, we won't use them anymore. qDeleteAll(sizeHintConstraints); - sizeHintConstraints.clear(); } } graphHasConflicts[orientation] = !feasible; @@ -2318,7 +2312,7 @@ void QGraphicsAnchorLayoutPrivate::interpolateSequentialEdges( interpolateEdge(prev, data->m_edges.last(), orientation); } -bool QGraphicsAnchorLayoutPrivate::solveMinMax(QList constraints, +bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList &constraints, GraphPath path, qreal *min, qreal *max) { QSimplex simplex; @@ -2359,9 +2353,9 @@ bool QGraphicsAnchorLayoutPrivate::solveMinMax(QList const return feasible; } -bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList constraints) +bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList &constraints, + const QList &variables) { - QList variables = getVariables(constraints); QList preferredConstraints; QList preferredVariables; QSimplexConstraint objective; @@ -2384,7 +2378,7 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList co // A + A_shrinker - A_grower = A_pref // for (int i = 0; i < variables.size(); ++i) { - AnchorData *ad = static_cast(variables[i]); + AnchorData *ad = variables[i]; if (ad->skipInPreferred) continue; @@ -2415,7 +2409,7 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList co // Save sizeAtPreferred results for (int i = 0; i < variables.size(); ++i) { - AnchorData *ad = static_cast(variables[i]); + AnchorData *ad = variables[i]; ad->sizeAtPreferred = ad->result; } @@ -2476,9 +2470,9 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList co 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) +void QGraphicsAnchorLayoutPrivate::solveExpanding(const QList &constraints, + const QList &variables) { - QList variables = getVariables(constraints); QList itemConstraints; QSimplexConstraint *objective = new QSimplexConstraint; bool hasExpanding = false; diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index b14bc62..1d7b758 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -471,10 +471,12 @@ public: Orientation orientation); // Linear Programming solver methods - bool solveMinMax(QList constraints, + bool solveMinMax(const QList &constraints, GraphPath path, qreal *min, qreal *max); - bool solvePreferred(QList constraints); - void solveExpanding(QList constraints); + bool solvePreferred(const QList &constraints, + const QList &variables); + void solveExpanding(const QList &constraints, + const QList &variables); bool hasConflicts() const; #ifdef QT_DEBUG -- cgit v0.12 From e7e67b26fcd6c53dd822b1ae36074d418d432b4a Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 8 Oct 2009 16:55:42 -0300 Subject: QGAL: update the sizes of all anchors instead of doing it separatedly After calculations, update the size of all anchors in the simplified graph. Those updates were happening locally after each calculation (trunk and semifloats), however some anchors that were not involved in simplex calculation were missing. One concrete consequence of the previous behaviour is that semifloat parts that were simplified into just one anchor, didn't have the chance to set their sizeAt* values. One consequence of the new behaviour is one more test passing. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 28 +++++++++++++++------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 1 + .../tst_qgraphicsanchorlayout1.cpp | 2 -- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 485743e..260e8dc 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1709,11 +1709,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // they have a different logic for solveExpanding(). solveExpanding(trunkConstraints, trunkVariables); - // Propagate the new sizes down the simplified graph, ie. tell the - // group anchors to set their children anchors sizes. - for (int i = 0; i < trunkVariables.count(); ++i) - trunkVariables.at(i)->updateChildrenSizes(); - // Calculate and set the preferred and expanding sizes for the layout, // from the edge sizes that were calculated above. qreal pref(0.0); @@ -1756,9 +1751,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( ad->sizeAtExpanding = ad->expSize; ad->sizeAtMaximum = ad->maxSize; - // Propagate - ad->updateChildrenSizes(); - sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum; sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred; sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum; @@ -1790,13 +1782,17 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( ad->sizeAtMinimum = ad->sizeAtPreferred; ad->sizeAtExpanding = ad->sizeAtPreferred; ad->sizeAtMaximum = ad->sizeAtPreferred; - ad->updateChildrenSizes(); } // Delete the constraints, we won't use them anymore. qDeleteAll(sizeHintConstraints); } } + + // Propagate the new sizes down the simplified graph, ie. tell the + // group anchors to set their children anchors sizes. + updateAnchorSizes(orientation); + graphHasConflicts[orientation] = !feasible; // Clean up our data structures. They are not needed anymore since @@ -1907,6 +1903,20 @@ void QGraphicsAnchorLayoutPrivate::constraintsFromPaths(Orientation orientation) /*! \internal +*/ +void QGraphicsAnchorLayoutPrivate::updateAnchorSizes(Orientation orientation) +{ + Graph &g = graph[orientation]; + const QList > &vertices = g.connections(); + + for (int i = 0; i < vertices.count(); ++i) { + AnchorData *ad = g.edgeData(vertices.at(i).first, vertices.at(i).second); + ad->updateChildrenSizes(); + } +} + +/*! + \internal Create LP constraints for each anchor based on its minimum and maximum sizes, as specified in its size hints diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 1d7b758..e045fbd 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -441,6 +441,7 @@ public: void setAnchorSizeHintsFromItems(Orientation orientation); void findPaths(Orientation orientation); void constraintsFromPaths(Orientation orientation); + void updateAnchorSizes(Orientation orientation); QList constraintsFromSizeHints(const QList &anchors); QList > getGraphParts(Orientation orientation); void identifyFloatItems(const QSet &visited, Orientation orientation); diff --git a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp index 148b2c8..755d866 100644 --- a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp +++ b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp @@ -1711,8 +1711,6 @@ void tst_QGraphicsAnchorLayout1::testBasicLayout() // Validate for (int i = 0; i < result.count(); ++i) { - if (i == 1) - QEXPECT_FAIL("Two, mixed", "Works with simplification disabled.", Continue); const BasicLayoutTestResult item = result[i]; QCOMPARE(widgets[item.index]->geometry(), item.rect); } -- cgit v0.12 From aff8e7a24e5e8cc1f330a1b3c2947ba4d07d51ed Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 8 Oct 2009 20:09:21 -0300 Subject: QGAL: refactor calculateGraphs() method Create calculateTrunk() and calculateNonTrunk() methods, that calculate sizes for anchors in different parts of the simplified graph (using simplex when needed). Also fixes a minor leak when the nontrunk part is non-feasible. The old code left the loop leaving the contents of 'sizeHintConstraints' not allocated. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 161 ++++++++++++----------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 7 + 2 files changed, 93 insertions(+), 75 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 260e8dc..c9821ae 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1600,7 +1600,8 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs() calculateGraphCacheDirty = 0; } -// ### remove me: +// ### Maybe getGraphParts could return the variables when traversing, at least +// for trunk... QList getVariables(QList constraints) { QSet variableSet; @@ -1664,8 +1665,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // 2) The floating or semi-floating anchors (items) that are those which // are connected to only one (or none) of the layout sides, thus are not // influenced by the layout size. - QList > parts; - parts = getGraphParts(orientation); + QList > parts = getGraphParts(orientation); // Now run the simplex solver to calculate Minimum, Preferred and Maximum sizes // of the "trunk" set of constraints and variables. @@ -1675,49 +1675,82 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // For minimum and maximum, use the path between the two layout sides as the // objective function. - - // Retrieve that path AnchorVertex *v = internalVertex(q, pickEdge(Qt::AnchorRight, orientation)); GraphPath trunkPath = graphPaths[orientation].value(v); + bool feasible = calculateTrunk(orientation, trunkPath, trunkConstraints, trunkVariables); + + // For the other parts that not the trunk, solve only for the preferred size + // that is the size they will remain at, since they are not stretched by the + // layout. + + // Skipping the first (trunk) + for (int i = 1; i < parts.count(); ++i) { + if (!feasible) + break; + + QList partConstraints = parts[i]; + QList partVariables = getVariables(partConstraints); + Q_ASSERT(!partVariables.isEmpty()); + feasible &= calculateNonTrunk(partConstraints, partVariables); + } + + // Propagate the new sizes down the simplified graph, ie. tell the + // group anchors to set their children anchors sizes. + updateAnchorSizes(orientation); + + graphHasConflicts[orientation] = !feasible; + + // Clean up our data structures. They are not needed anymore since + // distribution uses just interpolation. + qDeleteAll(constraints[orientation]); + constraints[orientation].clear(); + graphPaths[orientation].clear(); // ### +} + +/*! + \internal + + Calculate the sizes for all anchors which are part of the trunk. This works + on top of a (possibly) simplified graph. +*/ +bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const GraphPath &path, + const QList &constraints, + const QList &variables) +{ bool feasible = true; - if (!trunkConstraints.isEmpty()) { -#if 0 - qDebug("Simplex used for trunk of %s", - orientation == Horizontal ? "Horizontal" : "Vertical"); -#endif + bool needsSimplex = !constraints.isEmpty(); -#ifdef QT_DEBUG - lastCalculationUsedSimplex[orientation] = true; +#if 0 + qDebug("Simplex %s for trunk of %s", needsSimplex ? "used" : "NOT used", + orientation == Horizontal ? "Horizontal" : "Vertical"); #endif - // Solve min and max size hints for trunk - qreal min, max; + if (needsSimplex) { - QList sizeHintConstraints = constraintsFromSizeHints(trunkVariables); - QList allTrunkConstraints = trunkConstraints + sizeHintConstraints; + QList sizeHintConstraints = constraintsFromSizeHints(variables); + QList allConstraints = constraints + sizeHintConstraints; - feasible = solveMinMax(allTrunkConstraints, trunkPath, &min, &max); + // Solve min and max size hints + qreal min, max; + feasible = solveMinMax(allConstraints, path, &min, &max); if (feasible) { - // Solve for preferred. The objective function is calculated from the constraints - // and variables internally. - solvePreferred(allTrunkConstraints, trunkVariables); + solvePreferred(allConstraints, variables); - // Solve for expanding. The objective function and the constraints from items - // are calculated internally. Note that we don't include the sizeHintConstraints, since - // they have a different logic for solveExpanding(). - solveExpanding(trunkConstraints, trunkVariables); + // Note that we don't include the sizeHintConstraints, since they + // have a different logic for solveExpanding(). + solveExpanding(constraints, variables); // 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) { + foreach (const AnchorData *ad, path.positives) { pref += ad->sizeAtPreferred; expanding += ad->sizeAtExpanding; } - foreach (const AnchorData *ad, trunkPath.negatives) { + foreach (const AnchorData *ad, path.negatives) { pref -= ad->sizeAtPreferred; expanding -= ad->sizeAtExpanding; } @@ -1731,21 +1764,12 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( qDeleteAll(sizeHintConstraints); } else { -#if 0 - qDebug("Simplex NOT used for trunk of %s", - orientation == Horizontal ? "Horizontal" : "Vertical"); -#endif - -#ifdef QT_DEBUG - lastCalculationUsedSimplex[orientation] = false; -#endif - // No Simplex is necessary because the path was simplified all the way to a single // anchor. - Q_ASSERT(trunkPath.positives.count() == 1); - Q_ASSERT(trunkPath.negatives.count() == 0); + Q_ASSERT(path.positives.count() == 1); + Q_ASSERT(path.negatives.count() == 0); - AnchorData *ad = trunkPath.positives.toList()[0]; + AnchorData *ad = path.positives.toList()[0]; ad->sizeAtMinimum = ad->minSize; ad->sizeAtPreferred = ad->prefSize; ad->sizeAtExpanding = ad->expSize; @@ -1757,49 +1781,36 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( sizeAtExpanding[orientation] = ad->sizeAtExpanding; } - // For the other parts that not the trunk, solve only for the preferred size - // that is the size they will remain at, since they are not stretched by the - // layout. +#ifdef QT_DEBUG + lastCalculationUsedSimplex[orientation] = needsSimplex; +#endif - // Solve the other only for preferred, skip trunk - if (feasible) { - for (int i = 1; i < parts.count(); ++i) { - QList partConstraints = parts[i]; - QList partVariables = getVariables(partConstraints); - Q_ASSERT(!partVariables.isEmpty()); - - QList sizeHintConstraints = constraintsFromSizeHints(partVariables); - partConstraints += sizeHintConstraints; - feasible &= solvePreferred(partConstraints, partVariables); - if (!feasible) - break; + return feasible; +} - // Propagate size at preferred to other sizes. Semi-floats - // always will be in their sizeAtPreferred. - for (int j = 0; j < partVariables.count(); ++j) { - AnchorData *ad = partVariables[j]; - Q_ASSERT(ad); - ad->sizeAtMinimum = ad->sizeAtPreferred; - ad->sizeAtExpanding = ad->sizeAtPreferred; - ad->sizeAtMaximum = ad->sizeAtPreferred; - } +/*! + \internal +*/ +bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList &constraints, + const QList &variables) +{ + QList sizeHintConstraints = constraintsFromSizeHints(variables); + bool feasible = solvePreferred(constraints + sizeHintConstraints, variables); - // Delete the constraints, we won't use them anymore. - qDeleteAll(sizeHintConstraints); + if (feasible) { + // Propagate size at preferred to other sizes. Semi-floats always will be + // in their sizeAtPreferred. + for (int j = 0; j < variables.count(); ++j) { + AnchorData *ad = variables[j]; + Q_ASSERT(ad); + ad->sizeAtMinimum = ad->sizeAtPreferred; + ad->sizeAtExpanding = ad->sizeAtPreferred; + ad->sizeAtMaximum = ad->sizeAtPreferred; } } - // Propagate the new sizes down the simplified graph, ie. tell the - // group anchors to set their children anchors sizes. - updateAnchorSizes(orientation); - - graphHasConflicts[orientation] = !feasible; - - // Clean up our data structures. They are not needed anymore since - // distribution uses just interpolation. - qDeleteAll(constraints[orientation]); - constraints[orientation].clear(); - graphPaths[orientation].clear(); // ### + qDeleteAll(sizeHintConstraints); + return feasible; } /*! diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index e045fbd..d96a035 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -438,6 +438,13 @@ public: void calculateGraphs(); void calculateGraphs(Orientation orientation); + + bool calculateTrunk(Orientation orientation, const GraphPath &trunkPath, + const QList &constraints, + const QList &variables); + bool calculateNonTrunk(const QList &constraints, + const QList &variables); + void setAnchorSizeHintsFromItems(Orientation orientation); void findPaths(Orientation orientation); void constraintsFromPaths(Orientation orientation); -- cgit v0.12 From 6a4d30b12baa85a223116d6629fa1e08e922d659 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Tue, 13 Oct 2009 16:10:10 -0300 Subject: QGAL: refactor and document the simplification algorithm Refactor the simplifyGraphIteration() function. The aim was to make it more clear without changing too much the way it does stuff. Before it collected a list of candidates and then filtered that into sublists of same order, and after that removed the center edges from the borders. It also reversed the list if the direction wasn't forward, to pass it in forward order to simplifySequentialChunk() helper function. The refactored version - take in account the order when building the candidates, this avoids index manipulation; - try to calculate 'beforeSequence' and 'afterSequence' as it builds the candidates list; - make simplifySequentialChunk() aware of directions, now it deals internally with reversed direction sequences. This commits also adds explanations to trickier parts of the code. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza Reviewed-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 324 ++++++++++++----------- 1 file changed, 165 insertions(+), 159 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index c9821ae..937ccfa 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -506,36 +506,46 @@ static bool simplifySequentialChunk(Graph *graph, const QVector &vertices, AnchorVertex *after) { - int i; + AnchorData *data = graph->edgeData(before, vertices.first()); + Q_ASSERT(data); + + const bool forward = (before == data->from); + QVector orderedVertices; + + if (forward) { + orderedVertices = vertices; + } else { + qSwap(before, after); + for (int i = vertices.count() - 1; i >= 0; --i) + orderedVertices.append(vertices.at(i)); + } + #if defined(QT_DEBUG) && 0 QString strVertices; - for (i = 0; i < vertices.count(); ++i) - strVertices += QString::fromAscii("%1 - ").arg(vertices.at(i)->toString()); + for (int i = 0; i < orderedVertices.count(); ++i) { + strVertices += QString::fromAscii("%1 - ").arg(orderedVertices.at(i)->toString()); + } QString strPath = QString::fromAscii("%1 - %2%3").arg(before->toString(), strVertices, after->toString()); qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString())); #endif 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); - sequence->m_edges.append(data); + + for (int i = 0; i <= orderedVertices.count(); ++i) { + AnchorVertex *next = (i < orderedVertices.count()) ? orderedVertices.at(i) : after; + AnchorData *ad = graph->takeEdge(prev, next); + Q_ASSERT(ad); + sequence->m_edges.append(ad); prev = next; } - sequence->setVertices(vertices); + + sequence->setVertices(orderedVertices); 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 - if (data->from != vertices.last()) - qSwap(sequence->from, sequence->to); - // Note that since layout 'edges' can't be simplified away from // the graph, it's safe to assume that if there's a layout // 'edge', it'll be in the boundaries of the sequence. @@ -590,15 +600,6 @@ static bool simplifySequentialChunk(Graph *graph, 2. Go to (1) 3. Done - - * Gathering sequential anchors * - The algorithm walks the graph in depth-first order, and only collects vertices that has two - edges connected to it. If the vertex does not have two edges or if it is a layout edge, - it will take all the previously collected vertices and try to create a simplified sequential - anchor representing all the previously collected vertices. - Once the simplified anchor is inserted, the collected list is cleared in order to find the next - sequence to simplify. - Note that there are some catches to this that are not covered by the above explanation. */ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) { @@ -615,9 +616,7 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) orientation == Horizontal ? "Horizontal" : "Vertical"); #endif - AnchorVertex *rootVertex = graph[orientation].rootVertex(); - - if (!rootVertex) + if (!graph[orientation].rootVertex()) return; bool dirty; @@ -626,164 +625,171 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) } while (dirty); } +/*! + \internal + + One iteration of the simplification algorithm. Returns true if another iteration is needed. + + The algorithm walks the graph in depth-first order, and only collects vertices that has two + edges connected to it. If the vertex does not have two edges or if it is a layout edge, it + will take all the previously collected vertices and try to create a simplified sequential + anchor representing all the previously collected vertices. Once the simplified anchor is + inserted, the collected list is cleared in order to find the next sequence to simplify. + + Note that there are some catches to this that are not covered by the above explanation, see + the function comments for more details. +*/ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutPrivate::Orientation orientation) { Q_Q(QGraphicsAnchorLayout); Graph &g = graph[orientation]; - AnchorVertex *v = g.rootVertex(); QSet visited; - QStack stack; - stack.push(v); + QStack > stack; + stack.push(qMakePair(static_cast(0), g.rootVertex())); QVector candidates; + bool candidatesForward; const Qt::AnchorPoint centerEdge = pickEdge(Qt::AnchorHorizontalCenter, orientation); - const Qt::AnchorPoint layoutEdge = oppositeEdge(v->m_edge); - bool dirty = false; - - // walk depth-first. + // Walk depth-first, in the stack we store start of the candidate sequence (beforeSequence) + // and the vertex to be visited. while (!stack.isEmpty()) { - v = stack.pop(); - QList vertices = g.adjacentVertices(v); - const int count = vertices.count(); - bool endOfSequence = (v->m_item == q && v->m_edge == layoutEdge) || count != 2; - if (count == 2 && v->m_item != q) { - candidates.append(v); - if (visited.contains(vertices.first()) && visited.contains(vertices.last())) { - // in case of a cycle - endOfSequence = true; + QPair pair = stack.pop(); + AnchorVertex *beforeSequence = pair.first; + AnchorVertex *v = pair.second; + + // The basic idea is to determine whether we found an end of sequence, + // if that's the case, we stop adding vertices to the candidate list + // and do a simplification step. + // + // A vertex can trigger an end of sequence if + // (a) it is a layout vertex, we don't simplify away the layout vertices; + // (b) it does not have exactly 2 adjacents; + // (c) it will change the direction of the sequence; + // (d) its next adjacent is already visited (a cycle in the graph). + + const QList &adjacents = g.adjacentVertices(v); + const bool isLayoutVertex = v->m_item == q; + AnchorVertex *afterSequence = v; + bool endOfSequence = false; + + // + // Identify the end cases. + // + + // Identifies cases (a) and (b) + endOfSequence = isLayoutVertex || adjacents.count() != 2; + + if (!endOfSequence) { + // If this is the first vertice, determine what is the direction to use for this + // sequence. + if (candidates.isEmpty()) { + const AnchorData *data = g.edgeData(beforeSequence, v); + Q_ASSERT(data); + candidatesForward = (beforeSequence == data->from); } - } - if (endOfSequence && candidates.count() >= 1) { - int i; - AnchorVertex *afterSequence= 0; - AnchorVertex *beforeSequence = 0; - // find the items before and after the valid sequence - if (candidates.count() == 1) { - QList beforeAndAfterVertices = g.adjacentVertices(candidates.at(0)); - Q_ASSERT(beforeAndAfterVertices.count() == 2); - // Since we only have one vertex, we can pick - // any of the two vertices to become before/after. - afterSequence = beforeAndAfterVertices.last(); - beforeSequence = beforeAndAfterVertices.first(); - } else { - QList adjacentOfSecondLastVertex = g.adjacentVertices(candidates.last()); - Q_ASSERT(adjacentOfSecondLastVertex.count() == 2); - if (adjacentOfSecondLastVertex.first() == candidates.at(candidates.count() - 2)) - afterSequence = adjacentOfSecondLastVertex.last(); - else - afterSequence = adjacentOfSecondLastVertex.first(); - QList adjacentOfSecondVertex = g.adjacentVertices(candidates.first()); - Q_ASSERT(adjacentOfSecondVertex.count() == 2); - if (adjacentOfSecondVertex.first() == candidates.at(1)) - beforeSequence = adjacentOfSecondVertex.last(); - else - beforeSequence = adjacentOfSecondVertex.first(); + // This is a tricky part. We peek at the next vertex to find out + // + // - whether the edge from this vertex to the next vertex has the same direction; + // - whether we already visited the next vertex. + // + // Those are needed to identify (c) and (d). Note that unlike (a) and (b), we preempt + // the end of sequence by looking into the next vertex. + + // Peek at the next vertex + AnchorVertex *after; + if (candidates.isEmpty()) + after = (beforeSequence == adjacents.last() ? adjacents.first() : adjacents.last()); + else + after = (candidates.last() == adjacents.last() ? adjacents.first() : adjacents.last()); + + // ### At this point we assumed that candidates will not contain 'after', this may not hold + // when simplifying FLOATing anchors. + Q_ASSERT(!candidates.contains(after)); + + const AnchorData *data = g.edgeData(v, after); + Q_ASSERT(data); + const bool willChangeDirection = (candidatesForward != (v == data->from)); + const bool cycleFound = visited.contains(after); + + // Now cases (c) and (d)... + endOfSequence = willChangeDirection || cycleFound; + + if (endOfSequence) { + if (!willChangeDirection) { + // If the direction will not change, we can add the current vertex to the + // candidates list and we know that 'after' can be used as afterSequence. + candidates.append(v); + afterSequence = after; + } + } else { + // If it's not an end of sequence, then the vertex didn't trigger neither of the + // previously four cases, so it can be added to the candidates list. + candidates.append(v); } - // The complete path of the sequence to simplify is: beforeSequence, , afterSequence - // where beforeSequence and afterSequence are the endpoints where the anchor is inserted - // between. -#if defined(QT_DEBUG) && 0 - // ### DEBUG - QString strCandidates; - for (i = 0; i < candidates.count(); ++i) - strCandidates += QString::fromAscii("%1 - ").arg(candidates.at(i)->toString()); - QString strPath = QString::fromAscii("%1 - %2%3").arg(beforeSequence->toString(), strCandidates, afterSequence->toString()); - qDebug("candidate list for sequential simplification:\n[%s]", qPrintable(strPath)); -#endif + } - bool forward = true; - AnchorVertex *prev = beforeSequence; - int intervalFrom = 0; + // + // Add next non-visited vertices to the stack. + // + for (int i = 0; i < adjacents.count(); ++i) { + AnchorVertex *next = adjacents.at(i); + if (visited.contains(next)) + continue; - // Check for directionality (from). We don't want to destroy that information, - // thus we only combine anchors with the same direction. + // If current vertex is an end of sequence, and it'll reset the candidates list. So + // the next vertices will build candidates lists with the current vertex as 'before' + // vertex. If it's not an end of sequence, we keep the original 'before' vertex, + // since we are keeping the candidates list. + if (endOfSequence) + stack.push(qMakePair(v, next)); + else + stack.push(qMakePair(beforeSequence, next)); + } - // "i" is the index *including* the beforeSequence and afterSequence vertices. - for (i = 1; i <= candidates.count() + 1; ++i) { - bool atVertexAfter = i > candidates.count(); - AnchorVertex *v1 = atVertexAfter ? afterSequence : candidates.at(i - 1); - AnchorData *data = g.edgeData(prev, v1); - Q_ASSERT(data); - if (i == 1) { - forward = (prev == data->from ? true : false); - } else if (forward != (prev == data->from) || atVertexAfter) { - int intervalTo = i; - if (forward != (prev == data->from)) - --intervalTo; - - // intervalFrom and intervalTo should now be indices to the vertex before and - // after the sequential anchor. - if (intervalTo - intervalFrom >= 2) { - // simplify in the range [intervalFrom, intervalTo] - - // Trim off internal center anchors (Left-Center/Center-Right) from the - // start and the end of the sequence. We never want to simplify internal - // center anchors where there is an external anchor connected to the center. - AnchorVertex *intervalVertexFrom = intervalFrom == 0 ? beforeSequence : candidates.at(intervalFrom - 1); - int effectiveIntervalFrom = intervalFrom; - if (intervalVertexFrom->m_edge == centerEdge - && intervalVertexFrom->m_item == candidates.at(effectiveIntervalFrom)->m_item) { - ++effectiveIntervalFrom; - intervalVertexFrom = candidates.at(effectiveIntervalFrom - 1); - } - AnchorVertex *intervalVertexTo = intervalTo <= candidates.count() ? candidates.at(intervalTo - 1) : afterSequence; - int effectiveIntervalTo = intervalTo; - if (intervalVertexTo->m_edge == centerEdge - && intervalVertexTo->m_item == candidates.at(effectiveIntervalTo - 2)->m_item) { - --effectiveIntervalTo; - intervalVertexTo = candidates.at(effectiveIntervalTo - 1); - } - if (effectiveIntervalTo - effectiveIntervalFrom >= 2) { - QVector subCandidates; - if (forward) { - subCandidates = candidates.mid(effectiveIntervalFrom, effectiveIntervalTo - effectiveIntervalFrom - 1); - } else { - // reverse the order of the candidates. - qSwap(intervalVertexFrom, intervalVertexTo); - do { - ++effectiveIntervalFrom; - subCandidates.prepend(candidates.at(effectiveIntervalFrom - 1)); - } while (effectiveIntervalFrom < effectiveIntervalTo - 1); - } - if (simplifySequentialChunk(&g, intervalVertexFrom, subCandidates, intervalVertexTo)) { - dirty = true; - break; - } - // finished simplification of chunk with same direction - } - } - if (forward == (prev == data->from)) - --intervalTo; - intervalFrom = intervalTo; - - forward = !forward; - } - prev = v1; - } + visited.insert(v); - if (dirty) - break; - } + if (!endOfSequence || candidates.isEmpty()) + continue; + + // + // Create a sequence for (beforeSequence, candidates, afterSequence). + // - if (endOfSequence) - candidates.clear(); + // One restriction we have is to not simplify half of an anchor and let the other half + // unsimplified. So we remove center edges before and after the sequence. + if (beforeSequence->m_edge == centerEdge && beforeSequence->m_item == candidates.first()->m_item) { + beforeSequence = candidates.first(); + candidates.remove(0); - for (int i = 0; i < count; ++i) { - AnchorVertex *next = vertices.at(i); - if (next->m_item == q && next->m_edge == centerEdge) + // If there's not candidates to be simplified, leave. + if (candidates.isEmpty()) continue; - if (visited.contains(next)) + } + + if (afterSequence->m_edge == centerEdge && afterSequence->m_item == candidates.last()->m_item) { + afterSequence = candidates.last(); + candidates.remove(candidates.count() - 1); + + if (candidates.isEmpty()) continue; - stack.push(next); } - visited.insert(v); + // This function will remove the candidates from the graph and create one edge between + // beforeSequence and afterSequence. This function returns true if the sequential + // simplification also caused a parallel simplification to be created. In this case we end + // the iteration and start again (since all the visited state we have may be outdated). + if (simplifySequentialChunk(&g, beforeSequence, candidates, afterSequence)) + return true; + + // If there was no parallel simplification, we'll keep walking the graph. So we clear the + // candidates list to start again. + candidates.clear(); } - return dirty; + return false; } static void restoreSimplifiedAnchor(Graph &g, -- cgit v0.12 From 8ef83e60df0291ba2a36cc744e1f0c11f465f1e2 Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Thu, 15 Oct 2009 18:45:13 +0200 Subject: Fixed a rare crash in qt_x11_enforce_cursor A QPointer was set to point to a QWidget by one of its children, during its deletion. This happens during the child deletion, and after the call to QObject::clearGuards(), which means that the QPointer becomes a dangling one. The fix ensures that qt_x11_enforce_cursor will not be called with a being-deleted QWidget. The included auto-test doesn't test anything, except that it doesn't crash. Reviewed-by: Olivier --- src/gui/kernel/qapplication.cpp | 5 ++++- tests/auto/qwidget/tst_qwidget.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index b990fe2..5476e36 100644 --- a/src/gui/kernel/qapplication.cpp +++ b/src/gui/kernel/qapplication.cpp @@ -2662,7 +2662,10 @@ void QApplicationPrivate::dispatchEnterLeave(QWidget* enter, QWidget* leave) { if (!isAlien(w)) break; if (w->testAttribute(Qt::WA_SetCursor)) { - parentOfLeavingCursor = w->parentWidget(); + QWidget *parent = w->parentWidget(); + while (parent && parent->d_func()->data.in_destructor) + parent = parent->parentWidget(); + parentOfLeavingCursor = parent; //continue looping, we need to find the downest alien widget with a cursor. // (downest on the screen) } diff --git a/tests/auto/qwidget/tst_qwidget.cpp b/tests/auto/qwidget/tst_qwidget.cpp index daeee3d..4cb9f72 100644 --- a/tests/auto/qwidget/tst_qwidget.cpp +++ b/tests/auto/qwidget/tst_qwidget.cpp @@ -69,6 +69,9 @@ #include #include +#include +#include + #include "../../shared/util.h" @@ -285,6 +288,7 @@ private slots: #ifdef Q_WS_X11 void minAndMaxSizeWithX11BypassWindowManagerHint(); void showHideShow(); + void clean_qt_x11_enforce_cursor(); #endif void compatibilityChildInsertedEvents(); @@ -6048,6 +6052,35 @@ void tst_QWidget::showHideShow() QVERIFY(w.gotExpectedMapNotify); } + +void tst_QWidget::clean_qt_x11_enforce_cursor() +{ + { + QWidget window; + QWidget *w = new QWidget(&window); + QWidget *child = new QWidget(w); + child->setAttribute(Qt::WA_SetCursor, true); + + window.show(); + QApplication::setActiveWindow(&window); + QTest::qWaitForWindowShown(&window); + QTest::qWait(100); + QCursor::setPos(window.geometry().center()); + QTest::qWait(100); + + child->setFocus(); + QApplication::processEvents(); + QTest::qWait(100); + + delete w; + } + + QGraphicsScene scene; + QLineEdit *edit = new QLineEdit; + scene.addWidget(edit); + + // If the test didn't crash, then it passed. +} #endif class EventRecorder : public QObject -- cgit v0.12 From db795de8e92da117070c45fb963273a6fa61c688 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 15 Oct 2009 11:16:32 -0300 Subject: QGraphicsAnchorLayout: Mark expected failures in tests w/o simplification Some tests are expected to fail when run without simplification, ie. by setting the environment var QT_ANCHORLAYOUT_NO_SIMPLIFICATION. This commit adds QEXPECT_FAIL before those tests. In some cases the failures are random therefore we must disable the test instead. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- .../tst_qgraphicsanchorlayout.cpp | 91 ++++++++++++++++------ 1 file changed, 67 insertions(+), 24 deletions(-) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index cef56b9..a51d2ab 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -49,6 +49,14 @@ class tst_QGraphicsAnchorLayout : public QObject { Q_OBJECT; +public: + tst_QGraphicsAnchorLayout() : QObject() { + hasSimplification = qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty(); + } + +private: + bool hasSimplification; + private slots: void simple(); void simple_center(); @@ -185,8 +193,10 @@ void tst_QGraphicsAnchorLayout::simple() p.setLayout(l); p.adjustSize(); - QVERIFY(!usedSimplex(l, Qt::Horizontal)); - QVERIFY(!usedSimplex(l, Qt::Vertical)); + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::simple_center() @@ -226,8 +236,10 @@ void tst_QGraphicsAnchorLayout::simple_center() QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize, QSizeF(200, 20)); - QVERIFY(usedSimplex(l, Qt::Horizontal)); - QVERIFY(!usedSimplex(l, Qt::Vertical)); + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } delete p; } @@ -326,8 +338,10 @@ void tst_QGraphicsAnchorLayout::layoutDirection() QCOMPARE(checkReverseDirection(p), true); - QVERIFY(usedSimplex(l, Qt::Horizontal)); - QVERIFY(!usedSimplex(l, Qt::Vertical)); + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } delete p; delete view; @@ -417,8 +431,10 @@ void tst_QGraphicsAnchorLayout::diagonal() QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 75.0, 100.0)); QCOMPARE(p.size(), testA); - QVERIFY(usedSimplex(l, Qt::Horizontal)); - QVERIFY(!usedSimplex(l, Qt::Vertical)); + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } QCOMPARE(checkReverseDirection(&p), true); @@ -496,6 +512,9 @@ void tst_QGraphicsAnchorLayout::parallel() QCOMPARE(f->geometry(), QRectF(350, 500, 100, 100)); QCOMPARE(p.size(), layoutMinimumSize); + if (!hasSimplification) + return; + p.resize(layoutPreferredSize); QCOMPARE(a->geometry(), QRectF(0, 0, 150, 100)); QCOMPARE(b->geometry(), QRectF(150, 100, 150, 100)); @@ -565,8 +584,10 @@ void tst_QGraphicsAnchorLayout::parallel2() p.resize(layoutMaximumSize); QCOMPARE(p.size(), layoutMaximumSize); - QVERIFY(!usedSimplex(l, Qt::Horizontal)); - QVERIFY(!usedSimplex(l, Qt::Vertical)); + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::snake() @@ -758,6 +779,8 @@ void tst_QGraphicsAnchorLayout::fairDistribution() QCOMPARE(layoutMaximumSize, QSizeF(300.0, 400.0)); p.resize(layoutMinimumSize); + if (!hasSimplification) + QEXPECT_FAIL("", "Without simplification there is no fair distribution.", Abort); QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 20.0, 100.0)); QCOMPARE(b->geometry(), QRectF(20.0, 100.0, 20.0, 100.0)); QCOMPARE(c->geometry(), QRectF(40.0, 200.0, 20.0, 100.0)); @@ -778,8 +801,10 @@ void tst_QGraphicsAnchorLayout::fairDistribution() QCOMPARE(d->geometry(), QRectF(0.0, 300.0, 300.0, 100.0)); QCOMPARE(p.size(), layoutMaximumSize); - QVERIFY(!usedSimplex(l, Qt::Horizontal)); - QVERIFY(!usedSimplex(l, Qt::Vertical)); + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() @@ -836,6 +861,9 @@ void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() QCOMPARE(layoutPreferredSize, QSizeF(220.0, 500.0)); QCOMPARE(layoutMaximumSize, QSizeF(400.0, 500.0)); + if (!hasSimplification) + return; + p.resize(layoutMinimumSize); QCOMPARE(a->size(), b->size()); QCOMPARE(a->size(), c->size()); @@ -922,8 +950,10 @@ void tst_QGraphicsAnchorLayout::proportionalPreferred() QCOMPARE(c->size().width(), 14 * factor); QCOMPARE(p.size(), QSizeF(12, 400)); - QVERIFY(!usedSimplex(l, Qt::Horizontal)); - QVERIFY(!usedSimplex(l, Qt::Vertical)); + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::example() @@ -1008,8 +1038,10 @@ void tst_QGraphicsAnchorLayout::example() QCOMPARE(b->size(), d->size()); QCOMPARE(f->size(), g->size()); - QVERIFY(usedSimplex(l, Qt::Horizontal)); - QVERIFY(usedSimplex(l, Qt::Vertical)); + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::setSpacing() @@ -1331,8 +1363,10 @@ void tst_QGraphicsAnchorLayout::sizePolicy() QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(100, 100)); QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(100, 100)); - QVERIFY(!usedSimplex(l, Qt::Horizontal)); - QVERIFY(!usedSimplex(l, Qt::Vertical)); + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } delete p; delete view; @@ -1425,8 +1459,10 @@ void tst_QGraphicsAnchorLayout::expandingSequence() QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize.width(), qreal(200)); - QVERIFY(!usedSimplex(l, Qt::Horizontal)); - QVERIFY(!usedSimplex(l, Qt::Vertical)); + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() @@ -1488,8 +1524,10 @@ void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize.width(), qreal(400)); - QVERIFY(!usedSimplex(l, Qt::Horizontal)); - QVERIFY(!usedSimplex(l, Qt::Vertical)); + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } // 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 @@ -1514,8 +1552,10 @@ void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() QCOMPARE(c->geometry().size(), pref); QCOMPARE(d->geometry().size(), pref + QSizeF(50, 0)); - QVERIFY(!usedSimplex(l, Qt::Horizontal)); - QVERIFY(!usedSimplex(l, Qt::Vertical)); + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::expandingParallel() @@ -1672,6 +1712,9 @@ void tst_QGraphicsAnchorLayout::infiniteMaxSizes() QCOMPARE(c->geometry(), QRectF(100, 0, 50, 10)); QCOMPARE(d->geometry(), QRectF(150, 0, 50, 10)); + if (!hasSimplification) + QEXPECT_FAIL("", "Without simplification there is no fair distribution.", Abort); + p.resize(1000, 10); QCOMPARE(a->geometry(), QRectF(0, 0, 450, 10)); QCOMPARE(b->geometry(), QRectF(450, 0, 50, 10)); -- cgit v0.12 From c5609a9247b965ea57db98607ad498a216e179d6 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 15 Oct 2009 15:25:03 -0300 Subject: QGAL: Add anchor layout vs nested linear layouts benchmark Adding a benchmark to compare the use of three nested linear layouts versus the use of a single anchor layout. We also test two different anchor layout setups to achieve the same visual result. In the first one we use the addCornerAnchors API that, in the way it was used, adds reduntant anchors. The second uses the basic addAnchor API to create only the essential anchors. Currently the first setup is way slower than the second. That happens because the redundant anchors create "zero-sized knots" in the graph, that are not eaten by the simplification algorithm, thus requiring the usage of the simplex solver. Zero-sized knots are groups of three or more vertices that are linked together by zero-sized anchors. In pratice, these vertices represent the same place in the graph (remember, the distance between them is zero), but to the simplification mechanism, they look like three distinct ones. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- .../tst_qgraphicsanchorlayout.cpp | 111 +++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/tests/benchmarks/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/benchmarks/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 000ab6e..43dff93 100644 --- a/tests/benchmarks/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/benchmarks/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -41,6 +41,7 @@ #include #include +#include #include #include @@ -54,6 +55,9 @@ public: private slots: void s60_hard_complex_data(); void s60_hard_complex(); + void linearVsAnchorNested_data(); + void linearVsAnchorNested(); + }; @@ -192,6 +196,113 @@ void tst_QGraphicsAnchorLayout::s60_hard_complex() } } +void tst_QGraphicsAnchorLayout::linearVsAnchorNested_data() +{ + QTest::addColumn("whichLayout"); + QTest::newRow("LinearLayout") + << 0; + QTest::newRow("AnchorLayout setup with null-anchors knot") + << 1; + QTest::newRow("AnchorLayout setup easy to simplificate") + << 2; +} + +void tst_QGraphicsAnchorLayout::linearVsAnchorNested() +{ + QFETCH(int, whichLayout); + + QSizeF min(10, 10); + QSizeF pref(80, 80); + QSizeF max(150, 150); + + 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"); + + QGraphicsLayout *layout; + + if (whichLayout == 0) { + QGraphicsLinearLayout *linear1 = new QGraphicsLinearLayout; + QGraphicsLinearLayout *linear2 = new QGraphicsLinearLayout(Qt::Vertical); + QGraphicsLinearLayout *linear3 = new QGraphicsLinearLayout; + + linear1->addItem(a); + linear1->addItem(linear2); + linear2->addItem(b); + linear2->addItem(linear3); + linear3->addItem(c); + linear3->addItem(d); + + layout = linear1; + } else if (whichLayout == 1) { + QGraphicsAnchorLayout *anchor = new QGraphicsAnchorLayout; + + // A + anchor->addCornerAnchors(a, Qt::TopLeftCorner, anchor, Qt::TopLeftCorner); + anchor->addCornerAnchors(a, Qt::TopRightCorner, b, Qt::TopLeftCorner); + anchor->addCornerAnchors(a, Qt::BottomLeftCorner, anchor, Qt::BottomLeftCorner); + anchor->addCornerAnchors(a, Qt::BottomRightCorner, c, Qt::BottomLeftCorner); + + // B + anchor->addCornerAnchors(b, Qt::TopRightCorner, anchor, Qt::TopRightCorner); + anchor->addCornerAnchors(b, Qt::BottomLeftCorner, c, Qt::TopLeftCorner); + anchor->addCornerAnchors(b, Qt::BottomRightCorner, d, Qt::TopRightCorner); + + // C + anchor->addCornerAnchors(c, Qt::TopRightCorner, d, Qt::TopLeftCorner); + anchor->addCornerAnchors(c, Qt::BottomRightCorner, d, Qt::BottomLeftCorner); + + // D + anchor->addCornerAnchors(d, Qt::BottomRightCorner, anchor, Qt::BottomRightCorner); + + layout = anchor; + } else { + QGraphicsAnchorLayout *anchor = new QGraphicsAnchorLayout; + + // A + anchor->addAnchor(a, Qt::AnchorLeft, anchor, Qt::AnchorLeft); + anchor->addAnchors(a, anchor, Qt::Vertical); + anchor->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + anchor->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + + // B + anchor->addAnchor(b, Qt::AnchorTop, anchor, Qt::AnchorTop); + anchor->addAnchor(b, Qt::AnchorRight, anchor, Qt::AnchorRight); + anchor->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + anchor->addAnchor(b, Qt::AnchorBottom, d, Qt::AnchorTop); + + // C + anchor->addAnchor(c, Qt::AnchorRight, d, Qt::AnchorLeft); + anchor->addAnchor(c, Qt::AnchorBottom, anchor, Qt::AnchorBottom); + + // D + anchor->addAnchor(d, Qt::AnchorRight, anchor, Qt::AnchorRight); + anchor->addAnchor(d, Qt::AnchorBottom, anchor, Qt::AnchorBottom); + + layout = anchor; + } + + QSizeF sizeHint; + // warm up instruction cache + layout->invalidate(); + sizeHint = layout->effectiveSizeHint(Qt::PreferredSize); + + // ...then measure... + QBENCHMARK { + // To ensure that all sizeHints caches are invalidated in + // the LinearLayout setup, we must call updateGeometry on the + // children. If we didn't, only the top level layout would be + // re-calculated. + static_cast(a)->updateGeometry(); + static_cast(b)->updateGeometry(); + static_cast(c)->updateGeometry(); + static_cast(d)->updateGeometry(); + layout->invalidate(); + sizeHint = layout->effectiveSizeHint(Qt::PreferredSize); + } +} + QTEST_MAIN(tst_QGraphicsAnchorLayout) #include "tst_qgraphicsanchorlayout.moc" -- cgit v0.12 From ea52aef43acf862a51ea5b2eb65fa2ad60e5590f Mon Sep 17 00:00:00 2001 From: Jesus Sanchez-Palencia Date: Wed, 14 Oct 2009 16:56:54 -0300 Subject: QGraphicsAnchorLayout: Adding two benchmarks on LinearLayout vs AnchorLayout cases Signed-off-by: Jesus Sanchez-Palencia Reviewed-by: Eduardo M. Fleury --- .../tst_qgraphicsanchorlayout.cpp | 127 ++++++++++++++++++++- 1 file changed, 126 insertions(+), 1 deletion(-) diff --git a/tests/benchmarks/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/benchmarks/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 43dff93..81064d7 100644 --- a/tests/benchmarks/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/benchmarks/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -55,9 +55,12 @@ public: private slots: void s60_hard_complex_data(); void s60_hard_complex(); + void linearVsAnchorSizeHints_data(); + void linearVsAnchorSizeHints(); + void linearVsAnchorSetGeometry_data(); + void linearVsAnchorSetGeometry(); void linearVsAnchorNested_data(); void linearVsAnchorNested(); - }; @@ -196,6 +199,128 @@ void tst_QGraphicsAnchorLayout::s60_hard_complex() } } +static QGraphicsLayout* createLayouts(int whichLayout) +{ + QSizeF min(0, 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"); + + QGraphicsLayout *l; + if (whichLayout == 0) { + l = new QGraphicsLinearLayout; + QGraphicsLinearLayout *linear = static_cast(l); + linear->setContentsMargins(0, 0, 0, 0); + + linear->addItem(a); + linear->addItem(b); + linear->addItem(c); + linear->addItem(d); + } else { + l = new QGraphicsAnchorLayout; + QGraphicsAnchorLayout *anchor = static_cast(l); + anchor->setContentsMargins(0, 0, 0, 0); + + // Horizontal + setAnchor(anchor, anchor, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + setAnchor(anchor, a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + setAnchor(anchor, b, Qt::AnchorRight, c, Qt::AnchorLeft, 0); + setAnchor(anchor, c, Qt::AnchorRight, d, Qt::AnchorLeft, 0); + setAnchor(anchor, d, Qt::AnchorRight, anchor, Qt::AnchorRight, 0); + + // Vertical + anchor->addAnchors(anchor, a, Qt::Vertical); + anchor->addAnchors(anchor, b, Qt::Vertical); + anchor->addAnchors(anchor, c, Qt::Vertical); + anchor->addAnchors(anchor, d, Qt::Vertical); + } + + return l; +} + +void tst_QGraphicsAnchorLayout::linearVsAnchorSizeHints_data() +{ + QTest::addColumn("whichLayout"); + QTest::addColumn("whichSizeHint"); + + QTest::newRow("QGraphicsLinearLayout::minimum") + << 0 << int(Qt::MinimumSize); + QTest::newRow("QGraphicsLinearLayout::preferred") + << 0 << int(Qt::PreferredSize); + QTest::newRow("QGraphicsLinearLayout::maximum") + << 0 << int(Qt::MaximumSize); + QTest::newRow("QGraphicsLinearLayout::noSizeHint") + << 0 << -1; + + QTest::newRow("QGraphicsAnchorLayout::minimum") + << 1 << int(Qt::MinimumSize); + QTest::newRow("QGraphicsAnchorLayout::preferred") + << 1 << int(Qt::PreferredSize); + QTest::newRow("QGraphicsAnchorLayout::maximum") + << 1 << int(Qt::MaximumSize); + QTest::newRow("QGraphicsAnchorLayout::noSizeHint") + << 1 << -1; +} + +void tst_QGraphicsAnchorLayout::linearVsAnchorSizeHints() +{ + QFETCH(int, whichSizeHint); + QFETCH(int, whichLayout); + + QGraphicsLayout *l = createLayouts(whichLayout); + + QSizeF sizeHint; + // warm up instruction cache + l->invalidate(); + sizeHint = l->effectiveSizeHint((Qt::SizeHint)whichSizeHint); + // ...then measure... + + QBENCHMARK { + l->invalidate(); + sizeHint = l->effectiveSizeHint((Qt::SizeHint)whichSizeHint); + } +} + +void tst_QGraphicsAnchorLayout::linearVsAnchorSetGeometry_data() +{ + QTest::addColumn("whichLayout"); + + QTest::newRow("QGraphicsLinearLayout") + << 0; + QTest::newRow("QGraphicsAnchorLayout") + << 1; +} + +void tst_QGraphicsAnchorLayout::linearVsAnchorSetGeometry() +{ + QFETCH(int, whichLayout); + + QGraphicsLayout *l = createLayouts(whichLayout); + + QRectF sizeHint; + qreal maxWidth; + qreal increment; + // warm up instruction cache + l->invalidate(); + sizeHint.setSize(l->effectiveSizeHint(Qt::MinimumSize)); + maxWidth = l->effectiveSizeHint(Qt::MaximumSize).width(); + increment = (maxWidth - sizeHint.width()) / 100; + l->setGeometry(sizeHint); + // ...then measure... + + QBENCHMARK { + l->invalidate(); + for (qreal width = sizeHint.width(); width <= maxWidth; width += increment) { + sizeHint.setWidth(width); + l->setGeometry(sizeHint); + } + } +} + void tst_QGraphicsAnchorLayout::linearVsAnchorNested_data() { QTest::addColumn("whichLayout"); -- cgit v0.12 From 7644b352297e9fd1ee271421e9ce8b7256946b12 Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Fri, 16 Oct 2009 09:34:38 +0200 Subject: Fixed typo in qtableview.cpp. Auto-test included. Will timeout on fail. Reviewed-by: Alexis --- src/gui/itemviews/qtableview.cpp | 2 +- tests/auto/qtableview/tst_qtableview.cpp | 35 +++++++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/gui/itemviews/qtableview.cpp b/src/gui/itemviews/qtableview.cpp index a610b73..2a937f1 100644 --- a/src/gui/itemviews/qtableview.cpp +++ b/src/gui/itemviews/qtableview.cpp @@ -1599,7 +1599,7 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi break; visualColumn = right + 1; if (visualRow == 0) { - wrapped == true; + wrapped = true; visualRow = bottom; } else { --visualRow; diff --git a/tests/auto/qtableview/tst_qtableview.cpp b/tests/auto/qtableview/tst_qtableview.cpp index d75cfa7..d8110e1 100644 --- a/tests/auto/qtableview/tst_qtableview.cpp +++ b/tests/auto/qtableview/tst_qtableview.cpp @@ -1195,6 +1195,13 @@ void tst_QTableView::moveCursorStrikesBack_data() << 0 << 5 << (IntList() << int(QtTestTableView::MoveNext)) << 1 << 0; + QTest::newRow("Last column disabled. Task QTBUG-3878") << -1 << -1 + << IntList() + << (IntList() << 6) + << QRect() + << 1 << 0 << (IntList() << int(QtTestTableView::MovePrevious)) + << 0 << 5; + QTest::newRow("Span, anchor column hidden") << -1 << 1 << IntList() << IntList() @@ -1250,6 +1257,24 @@ void tst_QTableView::moveCursorStrikesBack_data() << QRect(1, 2, 2, 3) << 5 << 2 << (IntList() << int(QtTestTableView::MoveUp) << int(QtTestTableView::MoveUp)) << 1 << 2; + + IntList fullList; + for (int i = 0; i < 7; ++i) + fullList << i; + + QTest::newRow("All disabled, wrap forward. Timeout => FAIL") << -1 << -1 + << fullList + << fullList + << QRect() + << 1 << 0 << (IntList() << int(QtTestTableView::MoveNext)) + << 1 << 0; + + QTest::newRow("All disabled, wrap backwards. Timeout => FAIL") << -1 << -1 + << fullList + << fullList + << QRect() + << 1 << 0 << (IntList() << int(QtTestTableView::MovePrevious)) + << 1 << 0; } void tst_QTableView::moveCursorStrikesBack() @@ -1272,11 +1297,6 @@ void tst_QTableView::moveCursorStrikesBack() view.hideRow(hideRow); view.hideColumn(hideColumn); - foreach (int row, disableRows) - model.disableRow(row); - foreach (int column, disableColumns) - model.disableColumn(column); - if (span.height() && span.width()) view.setSpan(span.top(), span.left(), span.height(), span.width()); view.show(); @@ -1284,6 +1304,11 @@ void tst_QTableView::moveCursorStrikesBack() QModelIndex index = model.index(startRow, startColumn); view.setCurrentIndex(index); + foreach (int row, disableRows) + model.disableRow(row); + foreach (int column, disableColumns) + model.disableColumn(column); + int newRow = -1; int newColumn = -1; foreach (int cursorMoveAction, cursorMoveActions) { -- cgit v0.12 From 7e5724b7d4632899af2e84856dd7d6f2a1771ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Fri, 16 Oct 2009 11:56:08 +0200 Subject: Make sure that the anchor layout autotests pass in release configuration The problem was that lastCalculationUsedSimplex was only compiled in debug mode. The autotests run in release, so it did not compile. Reviewed-by: alexis --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 2 +- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index c9821ae..5a531f5 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1781,7 +1781,7 @@ bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const sizeAtExpanding[orientation] = ad->sizeAtExpanding; } -#ifdef QT_DEBUG +#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT) lastCalculationUsedSimplex[orientation] = needsSimplex; #endif diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index d96a035..3a23677 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -523,7 +523,7 @@ public: bool graphHasConflicts[2]; QSet m_floatItems[2]; -#ifdef QT_DEBUG +#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT) bool lastCalculationUsedSimplex[2]; #endif -- cgit v0.12 From 299fd2c5d3c901890c0bc2c36c4b5f48d46859dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Fri, 16 Oct 2009 17:31:10 +0200 Subject: API addition: Make it possible to set the size policy of an anchor. After this commit, when you modify the spacing of an anchor you are effectively modifying the preferred size of the anchor, since all anchors (except internal ones) have their minimumSizeHint to 0 and maximumSizeHint to QWIDGETSIZE_MAX. I also changed the sizeHintsFromItem to be more generic so that I could use it for anchors. (Thus, it was renamed to "internalSizeHints"). It now only takes care of setting the min/pref/exp/maxSize of AnchorData based on the anchor/item size hint and their size policies. As a consequence of all of this, setFixedSize changed behaviour and became setPreferredSize (since setSpacing is basically setPreferredSize). The implementation of that now only sets the prefSize. The patch also has an unrelated fix for IgnoreFlag, where it will (again) return the minimumSize for sizeHint(Qt::PreferredSize) instead of the maximumSize. This was to be more consistent with qgridlayoutengine.cpp. The docs are not very clear on this behaviour unfortunately. This API change has been discussed. Reviewed-by: alexis --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 17 ++++ src/gui/graphicsview/qgraphicsanchorlayout.h | 2 + src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 109 ++++++++++++--------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 14 ++- .../tst_qgraphicsanchorlayout.cpp | 56 ++++++++--- 5 files changed, 130 insertions(+), 68 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 9f4a19b..c39e8a6 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -118,6 +118,23 @@ QGraphicsAnchor::~QGraphicsAnchor() } /*! + Sets the size policy of the anchor to \a policy. +*/ +void QGraphicsAnchor::setSizePolicy(QSizePolicy::Policy policy) +{ + Q_D(QGraphicsAnchor); + d->setSizePolicy(policy); +} +/*! + Returns the size policy of the anchor. The default size policy is QSizePolicy::Fixed +*/ +QSizePolicy::Policy QGraphicsAnchor::sizePolicy() const +{ + Q_D(const QGraphicsAnchor); + return d->sizePolicy; +} + +/*! \property QGraphicsAnchor::spacing \brief the space between items in the QGraphicsAnchorLayout. diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h index 99dbf92..f09ac43 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -65,6 +65,8 @@ class Q_GUI_EXPORT QGraphicsAnchor : public QObject public: void setSpacing(qreal spacing); void unsetSpacing(); + void setSizePolicy(QSizePolicy::Policy policy); + QSizePolicy::Policy sizePolicy() const; qreal spacing() const; ~QGraphicsAnchor(); private: diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index e1db939..8c8c303 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -53,7 +53,8 @@ QT_BEGIN_NAMESPACE QGraphicsAnchorPrivate::QGraphicsAnchorPrivate(int version) - : QObjectPrivate(version), layoutPrivate(0), data(0) + : QObjectPrivate(version), layoutPrivate(0), data(0), + sizePolicy(QSizePolicy::Fixed) { } @@ -62,6 +63,14 @@ QGraphicsAnchorPrivate::~QGraphicsAnchorPrivate() layoutPrivate->removeAnchor(data->from, data->to); } +void QGraphicsAnchorPrivate::setSizePolicy(QSizePolicy::Policy policy) +{ + if (sizePolicy != policy) { + sizePolicy = policy; + layoutPrivate->q_func()->invalidate(); + } +} + void QGraphicsAnchorPrivate::setSpacing(qreal value) { if (data) { @@ -92,28 +101,11 @@ qreal QGraphicsAnchorPrivate::spacing() const } -static void sizeHintsFromItem(QGraphicsLayoutItem *item, - const QGraphicsAnchorLayoutPrivate::Orientation orient, - qreal *minSize, qreal *prefSize, - qreal *expSize, qreal *maxSize) +static void internalSizeHints(QSizePolicy::Policy policy, + qreal minSizeHint, qreal prefSizeHint, qreal maxSizeHint, + 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. // @@ -139,7 +131,7 @@ static void sizeHintsFromItem(QGraphicsLayoutItem *item, // Note that these two initializations are affected by the previous flags if (policy & QSizePolicy::IgnoreFlag) - *prefSize = *maxSize; + *prefSize = *minSize; else *prefSize = prefSizeHint; @@ -153,38 +145,63 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) { const bool isInternalAnchor = from->m_item == to->m_item; + QSizePolicy::Policy policy; + qreal minSizeHint; + qreal prefSizeHint; + qreal maxSizeHint; + if (isInternalAnchor) { const QGraphicsAnchorLayoutPrivate::Orientation orient = QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge); + const Qt::AnchorPoint centerEdge = + QGraphicsAnchorLayoutPrivate::pickEdge(Qt::AnchorHorizontalCenter, orient); + bool hasCenter = (from->m_edge == centerEdge || to->m_edge == centerEdge); if (isLayoutAnchor) { minSize = 0; prefSize = 0; expSize = 0; maxSize = QWIDGETSIZE_MAX; + if (hasCenter) + maxSize /= 2; + return; } else { - QGraphicsLayoutItem *item = from->m_item; - sizeHintsFromItem(item, orient, &minSize, &prefSize, &expSize, &maxSize); - } - const Qt::AnchorPoint centerEdge = - QGraphicsAnchorLayoutPrivate::pickEdge(Qt::AnchorHorizontalCenter, orient); - bool hasCenter = (from->m_edge == centerEdge || to->m_edge == centerEdge); + QGraphicsLayoutItem *item = from->m_item; + 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(); + } - if (hasCenter) { - minSize /= 2; - prefSize /= 2; - expSize /= 2; - maxSize /= 2; + if (hasCenter) { + minSizeHint /= 2; + prefSizeHint /= 2; + maxSizeHint /= 2; + } } - - } else if (!hasSize) { - // Anchor has no size defined, use given default information - minSize = effectiveSpacing; - prefSize = effectiveSpacing; - expSize = effectiveSpacing; - maxSize = effectiveSpacing; + } else { + Q_ASSERT(graphicsAnchor); + policy = graphicsAnchor->sizePolicy(); + minSizeHint = 0; + if (hasSize) { + // One can only configure the preferred size of a normal anchor. Their minimum and + // maximum "size hints" are always 0 and QWIDGETSIZE_MAX, correspondingly. However, + // their effective size hints might be narrowed down due to their size policies. + prefSizeHint = prefSize; + } else { + prefSizeHint = effectiveSpacing; + } + maxSizeHint = QWIDGETSIZE_MAX; } + internalSizeHints(policy, minSizeHint, prefSizeHint, maxSizeHint, + &minSize, &prefSize, &expSize, &maxSize); // Set the anchor effective sizes to preferred. // @@ -1191,18 +1208,18 @@ QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *fi || secondItem == q || pickEdge(firstEdge, Horizontal) == Qt::AnchorHorizontalCenter || oppositeEdge(firstEdge) != secondEdge) { - data->setFixedSize(0); + data->setPreferredSize(0); } else { data->unsetSize(); } addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data); } else if (*spacing >= 0) { - data->setFixedSize(*spacing); + data->setPreferredSize(*spacing); addAnchor_helper(firstItem, firstEdge, secondItem, secondEdge, data); } else { - data->setFixedSize(-*spacing); + data->setPreferredSize(-*spacing); addAnchor_helper(secondItem, secondEdge, firstItem, firstEdge, data); } @@ -1389,9 +1406,9 @@ void QGraphicsAnchorLayoutPrivate::setAnchorSize(AnchorData *data, const qreal * // positive by definition. // "negative spacing" is handled by inverting the standard item order. if (*anchorSize >= 0) { - data->setFixedSize(*anchorSize); + data->setPreferredSize(*anchorSize); } else { - data->setFixedSize(-*anchorSize); + data->setPreferredSize(-*anchorSize); qSwap(data->from, data->to); } } else { diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 3a23677..d45c004 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -169,16 +169,9 @@ struct AnchorData : public QSimplexVariable { QString name; #endif - inline void setFixedSize(qreal size) + inline void setPreferredSize(qreal size) { - minSize = size; prefSize = size; - expSize = size; - maxSize = size; - sizeAtMinimum = size; - sizeAtPreferred = size; - sizeAtExpanding = size; - sizeAtMaximum = size; hasSize = true; } @@ -316,8 +309,11 @@ public: void unsetSpacing(); qreal spacing() const; + void setSizePolicy(QSizePolicy::Policy policy); + QGraphicsAnchorLayoutPrivate *layoutPrivate; AnchorData *data; + QSizePolicy::Policy sizePolicy; }; @@ -528,6 +524,8 @@ public: #endif uint calculateGraphCacheDirty : 1; + + friend class QGraphicsAnchorPrivate; }; QT_END_NAMESPACE diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index cce7b4c..7b87969 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -1296,19 +1296,8 @@ void tst_QGraphicsAnchorLayout::sizePolicy() l->setSpacing(0); l->setContentsMargins(0, 0, 0, 0); - // horizontal - QGraphicsAnchor *anchor = l->addAnchor(l, Qt::AnchorLeft, w1, Qt::AnchorLeft); - anchor->setSpacing(0); - - anchor = l->addAnchor(w1, Qt::AnchorRight, l, Qt::AnchorRight); - anchor->setSpacing(0); - - // vertical - anchor = l->addAnchor(l, Qt::AnchorTop, w1, Qt::AnchorTop); - anchor->setSpacing(0); - - anchor = l->addAnchor(w1, Qt::AnchorBottom, l, Qt::AnchorBottom); - anchor->setSpacing(0); + // horizontal and vertical + l->addAnchors(l, w1); QGraphicsWidget *p = new QGraphicsWidget; p->setLayout(l); @@ -1358,9 +1347,48 @@ void tst_QGraphicsAnchorLayout::sizePolicy() w1->adjustSize(); QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(0, 0)); - QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(100, 100)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(0, 0)); QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(100, 100)); + // Anchor size policies + w1->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + QGraphicsAnchor *anchor = l->anchor(l, Qt::AnchorLeft, w1, Qt::AnchorLeft); + anchor->setSpacing(10); + + // QSizePolicy::Minimum + anchor->setSizePolicy(QSizePolicy::Minimum); + QApplication::processEvents(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(60, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(60, 50)); + // The layout has a maximum size of QWIDGETSIZE_MAX, so the result won't exceed that value. + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(QWIDGETSIZE_MAX, 50)); + + // QSizePolicy::Preferred + anchor->setSizePolicy(QSizePolicy::Preferred); + QApplication::processEvents(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(60, 50)); + // The layout has a maximum size of QWIDGETSIZE_MAX, so the result won't exceed that value. + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(QWIDGETSIZE_MAX, 50)); + + // QSizePolicy::Maximum + anchor->setSizePolicy(QSizePolicy::Maximum); + QApplication::processEvents(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(60, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(60, 50)); + + // QSizePolicy::Ignored + anchor->setSizePolicy(QSizePolicy::Ignored); + QApplication::processEvents(); + + QCOMPARE(l->effectiveSizeHint(Qt::MinimumSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::PreferredSize), QSizeF(50, 50)); + QCOMPARE(l->effectiveSizeHint(Qt::MaximumSize), QSizeF(QWIDGETSIZE_MAX, 50)); + if (hasSimplification) { QVERIFY(!usedSimplex(l, Qt::Horizontal)); QVERIFY(!usedSimplex(l, Qt::Vertical)); -- cgit v0.12 From 6f20e48d946fa3c90215f6affe5aa490559a6971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Mon, 19 Oct 2009 09:25:12 +0200 Subject: Whitespace cleanup. --- src/gui/graphicsview/qgraphicsgridlayout.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsgridlayout.cpp b/src/gui/graphicsview/qgraphicsgridlayout.cpp index d1d91db..eef4e45 100644 --- a/src/gui/graphicsview/qgraphicsgridlayout.cpp +++ b/src/gui/graphicsview/qgraphicsgridlayout.cpp @@ -63,7 +63,7 @@ You can access each item in the layout by calling count() and itemAt(). Calling removeAt() will remove an item from the layout, without destroying it. - + \sa QGraphicsLinearLayout, QGraphicsWidget */ @@ -89,7 +89,7 @@ public: QLayoutStyleInfo styleInfo() const; QGridLayoutEngine engine; -#ifdef QT_DEBUG +#ifdef QT_DEBUG void dump(int indent) const; #endif }; @@ -121,7 +121,7 @@ QGraphicsGridLayout::~QGraphicsGridLayout() for (int i = count() - 1; i >= 0; --i) { QGraphicsLayoutItem *item = itemAt(i); // The following lines can be removed, but this removes the item - // from the layout more efficiently than the implementation of + // from the layout more efficiently than the implementation of // ~QGraphicsLayoutItem. removeAt(i); if (item) { @@ -141,18 +141,18 @@ void QGraphicsGridLayout::addItem(QGraphicsLayoutItem *item, int row, int column { Q_D(QGraphicsGridLayout); if (row < 0 || column < 0) { - qWarning("QGraphicsGridLayout::addItem: invalid row/column: %d", - row < 0 ? row : column); - return; + qWarning("QGraphicsGridLayout::addItem: invalid row/column: %d", + row < 0 ? row : column); + return; } if (columnSpan < 1 || rowSpan < 1) { - qWarning("QGraphicsGridLayout::addItem: invalid row span/column span: %d", - rowSpan < 1 ? rowSpan : columnSpan); - return; + qWarning("QGraphicsGridLayout::addItem: invalid row span/column span: %d", + rowSpan < 1 ? rowSpan : columnSpan); + return; } if (!item) { - qWarning("QGraphicsGridLayout::addItem: cannot add null item"); - return; + qWarning("QGraphicsGridLayout::addItem: cannot add null item"); + return; } d->addChildLayoutItem(item); @@ -647,5 +647,5 @@ QSizePolicy::ControlTypes QGraphicsGridLayout::controlTypes(LayoutSide side) con #endif QT_END_NAMESPACE - -#endif //QT_NO_GRAPHICSVIEW + +#endif //QT_NO_GRAPHICSVIEW -- cgit v0.12 From 856efef9bf87e0b7e5ed6ad2ccb225e294321888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Mon, 19 Oct 2009 09:39:30 +0200 Subject: Do not loop forever if we are adding the layout itself to the layout. Reviewed-by: alexis --- src/gui/graphicsview/qgraphicsgridlayout.cpp | 4 ++++ src/gui/graphicsview/qgraphicslinearlayout.cpp | 4 ++++ tests/auto/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp | 11 +++++++++++ .../auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp | 11 +++++++++++ 4 files changed, 30 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsgridlayout.cpp b/src/gui/graphicsview/qgraphicsgridlayout.cpp index eef4e45..9a8dba0 100644 --- a/src/gui/graphicsview/qgraphicsgridlayout.cpp +++ b/src/gui/graphicsview/qgraphicsgridlayout.cpp @@ -154,6 +154,10 @@ void QGraphicsGridLayout::addItem(QGraphicsLayoutItem *item, int row, int column qWarning("QGraphicsGridLayout::addItem: cannot add null item"); return; } + if (item == this) { + qWarning("QGraphicsGridLayout::addItem: cannot insert itself"); + return; + } d->addChildLayoutItem(item); diff --git a/src/gui/graphicsview/qgraphicslinearlayout.cpp b/src/gui/graphicsview/qgraphicslinearlayout.cpp index 0aa68df..7ff7c9b 100644 --- a/src/gui/graphicsview/qgraphicslinearlayout.cpp +++ b/src/gui/graphicsview/qgraphicslinearlayout.cpp @@ -272,6 +272,10 @@ void QGraphicsLinearLayout::insertItem(int index, QGraphicsLayoutItem *item) qWarning("QGraphicsLinearLayout::insertItem: cannot insert null item"); return; } + if (item == this) { + qWarning("QGraphicsLinearLayout::insertItem: cannot insert itself"); + return; + } d->addChildLayoutItem(item); Q_ASSERT(item); diff --git a/tests/auto/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp b/tests/auto/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp index 554292f..e33c7b6 100644 --- a/tests/auto/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp +++ b/tests/auto/qgraphicsgridlayout/tst_qgraphicsgridlayout.cpp @@ -104,6 +104,7 @@ private slots: void defaultStretchFactors(); void geometries_data(); void geometries(); + void avoidRecursionInInsertItem(); void task236367_maxSizeHint(); }; @@ -2081,6 +2082,16 @@ void tst_QGraphicsGridLayout::geometries() delete widget; } +void tst_QGraphicsGridLayout::avoidRecursionInInsertItem() +{ + QGraphicsWidget window(0, Qt::Window); + QGraphicsGridLayout *layout = new QGraphicsGridLayout(&window); + QCOMPARE(layout->count(), 0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsGridLayout::addItem: cannot insert itself"); + layout->addItem(layout, 0, 0); + QCOMPARE(layout->count(), 0); +} + void tst_QGraphicsGridLayout::task236367_maxSizeHint() { QGraphicsWidget *widget = new QGraphicsWidget; diff --git a/tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp b/tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp index 8e7bb45..546f92d 100644 --- a/tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp +++ b/tests/auto/qgraphicslinearlayout/tst_qgraphicslinearlayout.cpp @@ -101,6 +101,7 @@ private slots: void updateGeometry(); void layoutDirection(); void removeLayout(); + void avoidRecursionInInsertItem(); // Task specific tests void task218400_insertStretchCrash(); @@ -1432,6 +1433,16 @@ void tst_QGraphicsLinearLayout::removeLayout() QCOMPARE(pushButton->geometry(), r2); } +void tst_QGraphicsLinearLayout::avoidRecursionInInsertItem() +{ + QGraphicsWidget window(0, Qt::Window); + QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(&window); + QCOMPARE(layout->count(), 0); + QTest::ignoreMessage(QtWarningMsg, "QGraphicsLinearLayout::insertItem: cannot insert itself"); + layout->insertItem(0, layout); + QCOMPARE(layout->count(), 0); +} + void tst_QGraphicsLinearLayout::task218400_insertStretchCrash() { QGraphicsScene *scene = new QGraphicsScene; -- cgit v0.12 From 2633931653757decd93dd3939c09f5e07203da1c Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Mon, 19 Oct 2009 11:59:20 +0200 Subject: Fix a bug affecting keyboard navigation in the table view Reviewed-by: thierry Reviewed-by: pierre --- src/gui/itemviews/qlistview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp index 243f542..1d9b6e0 100644 --- a/src/gui/itemviews/qlistview.cpp +++ b/src/gui/itemviews/qlistview.cpp @@ -1105,13 +1105,13 @@ QModelIndex QListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifie ++row; if (row >= rowCount) return QModelIndex(); - return d->model->index(row, 0, d->root); + return d->model->index(row, d->column, d->root); } const QRect initialRect = rectForIndex(current); QRect rect = initialRect; if (rect.isEmpty()) { - return d->model->index(0, 0, d->root); + return d->model->index(0, d->column, d->root); } if (d->gridSize().isValid()) rect.setSize(d->gridSize()); -- cgit v0.12 From 93550050f4fe4f411bfbd80d7b30ff5bc8a20df7 Mon Sep 17 00:00:00 2001 From: Pierre Rossi Date: Mon, 19 Oct 2009 21:37:45 +0200 Subject: add the autotest for the QListView::setModelColumn bug sha1 of the fix: 2633931653757decd93dd3939c09f5e07203da1c --- tests/auto/qlistview/tst_qlistview.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/auto/qlistview/tst_qlistview.cpp b/tests/auto/qlistview/tst_qlistview.cpp index 7599ce6a06..3ee6889 100644 --- a/tests/auto/qlistview/tst_qlistview.cpp +++ b/tests/auto/qlistview/tst_qlistview.cpp @@ -116,6 +116,7 @@ private slots: void keyboardSearch(); void shiftSelectionWithNonUniformItemSizes(); void clickOnViewportClearsSelection(); + void task262152_setModelColumnNavigate(); }; // Testing get/set functions @@ -1767,6 +1768,29 @@ void tst_QListView::clickOnViewportClearsSelection() } +void tst_QListView::task262152_setModelColumnNavigate() +{ + QListView view; + QStandardItemModel model(3,2); + model.setItem(0,1,new QStandardItem("[0,1]")); + model.setItem(1,1,new QStandardItem("[1,1]")); + model.setItem(2,1,new QStandardItem("[2,1]")); + + view.setModel(&model); + view.setModelColumn(1); + + view.show(); + QTest::qWait(30); + QTest::keyClick(&view, Qt::Key_Down); + QTest::qWait(10); + QCOMPARE(view.currentIndex(), model.index(1,1)); + QTest::keyClick(&view, Qt::Key_Down); + QTest::qWait(10); + QCOMPARE(view.currentIndex(), model.index(2,1)); + +} + + QTEST_MAIN(tst_QListView) #include "tst_qlistview.moc" -- cgit v0.12