summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/graphicsview/qgraphicsanchorlayout_p.cpp315
-rw-r--r--src/gui/graphicsview/qgraphicsanchorlayout_p.h137
2 files changed, 379 insertions, 73 deletions
diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp
index ef89612..395c035 100644
--- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp
+++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp
@@ -61,6 +61,8 @@ QGraphicsAnchorPrivate::QGraphicsAnchorPrivate(int version)
QGraphicsAnchorPrivate::~QGraphicsAnchorPrivate()
{
+ // ###
+ layoutPrivate->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Orientation(data->orientation));
layoutPrivate->removeAnchor(data->from, data->to);
}
@@ -700,30 +702,185 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation)
if (graphSimplified[orientation])
return true;
- graphSimplified[orientation] = true;
#if 0
qDebug("Simplifying Graph for %s",
orientation == Horizontal ? "Horizontal" : "Vertical");
#endif
- if (!layoutFirstVertex[orientation])
- return true;
+ // Vertex simplification
+ if (!simplifyVertices(orientation)) {
+ restoreVertices(orientation);
+ return false;
+ }
+ // Anchor simplification
bool dirty;
bool feasible = true;
do {
dirty = simplifyGraphIteration(orientation, &feasible);
} while (dirty && feasible);
- if (!feasible)
- graphSimplified[orientation] = false;
+ // Note that if we are not feasible, we fallback and make sure that the graph is fully restored
+ if (!feasible) {
+ graphSimplified[orientation] = true;
+ restoreSimplifiedGraph(orientation);
+ restoreVertices(orientation);
+ return false;
+ }
+
+ graphSimplified[orientation] = true;
+ return true;
+}
+
+static AnchorVertex *replaceVertex_helper(AnchorData *data, AnchorVertex *oldV, AnchorVertex *newV)
+{
+ AnchorVertex *other;
+ if (data->from == oldV) {
+ data->from = newV;
+ other = data->to;
+ } else {
+ data->to = newV;
+ other = data->from;
+ }
+ return other;
+}
+
+bool QGraphicsAnchorLayoutPrivate::replaceVertex(Orientation orientation, AnchorVertex *oldV,
+ AnchorVertex *newV, const QList<AnchorData *> &edges)
+{
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+ bool feasible = true;
+
+ for (int i = 0; i < edges.count(); ++i) {
+ AnchorData *ad = edges[i];
+ AnchorVertex *otherV = replaceVertex_helper(ad, oldV, newV);
+
+#if defined(QT_DEBUG)
+ ad->name = QString::fromAscii("%1 --to--> %2").arg(ad->from->toString()).arg(ad->to->toString());
+#endif
+
+ bool newFeasible;
+ AnchorData *newAnchor = addAnchorMaybeParallel(ad, &newFeasible);
+ feasible &= newFeasible;
+
+ if (newAnchor != ad) {
+ // A parallel was created, we mark that in the list of anchors created by vertex
+ // simplification. This is needed because we want to restore them in a separate step
+ // from the restoration of anchor simplification.
+ anchorsFromSimplifiedVertices[orientation].append(newAnchor);
+ }
+
+ g.takeEdge(oldV, otherV);
+ }
return feasible;
}
/*!
\internal
+*/
+bool QGraphicsAnchorLayoutPrivate::simplifyVertices(Orientation orientation)
+{
+ Q_Q(QGraphicsAnchorLayout);
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+
+ // We'll walk through vertices
+ QStack<AnchorVertex *> stack;
+ stack.push(layoutFirstVertex[orientation]);
+ QSet<AnchorVertex *> visited;
+
+ while (!stack.isEmpty()) {
+ AnchorVertex *v = stack.pop();
+ visited.insert(v);
+
+ // Each adjacent of 'v' is a possible vertex to be merged. So we traverse all of
+ // them. Since once a merge is made, we might add new adjacents, and we don't want to
+ // pass two times through one adjacent. The 'index' is used to track our position.
+ QList<AnchorVertex *> adjacents = g.adjacentVertices(v);
+ int index = 0;
+
+ while (index < adjacents.count()) {
+ AnchorVertex *next = adjacents[index];
+ index++;
+
+ AnchorData *data = g.edgeData(v, next);
+ const bool bothLayoutVertices = v->m_item == q && next->m_item == q;
+ const bool zeroSized = !data->minSize && !data->maxSize;
+
+ if (!bothLayoutVertices && zeroSized) {
+
+ // Create a new vertex pair, note that we keep a list of those vertices so we can
+ // easily process them when restoring the graph.
+ AnchorVertexPair *newV = new AnchorVertexPair(v, next, data);
+ simplifiedVertices[orientation].append(newV);
+
+ // Collect the anchors of both vertices, the new vertex pair will take their place
+ // in those anchors
+ const QList<AnchorVertex *> &vAdjacents = g.adjacentVertices(v);
+ const QList<AnchorVertex *> &nextAdjacents = g.adjacentVertices(next);
+
+ for (int i = 0; i < vAdjacents.count(); ++i) {
+ AnchorVertex *adjacent = vAdjacents[i];
+ if (adjacent != next) {
+ AnchorData *ad = g.edgeData(v, adjacent);
+ newV->m_firstAnchors.append(ad);
+ }
+ }
+
+ for (int i = 0; i < nextAdjacents.count(); ++i) {
+ AnchorVertex *adjacent = nextAdjacents[i];
+ if (adjacent != v) {
+ AnchorData *ad = g.edgeData(next, adjacent);
+ newV->m_secondAnchors.append(ad);
+
+ // We'll also add new vertices to the adjacent list of the new 'v', to be
+ // created as a vertex pair and replace the current one.
+ if (!adjacents.contains(adjacent))
+ adjacents.append(adjacent);
+ }
+ }
+
+ // ### merge this loop into the ones that calculated m_firstAnchors/m_secondAnchors?
+ // Make newV take the place of v and next
+ bool feasible = replaceVertex(orientation, v, newV, newV->m_firstAnchors);
+ feasible &= replaceVertex(orientation, next, newV, newV->m_secondAnchors);
+
+ // Update the layout vertex information if one of the vertices is a layout vertex.
+ AnchorVertex *layoutVertex = 0;
+ if (v->m_item == q)
+ layoutVertex = v;
+ else if (next->m_item == q)
+ layoutVertex = next;
+
+ if (layoutVertex) {
+ // Layout vertices always have m_item == q...
+ newV->m_item = q;
+ changeLayoutVertex(orientation, layoutVertex, newV);
+ }
+
+ g.takeEdge(v, next);
+
+ // If a non-feasibility is found, we leave early and cancel the simplification
+ if (!feasible)
+ return false;
+
+ v = newV;
+ visited.insert(newV);
+
+ } else if (!visited.contains(next) && !stack.contains(next)) {
+ // If the adjacent is not fit for merge and it wasn't visited by the outermost
+ // loop, we add it to the stack.
+ stack.push(next);
+ }
+ }
+ }
+
+ return true;
+}
+
+/*!
+ \internal
One iteration of the simplification algorithm. Returns true if another iteration is needed.
@@ -763,7 +920,8 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP
// (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).
+ // (d) its next adjacent is already visited (a cycle in the graph);
+ // (e) the next anchor is a center anchor.
const QList<AnchorVertex *> &adjacents = g.adjacentVertices(v);
const bool isLayoutVertex = v->m_item == q;
@@ -786,13 +944,14 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP
candidatesForward = (beforeSequence == data->from);
}
- // This is a tricky part. We peek at the next vertex to find out
+ // This is a tricky part. We peek at the next vertex to find out whether
//
- // - whether the edge from this vertex to the next vertex has the same direction;
- // - whether we already visited the next vertex.
+ // - the edge from this vertex to the next vertex has the same direction;
+ // - we already visited the next vertex;
+ // - the next anchor is a center.
//
- // 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.
+ // Those are needed to identify the remaining end of sequence cases. 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;
@@ -810,8 +969,8 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP
const bool willChangeDirection = (candidatesForward != (v == data->from));
const bool cycleFound = visited.contains(after);
- // Now cases (c) and (d)...
- endOfSequence = willChangeDirection || cycleFound;
+ // Now cases (c), (d) and (e)...
+ endOfSequence = willChangeDirection || cycleFound || data->isCenterAnchor;
if (endOfSequence) {
if (!willChangeDirection) {
@@ -879,13 +1038,6 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP
// Add the sequence to the graph.
//
- // ### At this point we assume that if some parallel anchor will be created because
- // of the new sequence, the other anchor will not be a center anchor (since we
- // not deal with that case yet). This assumption will break once we start simplifying
- // vertices.
- AnchorData *possibleParallel = g.edgeData(beforeSequence, afterSequence);
- Q_ASSERT(!possibleParallel || !possibleParallel->isCenterAnchor);
-
AnchorData *sequence = createSequence(&g, beforeSequence, candidates, afterSequence);
// If 'beforeSequence' and 'afterSequence' already had an anchor between them, we'll
@@ -938,6 +1090,13 @@ void QGraphicsAnchorLayoutPrivate::restoreSimplifiedAnchor(AnchorData *edge)
delete sequence;
} else if (edge->type == AnchorData::Parallel) {
+
+ // Skip parallel anchors that were created by vertex simplification, they will be processed
+ // later, when restoring vertex simplification.
+ // ### we could improve this check bit having a bit inside 'edge'
+ if (anchorsFromSimplifiedVertices[edge->orientation].contains(edge))
+ return;
+
ParallelAnchorData* parallel = static_cast<ParallelAnchorData*>(edge);
restoreSimplifiedConstraints(parallel);
@@ -984,18 +1143,93 @@ void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientatio
orientation == Horizontal ? "Horizontal" : "Vertical");
#endif
+ // Restore anchor simplification
Graph<AnchorVertex, AnchorData> &g = graph[orientation];
-
QList<QPair<AnchorVertex*, AnchorVertex*> > connections = g.connections();
for (int i = 0; i < connections.count(); ++i) {
AnchorVertex *v1 = connections.at(i).first;
AnchorVertex *v2 = connections.at(i).second;
AnchorData *edge = g.edgeData(v1, v2);
- if (edge->type != AnchorData::Normal) {
+
+ // We restore only sequential anchors and parallels that were not created by
+ // vertex simplification.
+ if (edge->type == AnchorData::Sequential
+ || (edge->type == AnchorData::Parallel &&
+ !anchorsFromSimplifiedVertices[orientation].contains(edge))) {
+
g.takeEdge(v1, v2);
restoreSimplifiedAnchor(edge);
}
}
+
+ restoreVertices(orientation);
+}
+
+void QGraphicsAnchorLayoutPrivate::restoreVertices(Orientation orientation)
+{
+ Q_Q(QGraphicsAnchorLayout);
+
+ Graph<AnchorVertex, AnchorData> &g = graph[orientation];
+ QList<AnchorVertexPair *> &toRestore = simplifiedVertices[orientation];
+
+ // We will restore the vertices in the inverse order of creation, this way we ensure that
+ // the vertex being restored was not wrapped by another simplification.
+ for (int i = toRestore.count() - 1; i >= 0; --i) {
+ AnchorVertexPair *pair = toRestore[i];
+ QList<AnchorVertex *> adjacents = g.adjacentVertices(pair);
+
+ // Restore the removed edge, this will also restore both vertices 'first' and 'second' to
+ // the graph structure.
+ AnchorVertex *first = pair->m_first;
+ AnchorVertex *second = pair->m_second;
+ g.createEdge(first, second, pair->m_removedAnchor);
+
+ // Restore the anchors for the first child vertex
+ for (int j = 0; j < pair->m_firstAnchors.count(); ++j) {
+ AnchorData *ad = pair->m_firstAnchors[j];
+ Q_ASSERT(ad->from == pair || ad->to == pair);
+
+ replaceVertex_helper(ad, pair, first);
+ g.createEdge(ad->from, ad->to, ad);
+ }
+
+ // Restore the anchors for the second child vertex
+ for (int j = 0; j < pair->m_secondAnchors.count(); ++j) {
+ AnchorData *ad = pair->m_secondAnchors[j];
+ Q_ASSERT(ad->from == pair || ad->to == pair);
+
+ replaceVertex_helper(ad, pair, second);
+ g.createEdge(ad->from, ad->to, ad);
+ }
+
+ for (int j = 0; j < adjacents.count(); ++j) {
+ g.takeEdge(pair, adjacents[j]);
+ }
+
+ // The pair simplified a layout vertex, so place back the correct vertex in the variable
+ // that track layout vertices
+ if (pair->m_item == q) {
+ AnchorVertex *layoutVertex = first->m_item == q ? first : second;
+ Q_ASSERT(layoutVertex->m_item == q);
+ changeLayoutVertex(orientation, pair, layoutVertex);
+ }
+
+ delete pair;
+ }
+ toRestore.clear();
+
+ // The restoration process for vertex simplification also restored the effect of the
+ // parallel anchors created during vertex simplification, so we just need to restore
+ // the constraints in case of parallels that contain center anchors. For the same
+ // reason as above, order matters here.
+ QList<AnchorData *> &parallelAnchors = anchorsFromSimplifiedVertices[orientation];
+
+ for (int i = parallelAnchors.count() - 1; i >= 0; --i) {
+ ParallelAnchorData *parallel = static_cast<ParallelAnchorData *>(parallelAnchors[i]);
+ restoreSimplifiedConstraints(parallel);
+ delete parallel;
+ }
+ parallelAnchors.clear();
}
QGraphicsAnchorLayoutPrivate::Orientation
@@ -1149,7 +1383,6 @@ void QGraphicsAnchorLayoutPrivate::createCenterAnchors(
if (item == q) {
layoutCentralVertex[orientation] = internalVertex(q, centerEdge);
}
-
}
void QGraphicsAnchorLayoutPrivate::removeCenterAnchors(
@@ -1811,6 +2044,15 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs(
lastCalculationUsedSimplex[orientation] = false;
#endif
+ // ### This is necessary because now we do vertex simplification, we still don't know
+ // differentiate between invalidate()s that doesn't need resimplification and those which
+ // need. For example, when size hint of an item changes, this may cause an anchor to reach 0 or to
+ // leave 0 and get a size. In both cases we need resimplify.
+ //
+ // ### one possible solution would be tracking all the 0-sized anchors, if this set change, we need
+ // resimplify.
+ restoreSimplifiedGraph(orientation);
+
// Reset the nominal sizes of each anchor based on the current item sizes. This function
// works with both simplified and non-simplified graphs, so it'll work when the
// simplification is going to be reused.
@@ -2379,6 +2621,21 @@ void QGraphicsAnchorLayoutPrivate::setItemsGeometries(const QRectF &geom)
}
/*!
+ \internal
+
+ Fill the distance in the vertex and in the sub-vertices if its a combined vertex.
+*/
+static void setVertexDistance(AnchorVertex *v, qreal distance)
+{
+ v->distance = distance;
+ if (v->m_type == AnchorVertex::Pair) {
+ AnchorVertexPair *pair = static_cast<AnchorVertexPair *>(v);
+ setVertexDistance(pair->m_first, distance);
+ setVertexDistance(pair->m_second, distance);
+ }
+}
+
+/*!
\internal
Calculate the position of each vertex based on the paths to each of
@@ -2393,7 +2650,7 @@ void QGraphicsAnchorLayoutPrivate::calculateVertexPositions(
// Get root vertex
AnchorVertex *root = layoutFirstVertex[orientation];
- root->distance = 0;
+ setVertexDistance(root, 0);
visited.insert(root);
// Add initial edges to the queue
@@ -2483,10 +2740,12 @@ void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base,
Q_ASSERT(edge->from == base || edge->to == base);
- if (edge->from == base)
- edge->to->distance = base->distance + edgeDistance;
- else
- edge->from->distance = base->distance - edgeDistance;
+ // Calculate the distance for the vertex opposite to the base
+ if (edge->from == base) {
+ setVertexDistance(edge->to, base->distance + edgeDistance);
+ } else {
+ setVertexDistance(edge->from, base->distance - edgeDistance);
+ }
// Process child anchors
if (edge->type == AnchorData::Sequential)
diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h
index d7b3cfd..3a22db0 100644
--- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h
+++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h
@@ -78,66 +78,30 @@ QT_BEGIN_NAMESPACE
Represents a vertex (anchorage point) in the internal graph
*/
struct AnchorVertex {
+ enum Type {
+ Normal = 0,
+ Pair
+ };
+
AnchorVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge)
- : m_item(item), m_edge(edge) {}
+ : m_item(item), m_edge(edge), m_type(Normal) {}
AnchorVertex()
- : m_item(0), m_edge(Qt::AnchorPoint(0)) {}
+ : m_item(0), m_edge(Qt::AnchorPoint(0)), m_type(Normal) {}
#ifdef QT_DEBUG
inline QString toString() const;
#endif
+
QGraphicsLayoutItem *m_item;
Qt::AnchorPoint m_edge;
+ uint m_type : 1;
// Current distance from this vertex to the layout edge (Left or Top)
// Value is calculated from the current anchors sizes.
qreal distance;
};
-#ifdef QT_DEBUG
-inline QString AnchorVertex::toString() const
-{
- if (!this || !m_item) {
- return QLatin1String("NULL");
- }
- QString edge;
- switch (m_edge) {
- case Qt::AnchorLeft:
- edge = QLatin1String("Left");
- break;
- case Qt::AnchorHorizontalCenter:
- edge = QLatin1String("HorizontalCenter");
- break;
- case Qt::AnchorRight:
- edge = QLatin1String("Right");
- break;
- case Qt::AnchorTop:
- edge = QLatin1String("Top");
- break;
- case Qt::AnchorVerticalCenter:
- edge = QLatin1String("VerticalCenter");
- break;
- case Qt::AnchorBottom:
- edge = QLatin1String("Bottom");
- break;
- default:
- edge = QLatin1String("None");
- break;
- }
- QString itemName;
- if (m_item->isLayout()) {
- itemName = QLatin1String("layout");
- } else {
- if (QGraphicsItem *item = m_item->graphicsItem()) {
- itemName = item->data(0).toString();
- }
- }
- edge.insert(0, QLatin1String("%1_"));
- return edge.arg(itemName);
-}
-#endif
-
/*!
\internal
@@ -280,6 +244,68 @@ struct ParallelAnchorData : public AnchorData
QList<QSimplexConstraint *> m_secondConstraints;
};
+struct AnchorVertexPair : public AnchorVertex {
+ AnchorVertexPair(AnchorVertex *v1, AnchorVertex *v2, AnchorData *data)
+ : AnchorVertex(), m_first(v1), m_second(v2), m_removedAnchor(data) {
+ m_type = AnchorVertex::Pair;
+ }
+
+ AnchorVertex *m_first;
+ AnchorVertex *m_second;
+
+ AnchorData *m_removedAnchor;
+ QList<AnchorData *> m_firstAnchors;
+ QList<AnchorData *> m_secondAnchors;
+};
+
+#ifdef QT_DEBUG
+inline QString AnchorVertex::toString() const
+{
+ if (!this) {
+ return QLatin1String("NULL");
+ } else if (m_type == Pair) {
+ const AnchorVertexPair *vp = static_cast<const AnchorVertexPair *>(this);
+ return QString::fromAscii("(%1, %2)").arg(vp->m_first->toString()).arg(vp->m_second->toString());
+ } else if (!m_item) {
+ return QString::fromAscii("NULL_%1").arg(int(this));
+ }
+ QString edge;
+ switch (m_edge) {
+ case Qt::AnchorLeft:
+ edge = QLatin1String("Left");
+ break;
+ case Qt::AnchorHorizontalCenter:
+ edge = QLatin1String("HorizontalCenter");
+ break;
+ case Qt::AnchorRight:
+ edge = QLatin1String("Right");
+ break;
+ case Qt::AnchorTop:
+ edge = QLatin1String("Top");
+ break;
+ case Qt::AnchorVerticalCenter:
+ edge = QLatin1String("VerticalCenter");
+ break;
+ case Qt::AnchorBottom:
+ edge = QLatin1String("Bottom");
+ break;
+ default:
+ edge = QLatin1String("None");
+ break;
+ }
+ QString itemName;
+ if (m_item->isLayout()) {
+ itemName = QLatin1String("layout");
+ } else {
+ if (QGraphicsItem *item = m_item->graphicsItem()) {
+ itemName = item->data(0).toString();
+ }
+ }
+ edge.insert(0, QLatin1String("%1_"));
+ return edge.arg(itemName);
+}
+#endif
+
/*!
\internal
@@ -444,11 +470,17 @@ public:
// Simplification
bool simplifyGraph(Orientation orientation);
+ bool simplifyVertices(Orientation orientation);
bool simplifyGraphIteration(Orientation orientation, bool *feasible);
+ bool replaceVertex(Orientation orientation, AnchorVertex *oldV,
+ AnchorVertex *newV, const QList<AnchorData *> &edges);
+
+
void restoreSimplifiedGraph(Orientation orientation);
void restoreSimplifiedAnchor(AnchorData *edge);
void restoreSimplifiedConstraints(ParallelAnchorData *parallel);
+ void restoreVertices(Orientation orientation);
bool calculateTrunk(Orientation orientation, const GraphPath &trunkPath,
const QList<QSimplexConstraint *> &constraints,
@@ -476,6 +508,17 @@ public:
return internalVertex(qMakePair(const_cast<QGraphicsLayoutItem *>(item), edge));
}
+ inline void changeLayoutVertex(Orientation orientation, AnchorVertex *oldV, AnchorVertex *newV)
+ {
+ if (layoutFirstVertex[orientation] == oldV)
+ layoutFirstVertex[orientation] = newV;
+ else if (layoutCentralVertex[orientation] == oldV)
+ layoutCentralVertex[orientation] = newV;
+ else if (layoutLastVertex[orientation] == oldV)
+ layoutLastVertex[orientation] = newV;
+ }
+
+
AnchorVertex *addInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge);
void removeInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge);
@@ -519,6 +562,10 @@ public:
AnchorVertex *layoutCentralVertex[2];
AnchorVertex *layoutLastVertex[2];
+ // Combined anchors in order of creation
+ QList<AnchorVertexPair *> simplifiedVertices[2];
+ QList<AnchorData *> anchorsFromSimplifiedVertices[2];
+
// Graph paths and constraints, for both orientations
QMultiHash<AnchorVertex *, GraphPath> graphPaths[2];
QList<QSimplexConstraint *> constraints[2];