diff options
author | Alexis Menard <alexis.menard@nokia.com> | 2009-10-20 08:28:49 (GMT) |
---|---|---|
committer | Alexis Menard <alexis.menard@nokia.com> | 2009-10-20 08:28:49 (GMT) |
commit | 1a11cc88e8516097e73d7aefc44c9a97ea0b5aef (patch) | |
tree | 0d1e57ee49db51982083f1fbdba19f5f852f9bf4 | |
parent | eeac9aa8d51b11f3040dcf818cec0a58cd0898f0 (diff) | |
parent | 93550050f4fe4f411bfbd80d7b30ff5bc8a20df7 (diff) | |
download | Qt-1a11cc88e8516097e73d7aefc44c9a97ea0b5aef.zip Qt-1a11cc88e8516097e73d7aefc44c9a97ea0b5aef.tar.gz Qt-1a11cc88e8516097e73d7aefc44c9a97ea0b5aef.tar.bz2 |
Merge branch '4.6' of git@scm.dev.nokia.troll.no:qt/qt-widget-team into 4.6
20 files changed, 1089 insertions, 453 deletions
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")); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index ffbb67c..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. @@ -306,6 +323,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 +342,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 +358,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.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 f9b5c8c..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. // @@ -250,44 +267,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<QGraphicsAnchorLayoutPrivate::Interval, qreal> 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<QGraphicsAnchorLayoutPrivate::Interval, qreal> &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 +341,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<QGraphicsAnchorLayoutPrivate::Interval, qreal> minFactor = + getFactor(sizeAtMinimum, minSize, prefSize, expSize, maxSize); + const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> prefFactor = + getFactor(sizeAtPreferred, minSize, prefSize, expSize, maxSize); + const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> expFactor = + getFactor(sizeAtExpanding, minSize, prefSize, expSize, maxSize); + const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> 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(); } @@ -494,36 +523,46 @@ static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *graph, const QVector<AnchorVertex*> &vertices, AnchorVertex *after) { - int i; + AnchorData *data = graph->edgeData(before, vertices.first()); + Q_ASSERT(data); + + const bool forward = (before == data->from); + QVector<AnchorVertex *> 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. @@ -578,15 +617,6 @@ static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *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) { @@ -603,9 +633,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; @@ -614,164 +642,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<AnchorVertex, AnchorData> &g = graph[orientation]; - AnchorVertex *v = g.rootVertex(); QSet<AnchorVertex *> visited; - QStack<AnchorVertex *> stack; - stack.push(v); + QStack<QPair<AnchorVertex *, AnchorVertex *> > stack; + stack.push(qMakePair(static_cast<AnchorVertex *>(0), g.rootVertex())); QVector<AnchorVertex*> 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<AnchorVertex *> 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<AnchorVertex *, AnchorVertex *> 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<AnchorVertex *> &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<AnchorVertex *> 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<AnchorVertex *> 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<AnchorVertex *> 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, <candidates>, 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<AnchorVertex*> 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; - if (endOfSequence) - candidates.clear(); + // + // Create a sequence for (beforeSequence, candidates, afterSequence). + // - for (int i = 0; i < count; ++i) { - AnchorVertex *next = vertices.at(i); - if (next->m_item == q && next->m_edge == centerEdge) + // 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); + + // 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<AnchorVertex, AnchorData> &g, @@ -1173,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); } @@ -1371,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 { @@ -1550,6 +1585,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; } @@ -1565,13 +1607,24 @@ 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; } -// ### remove me: +// ### Maybe getGraphParts could return the variables when traversing, at least +// for trunk... QList<AnchorData *> getVariables(QList<QSimplexConstraint *> constraints) { QSet<AnchorData *> variableSet; @@ -1635,65 +1688,92 @@ 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<QList<QSimplexConstraint *> > parts; - parts = getGraphParts(orientation); + QList<QList<QSimplexConstraint *> > parts = getGraphParts(orientation); // Now run the simplex solver to calculate Minimum, Preferred and Maximum sizes // of the "trunk" set of constraints and variables. // ### does trunk always exist? empty = trunk is the layout left->center->right QList<QSimplexConstraint *> trunkConstraints = parts[0]; - QList<QSimplexConstraint *> sizeHintConstraints; - sizeHintConstraints = constraintsFromSizeHints(getVariables(trunkConstraints)); - trunkConstraints += sizeHintConstraints; + QList<AnchorData *> trunkVariables = getVariables(trunkConstraints); // 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<QSimplexConstraint *> partConstraints = parts[i]; + QList<AnchorData *> 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<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables) +{ bool feasible = true; - if (!trunkConstraints.isEmpty()) { + bool needsSimplex = !constraints.isEmpty(); + #if 0 - qDebug("Simplex used for trunk of %s", - orientation == Horizontal ? "Horizontal" : "Vertical"); + 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; - feasible = solveMinMax(trunkConstraints, trunkPath, &min, &max); + if (needsSimplex) { - if (feasible) { - // Solve for preferred. The objective function is calculated from the constraints - // and variables internally. - solvePreferred(trunkConstraints); + QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables); + QList<QSimplexConstraint *> allConstraints = constraints + sizeHintConstraints; - // remove sizeHintConstraints from trunkConstraints - trunkConstraints = parts[0]; - - // Solve for expanding. The objective function and the constraints from items - // are calculated internally. - solveExpanding(trunkConstraints); - - // Propagate the new sizes down the simplified graph, ie. tell the - // group anchors to set their children anchors sizes. + // Solve min and max size hints + qreal min, max; + feasible = solveMinMax(allConstraints, path, &min, &max); - // ### we calculated variables already a few times, can't we reuse that? - QList<AnchorData *> trunkVariables = getVariables(trunkConstraints); + if (feasible) { + solvePreferred(allConstraints, variables); - for (int i = 0; i < trunkVariables.count(); ++i) - trunkVariables.at(i)->updateChildrenSizes(); + // 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; } @@ -1703,76 +1783,57 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( sizeHints[orientation][Qt::MaximumSize] = max; sizeAtExpanding[orientation] = expanding; } - } else { -#if 0 - qDebug("Simplex NOT used for trunk of %s", - orientation == Horizontal ? "Horizontal" : "Vertical"); -#endif + qDeleteAll(sizeHintConstraints); + + } else { // 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; 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; sizeAtExpanding[orientation] = ad->sizeAtExpanding; } - // Delete the constraints, we won't use them anymore. - qDeleteAll(sizeHintConstraints); - sizeHintConstraints.clear(); +#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT) + lastCalculationUsedSimplex[orientation] = needsSimplex; +#endif - // 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. + return feasible; +} - // Solve the other only for preferred, skip trunk - if (feasible) { - for (int i = 1; i < parts.count(); ++i) { - QList<QSimplexConstraint *> partConstraints = parts[i]; - QList<AnchorData *> partVariables = getVariables(partConstraints); - Q_ASSERT(!partVariables.isEmpty()); - - sizeHintConstraints = constraintsFromSizeHints(partVariables); - partConstraints += sizeHintConstraints; - feasible &= solvePreferred(partConstraints); - if (!feasible) - break; - - // 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; - ad->updateChildrenSizes(); - } +/*! + \internal +*/ +bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables) +{ + QList<QSimplexConstraint *> sizeHintConstraints = constraintsFromSizeHints(variables); + bool feasible = solvePreferred(constraints + sizeHintConstraints, variables); - // Delete the constraints, we won't use them anymore. - qDeleteAll(sizeHintConstraints); - sizeHintConstraints.clear(); + 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; } } - 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; } /*! @@ -1876,6 +1937,20 @@ void QGraphicsAnchorLayoutPrivate::constraintsFromPaths(Orientation orientation) /*! \internal +*/ +void QGraphicsAnchorLayoutPrivate::updateAnchorSizes(Orientation orientation) +{ + Graph<AnchorVertex, AnchorData> &g = graph[orientation]; + const QList<QPair<AnchorVertex *, AnchorVertex *> > &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 @@ -2160,39 +2235,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<Interval, qreal> 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; } /*! @@ -2219,20 +2281,11 @@ void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, AnchorData *edge, Orientation orientation) { - qreal lower, upper; + const QPair<Interval, qreal> factor(interpolationInterval[orientation], + interpolationProgress[orientation]); - 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; - } - - 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); @@ -2303,7 +2356,7 @@ void QGraphicsAnchorLayoutPrivate::interpolateSequentialEdges( interpolateEdge(prev, data->m_edges.last(), orientation); } -bool QGraphicsAnchorLayoutPrivate::solveMinMax(QList<QSimplexConstraint *> constraints, +bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *> &constraints, GraphPath path, qreal *min, qreal *max) { QSimplex simplex; @@ -2344,9 +2397,9 @@ bool QGraphicsAnchorLayoutPrivate::solveMinMax(QList<QSimplexConstraint *> const return feasible; } -bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList<QSimplexConstraint *> constraints) +bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables) { - QList<AnchorData *> variables = getVariables(constraints); QList<QSimplexConstraint *> preferredConstraints; QList<QSimplexVariable *> preferredVariables; QSimplexConstraint objective; @@ -2369,7 +2422,7 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList<QSimplexConstraint *> co // A + A_shrinker - A_grower = A_pref // for (int i = 0; i < variables.size(); ++i) { - AnchorData *ad = static_cast<AnchorData *>(variables[i]); + AnchorData *ad = variables[i]; if (ad->skipInPreferred) continue; @@ -2400,7 +2453,7 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList<QSimplexConstraint *> co // Save sizeAtPreferred results for (int i = 0; i < variables.size(); ++i) { - AnchorData *ad = static_cast<AnchorData *>(variables[i]); + AnchorData *ad = variables[i]; ad->sizeAtPreferred = ad->result; } @@ -2461,9 +2514,9 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(QList<QSimplexConstraint *> 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<QSimplexConstraint *> constraints) +void QGraphicsAnchorLayoutPrivate::solveExpanding(const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables) { - QList<AnchorData *> variables = getVariables(constraints); QList<QSimplexConstraint *> itemConstraints; QSimplexConstraint *objective = new QSimplexConstraint; bool hasExpanding = false; @@ -2566,9 +2619,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..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; }; @@ -438,9 +434,17 @@ public: void calculateGraphs(); void calculateGraphs(Orientation orientation); + + bool calculateTrunk(Orientation orientation, const GraphPath &trunkPath, + const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables); + bool calculateNonTrunk(const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables); + void setAnchorSizeHintsFromItems(Orientation orientation); void findPaths(Orientation orientation); void constraintsFromPaths(Orientation orientation); + void updateAnchorSizes(Orientation orientation); QList<QSimplexConstraint *> constraintsFromSizeHints(const QList<AnchorData *> &anchors); QList<QList<QSimplexConstraint *> > getGraphParts(Orientation orientation); void identifyFloatItems(const QSet<AnchorData *> &visited, Orientation orientation); @@ -471,14 +475,16 @@ public: Orientation orientation); // Linear Programming solver methods - bool solveMinMax(QList<QSimplexConstraint *> constraints, + bool solveMinMax(const QList<QSimplexConstraint *> &constraints, GraphPath path, qreal *min, qreal *max); - bool solvePreferred(QList<QSimplexConstraint *> constraints); - void solveExpanding(QList<QSimplexConstraint *> constraints); + bool solvePreferred(const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables); + void solveExpanding(const QList<QSimplexConstraint *> &constraints, + const QList<AnchorData *> &variables); bool hasConflicts() const; #ifdef QT_DEBUG - void dumpGraph(); + void dumpGraph(const QString &name = QString()); #endif @@ -513,7 +519,13 @@ public: bool graphHasConflicts[2]; QSet<QGraphicsLayoutItem *> m_floatItems[2]; +#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT) + bool lastCalculationUsedSimplex[2]; +#endif + uint calculateGraphCacheDirty : 1; + + friend class QGraphicsAnchorPrivate; }; QT_END_NAMESPACE diff --git a/src/gui/graphicsview/qgraphicsgridlayout.cpp b/src/gui/graphicsview/qgraphicsgridlayout.cpp index d1d91db..9a8dba0 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,22 @@ 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; + } + if (item == this) { + qWarning("QGraphicsGridLayout::addItem: cannot insert itself"); + return; } d->addChildLayoutItem(item); @@ -647,5 +651,5 @@ QSizePolicy::ControlTypes QGraphicsGridLayout::controlTypes(LayoutSide side) con #endif QT_END_NAMESPACE - -#endif //QT_NO_GRAPHICSVIEW + +#endif //QT_NO_GRAPHICSVIEW 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/src/gui/itemviews/qlistview.cpp b/src/gui/itemviews/qlistview.cpp index 9b0b00f..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()); @@ -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()); 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/src/gui/kernel/qapplication.cpp b/src/gui/kernel/qapplication.cpp index 6f6d706..41d8098 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/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 286ea2d..7b87969 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(); @@ -73,6 +81,7 @@ private slots: void expandingSequenceFairDistribution(); void expandingParallel(); void floatConflict(); + void infiniteMaxSizes(); }; class RectWidget : public QGraphicsWidget @@ -152,6 +161,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(); @@ -169,10 +187,16 @@ 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); + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::simple_center() @@ -212,6 +236,11 @@ void tst_QGraphicsAnchorLayout::simple_center() QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize, QSizeF(200, 20)); + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } + delete p; } @@ -309,6 +338,9 @@ void tst_QGraphicsAnchorLayout::layoutDirection() QCOMPARE(checkReverseDirection(p), true); + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + delete p; delete view; } @@ -397,6 +429,11 @@ void tst_QGraphicsAnchorLayout::diagonal() QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 75.0, 100.0)); QCOMPARE(p.size(), testA); + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } + QCOMPARE(checkReverseDirection(&p), true); c->setMinimumWidth(300); @@ -473,6 +510,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)); @@ -493,6 +533,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() @@ -538,6 +581,11 @@ void tst_QGraphicsAnchorLayout::parallel2() p.resize(layoutMaximumSize); QCOMPARE(p.size(), layoutMaximumSize); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::snake() @@ -729,6 +777,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)); @@ -748,6 +798,11 @@ 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); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() @@ -804,6 +859,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()); @@ -824,6 +882,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() @@ -886,6 +947,11 @@ void tst_QGraphicsAnchorLayout::proportionalPreferred() QCOMPARE(a->size().width(), 10 * factor); QCOMPARE(c->size().width(), 14 * factor); QCOMPARE(p.size(), QSizeF(12, 400)); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::example() @@ -969,6 +1035,11 @@ void tst_QGraphicsAnchorLayout::example() QCOMPARE(a->size(), e->size()); QCOMPARE(b->size(), d->size()); QCOMPARE(f->size(), g->size()); + + if (hasSimplification) { + QVERIFY(usedSimplex(l, Qt::Horizontal)); + QVERIFY(usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::setSpacing() @@ -1225,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); @@ -1287,9 +1347,53 @@ 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)); + } + delete p; delete view; } @@ -1380,6 +1484,11 @@ void tst_QGraphicsAnchorLayout::expandingSequence() QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize.width(), qreal(200)); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() @@ -1441,6 +1550,11 @@ void tst_QGraphicsAnchorLayout::expandingSequenceFairDistribution() QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); QCOMPARE(layoutMaximumSize.width(), qreal(400)); + 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 // this seems to not conform with what QGraphicsLinearLayout does. @@ -1463,6 +1577,11 @@ 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)); + + if (hasSimplification) { + QVERIFY(!usedSimplex(l, Qt::Horizontal)); + QVERIFY(!usedSimplex(l, Qt::Vertical)); + } } void tst_QGraphicsAnchorLayout::expandingParallel() @@ -1585,5 +1704,56 @@ 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"); + + //<!-- Trunk --> + 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)); + + 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)); + 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" diff --git a/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp b/tests/auto/qgraphicsanchorlayout1/tst_qgraphicsanchorlayout1.cpp index 148b2c8..1c7a159 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); @@ -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); } 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 4f28df4..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(); @@ -146,7 +147,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 +333,7 @@ void tst_QGraphicsLinearLayout::alignment() widget->resize(newSize); view.show(); widget->show(); + QTest::qWaitForWindowShown(&view); QApplication::processEvents(); int x = 0; @@ -1431,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; 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<QRectF> >("QList<QRectF>"); QSignalSpy signalSpy(&scene, SIGNAL(changed(const QList<QRectF> &))); 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); 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" 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) { diff --git a/tests/auto/qwidget/tst_qwidget.cpp b/tests/auto/qwidget/tst_qwidget.cpp index 1b898f4..a03f112 100644 --- a/tests/auto/qwidget/tst_qwidget.cpp +++ b/tests/auto/qwidget/tst_qwidget.cpp @@ -69,6 +69,9 @@ #include <QtGui/qpaintengine.h> #include <private/qbackingstore_p.h> +#include <QtGui/QGraphicsView> +#include <QtGui/QGraphicsProxyWidget> + #include "../../shared/util.h" @@ -290,6 +293,7 @@ private slots: #ifdef Q_WS_X11 void minAndMaxSizeWithX11BypassWindowManagerHint(); void showHideShow(); + void clean_qt_x11_enforce_cursor(); #endif void compatibilityChildInsertedEvents(); @@ -6194,6 +6198,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 @@ -7605,11 +7638,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(); @@ -8310,7 +8343,7 @@ public: static bool firstTime = true; if (firstTime) - QTimer::singleShot(150, this, SLOT(resizeMe())); + QTimer::singleShot(250, this, SLOT(resizeMe())); firstTime = false; } @@ -8327,7 +8360,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); diff --git a/tests/benchmarks/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/benchmarks/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 000ab6e..81064d7 100644 --- a/tests/benchmarks/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/benchmarks/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -41,6 +41,7 @@ #include <QtTest/QtTest> #include <QtGui/qgraphicsanchorlayout.h> +#include <QtGui/qgraphicslinearlayout.h> #include <QtGui/qgraphicswidget.h> #include <QtGui/qgraphicsview.h> @@ -54,6 +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(); }; @@ -192,6 +199,235 @@ 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<QGraphicsLinearLayout *>(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<QGraphicsAnchorLayout *>(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<int>("whichLayout"); + QTest::addColumn<int>("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<int>("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<int>("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<QGraphicsLayoutItem *>(a)->updateGeometry(); + static_cast<QGraphicsLayoutItem *>(b)->updateGeometry(); + static_cast<QGraphicsLayoutItem *>(c)->updateGeometry(); + static_cast<QGraphicsLayoutItem *>(d)->updateGeometry(); + layout->invalidate(); + sizeHint = layout->effectiveSizeHint(Qt::PreferredSize); + } +} + QTEST_MAIN(tst_QGraphicsAnchorLayout) #include "tst_qgraphicsanchorlayout.moc" |