diff options
Diffstat (limited to 'src/gui/graphicsview')
-rw-r--r-- | src/gui/graphicsview/qgraph_p.h | 16 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsanchorlayout.cpp | 21 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 375 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsanchorlayout_p.h | 26 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.cpp | 98 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem.h | 8 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsitem_p.h | 13 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicslinearlayout.cpp | 3 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.cpp | 78 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene.h | 1 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsscene_p.h | 11 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicsview.cpp | 26 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicswidget.cpp | 21 | ||||
-rw-r--r-- | src/gui/graphicsview/qgraphicswidget.h | 3 | ||||
-rw-r--r-- | src/gui/graphicsview/qsimplex_p.cpp | 2 | ||||
-rw-r--r-- | src/gui/graphicsview/qsimplex_p.h | 2 |
16 files changed, 414 insertions, 290 deletions
diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h index f1fa185..0a2bf27 100644 --- a/src/gui/graphicsview/qgraph_p.h +++ b/src/gui/graphicsview/qgraph_p.h @@ -201,11 +201,6 @@ public: return l; } - void setRootVertex(Vertex *vertex) - { - userVertex = vertex; - } - QSet<Vertex*> vertices() const { QSet<Vertex *> setOfVertices; for (const_iterator it = constBegin(); it != constEnd(); ++it) { @@ -241,7 +236,7 @@ public: EdgeData *data = edgeData(v, v1); bool forward = data->from == v; if (forward) { - edges += QString::fromAscii("%1->%2 [label=\"[%3,%4,%5]\" dir=both color=\"#000000:#a0a0a0\"] \n") + edges += QString::fromAscii("\"%1\"->\"%2\" [label=\"[%3,%4,%5]\" dir=both color=\"#000000:#a0a0a0\"] \n") .arg(v->toString()) .arg(v1->toString()) .arg(data->minSize) @@ -250,17 +245,12 @@ public: ; } } - strVertices += QString::fromAscii("%1 [label=\"%2\"]\n").arg(v->toString()).arg(v->toString()); + strVertices += QString::fromAscii("\"%1\" [label=\"%2\"]\n").arg(v->toString()).arg(v->toString()); } return QString::fromAscii("%1\n%2\n").arg(strVertices).arg(edges); } #endif - Vertex *rootVertex() const - { - return userVertex; - } - protected: void createDirectedEdge(Vertex *from, Vertex *to, EdgeData *data) { @@ -286,8 +276,6 @@ protected: } private: - Vertex *userVertex; - QHash<Vertex *, QHash<Vertex *, EdgeData *> *> m_graph; }; diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 56d70e1..872ec3c 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -83,10 +83,8 @@ \clearfloat \section1 Size Hints and Size Policies in an Anchor Layout - QGraphicsAnchorLayout respects each item's size hints and size policies. However it does - not currently respect their stretch factors. This might change in the future, so avoid - using stretch factors in anchor layouts if you want to avoid any future regressions in - behavior. + QGraphicsAnchorLayout respects each item's size hints and size policies. + Note that there are some properties of QSizePolicy that are \l{Known issues}{not respected}. \section1 Spacing within an Anchor Layout @@ -101,6 +99,21 @@ If the spacing is negative, the items will overlap to some extent. + + \section1 Known issues + There are some features that QGraphicsAnchorLayout currently does not support. + This might change in the future, so avoid using these features if you want to + avoid any future regressions in behaviour: + \list + + \o Stretch factors are not respected. + + \o QSizePolicy::ExpandFlag is not respected. + + \o Height for width is not respected. + + \endlist + \sa QGraphicsLinearLayout, QGraphicsGridLayout, QGraphicsLayout */ diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 41aa8aa..96f8930 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -105,7 +105,7 @@ qreal QGraphicsAnchorPrivate::spacing() const static void internalSizeHints(QSizePolicy::Policy policy, qreal minSizeHint, qreal prefSizeHint, qreal maxSizeHint, qreal *minSize, qreal *prefSize, - qreal *expSize, qreal *maxSize) + qreal *maxSize) { // minSize, prefSize and maxSize are initialized // with item's preferred Size: this is QSizePolicy::Fixed. @@ -135,11 +135,6 @@ static void internalSizeHints(QSizePolicy::Policy policy, *prefSize = *minSize; else *prefSize = prefSizeHint; - - if (policy & QSizePolicy::ExpandFlag) - *expSize = *maxSize; - else - *expSize = *prefSize; } bool AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) @@ -154,7 +149,6 @@ bool AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) if (isLayoutAnchor) { minSize = 0; prefSize = 0; - expSize = 0; maxSize = QWIDGETSIZE_MAX; if (isCenterAnchor) maxSize /= 2; @@ -205,8 +199,8 @@ bool AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) } maxSizeHint = QWIDGETSIZE_MAX; } - internalSizeHints(policy, minSizeHint, prefSizeHint, maxSizeHint, - &minSize, &prefSize, &expSize, &maxSize); + internalSizeHints(policy, minSizeHint, prefSizeHint, maxSizeHint, + &minSize, &prefSize, &maxSize); // Set the anchor effective sizes to preferred. // @@ -217,7 +211,6 @@ bool AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo) // recalculate and override the values we set here. sizeAtMinimum = prefSize; sizeAtPreferred = prefSize; - sizeAtExpanding = prefSize; sizeAtMaximum = prefSize; return true; @@ -227,7 +220,6 @@ void ParallelAnchorData::updateChildrenSizes() { firstEdge->sizeAtMinimum = secondEdge->sizeAtMinimum = sizeAtMinimum; firstEdge->sizeAtPreferred = secondEdge->sizeAtPreferred = sizeAtPreferred; - firstEdge->sizeAtExpanding = secondEdge->sizeAtExpanding = sizeAtExpanding; firstEdge->sizeAtMaximum = secondEdge->sizeAtMaximum = sizeAtMaximum; firstEdge->updateChildrenSizes(); @@ -257,16 +249,12 @@ bool ParallelAnchorData::refreshSizeHints_helper(const QLayoutStyleInfo *styleIn return false; } - expSize = qMax(firstEdge->expSize, secondEdge->expSize); - expSize = qMin(expSize, maxSize); - prefSize = qMax(firstEdge->prefSize, secondEdge->prefSize); - prefSize = qMin(prefSize, expSize); + prefSize = qMin(prefSize, maxSize); // See comment in AnchorData::refreshSizeHints() about sizeAt* values sizeAtMinimum = prefSize; sizeAtPreferred = prefSize; - sizeAtExpanding = prefSize; sizeAtMaximum = prefSize; return true; @@ -280,8 +268,7 @@ bool ParallelAnchorData::refreshSizeHints_helper(const QLayoutStyleInfo *styleIn 1 is at Maximum */ static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal value, qreal min, - qreal pref, qreal exp, - qreal max) + qreal pref, qreal max) { QGraphicsAnchorLayoutPrivate::Interval interval; qreal lower; @@ -291,13 +278,9 @@ static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal valu interval = QGraphicsAnchorLayoutPrivate::MinToPreferred; lower = min; upper = pref; - } else if (value < exp) { - interval = QGraphicsAnchorLayoutPrivate::PreferredToExpanding; - lower = pref; - upper = exp; } else { - interval = QGraphicsAnchorLayoutPrivate::ExpandingToMax; - lower = exp; + interval = QGraphicsAnchorLayoutPrivate::PreferredToMax; + lower = pref; upper = max; } @@ -313,7 +296,7 @@ static QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> getFactor(qreal valu static qreal interpolate(const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> &factor, qreal min, qreal pref, - qreal exp, qreal max) + qreal max) { qreal lower; qreal upper; @@ -323,12 +306,8 @@ static qreal interpolate(const QPair<QGraphicsAnchorLayoutPrivate::Interval, qre lower = min; upper = pref; break; - case QGraphicsAnchorLayoutPrivate::PreferredToExpanding: + case QGraphicsAnchorLayoutPrivate::PreferredToMax: lower = pref; - upper = exp; - break; - case QGraphicsAnchorLayoutPrivate::ExpandingToMax: - lower = exp; upper = max; break; } @@ -341,34 +320,31 @@ void SequentialAnchorData::updateChildrenSizes() // ### REMOVE ME // ### check whether we are guarantee to get those or we need to warn stuff at this // point. - Q_ASSERT(sizeAtMinimum > minSize || qFuzzyCompare(sizeAtMinimum, minSize)); - Q_ASSERT(sizeAtMinimum < maxSize || qFuzzyCompare(sizeAtMinimum, maxSize)); - Q_ASSERT(sizeAtPreferred > minSize || qFuzzyCompare(sizeAtPreferred, minSize)); - Q_ASSERT(sizeAtPreferred < maxSize || qFuzzyCompare(sizeAtPreferred, maxSize)); - Q_ASSERT(sizeAtExpanding > minSize || qFuzzyCompare(sizeAtExpanding, minSize)); - Q_ASSERT(sizeAtExpanding < maxSize || qFuzzyCompare(sizeAtExpanding, maxSize)); - Q_ASSERT(sizeAtMaximum > minSize || qFuzzyCompare(sizeAtMaximum, minSize)); - Q_ASSERT(sizeAtMaximum < maxSize || qFuzzyCompare(sizeAtMaximum, maxSize)); + Q_ASSERT(sizeAtMinimum > minSize || qAbs(sizeAtMinimum - minSize) < 0.00000001); + Q_ASSERT(sizeAtPreferred > minSize || qAbs(sizeAtPreferred - minSize) < 0.00000001); + Q_ASSERT(sizeAtMaximum > minSize || qAbs(sizeAtMaximum - minSize) < 0.00000001); + + // These may be false if this anchor was in parallel with the layout stucture + // Q_ASSERT(sizeAtMinimum < maxSize || qAbs(sizeAtMinimum - maxSize) < 0.00000001); + // Q_ASSERT(sizeAtPreferred < maxSize || qAbs(sizeAtPreferred - maxSize) < 0.00000001); + // Q_ASSERT(sizeAtMaximum < maxSize || qAbs(sizeAtMaximum - maxSize) < 0.00000001); // 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 QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> minFactor = - getFactor(sizeAtMinimum, minSize, prefSize, expSize, maxSize); + getFactor(sizeAtMinimum, minSize, prefSize, 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); + getFactor(sizeAtPreferred, minSize, prefSize, maxSize); const QPair<QGraphicsAnchorLayoutPrivate::Interval, qreal> maxFactor = - getFactor(sizeAtMaximum, minSize, prefSize, expSize, maxSize); + getFactor(sizeAtMaximum, minSize, prefSize, maxSize); for (int i = 0; i < m_edges.count(); ++i) { AnchorData *e = m_edges.at(i); - 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->sizeAtMinimum = interpolate(minFactor, e->minSize, e->prefSize, e->maxSize); + e->sizeAtPreferred = interpolate(prefFactor, e->minSize, e->prefSize, e->maxSize); + e->sizeAtMaximum = interpolate(maxFactor, e->minSize, e->prefSize, e->maxSize); e->updateChildrenSizes(); } @@ -384,7 +360,6 @@ bool SequentialAnchorData::refreshSizeHints_helper(const QLayoutStyleInfo *style { minSize = 0; prefSize = 0; - expSize = 0; maxSize = 0; for (int i = 0; i < m_edges.count(); ++i) { @@ -396,14 +371,12 @@ bool SequentialAnchorData::refreshSizeHints_helper(const QLayoutStyleInfo *style minSize += edge->minSize; prefSize += edge->prefSize; - expSize += edge->expSize; maxSize += edge->maxSize; } // See comment in AnchorData::refreshSizeHints() about sizeAt* values sizeAtMinimum = prefSize; sizeAtPreferred = prefSize; - sizeAtExpanding = prefSize; sizeAtMaximum = prefSize; return true; @@ -478,12 +451,15 @@ QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate() for (int j = 0; j < 3; ++j) { sizeHints[i][j] = -1; } - sizeAtExpanding[i] = -1; interpolationProgress[i] = -1; spacings[i] = -1; graphSimplified[i] = false; graphHasConflicts[i] = false; + + layoutFirstVertex[i] = 0; + layoutCentralVertex[i] = 0; + layoutLastVertex[i] = 0; } } @@ -529,13 +505,14 @@ inline static qreal checkAdd(qreal a, qreal b) Adds \a newAnchor to the graph \a g. Returns the newAnchor itself if it could be added without further changes to the graph. If a - new parallel anchor had to be created, then returns the new parallel anchor. In case the - addition is unfeasible -- because a parallel setup is not possible, returns 0. + new parallel anchor had to be created, then returns the new parallel anchor. If a parallel anchor + had to be created and it results in an unfeasible setup, \a feasible is set to false, otherwise + true. */ static AnchorData *addAnchorMaybeParallel(Graph<AnchorVertex, AnchorData> *g, - AnchorData *newAnchor) + AnchorData *newAnchor, bool *feasible) { - bool feasible = true; + *feasible = true; // If already exists one anchor where newAnchor is supposed to be, we create a parallel // anchor. @@ -544,12 +521,12 @@ static AnchorData *addAnchorMaybeParallel(Graph<AnchorVertex, AnchorData> *g, // At this point we can identify that the parallel anchor is not feasible, e.g. one // anchor minimum size is bigger than the other anchor maximum size. - feasible = parallel->refreshSizeHints_helper(0, false); + *feasible = parallel->refreshSizeHints_helper(0, false); newAnchor = parallel; } g->createEdge(newAnchor->from, newAnchor->to, newAnchor); - return feasible ? newAnchor : 0; + return newAnchor; } @@ -663,7 +640,7 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) orientation == Horizontal ? "Horizontal" : "Vertical"); #endif - if (!graph[orientation].rootVertex()) + if (!layoutFirstVertex[orientation]) return true; bool dirty; @@ -700,7 +677,7 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP QSet<AnchorVertex *> visited; QStack<QPair<AnchorVertex *, AnchorVertex *> > stack; - stack.push(qMakePair(static_cast<AnchorVertex *>(0), g.rootVertex())); + stack.push(qMakePair(static_cast<AnchorVertex *>(0), layoutFirstVertex[orientation])); QVector<AnchorVertex*> candidates; bool candidatesForward; @@ -835,13 +812,21 @@ 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 // create a parallel anchor between the new sequence and the old anchor. - AnchorData *newAnchor = addAnchorMaybeParallel(&g, sequence); + bool newFeasible; + AnchorData *newAnchor = addAnchorMaybeParallel(&g, sequence, &newFeasible); - if (!newAnchor) { + if (!newFeasible) { *feasible = false; return false; } @@ -959,9 +944,10 @@ void QGraphicsAnchorLayoutPrivate::createLayoutEdges() data->maxSize = QWIDGETSIZE_MAX; data->skipInPreferred = 1; - // Set the Layout Left edge as the root of the horizontal graph. - AnchorVertex *v = internalVertex(layout, Qt::AnchorLeft); - graph[Horizontal].setRootVertex(v); + // Save a reference to layout vertices + layoutFirstVertex[Horizontal] = internalVertex(layout, Qt::AnchorLeft); + layoutCentralVertex[Horizontal] = 0; + layoutLastVertex[Horizontal] = internalVertex(layout, Qt::AnchorRight); // Vertical data = new AnchorData; @@ -970,17 +956,18 @@ void QGraphicsAnchorLayoutPrivate::createLayoutEdges() data->maxSize = QWIDGETSIZE_MAX; data->skipInPreferred = 1; - // Set the Layout Top edge as the root of the vertical graph. - v = internalVertex(layout, Qt::AnchorTop); - graph[Vertical].setRootVertex(v); + // Save a reference to layout vertices + layoutFirstVertex[Vertical] = internalVertex(layout, Qt::AnchorTop); + layoutCentralVertex[Vertical] = 0; + layoutLastVertex[Vertical] = internalVertex(layout, Qt::AnchorBottom); } void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges() { Q_Q(QGraphicsAnchorLayout); - Q_ASSERT(internalVertex(q, Qt::AnchorHorizontalCenter) == NULL); - Q_ASSERT(internalVertex(q, Qt::AnchorVerticalCenter) == NULL); + Q_ASSERT(!internalVertex(q, Qt::AnchorHorizontalCenter)); + Q_ASSERT(!internalVertex(q, Qt::AnchorVerticalCenter)); removeAnchor_helper(internalVertex(q, Qt::AnchorLeft), internalVertex(q, Qt::AnchorRight)); @@ -1019,6 +1006,8 @@ void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) void QGraphicsAnchorLayoutPrivate::createCenterAnchors( QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge) { + Q_Q(QGraphicsAnchorLayout); + Orientation orientation; switch (centerEdge) { case Qt::AnchorHorizontalCenter: @@ -1061,24 +1050,33 @@ void QGraphicsAnchorLayoutPrivate::createCenterAnchors( c->variables.insert(data, 1.0); addAnchor_helper(item, firstEdge, item, centerEdge, data); data->isCenterAnchor = true; + data->dependency = AnchorData::Master; data->refreshSizeHints(0); data = new AnchorData; c->variables.insert(data, -1.0); addAnchor_helper(item, centerEdge, item, lastEdge, data); data->isCenterAnchor = true; + data->dependency = AnchorData::Slave; data->refreshSizeHints(0); itemCenterConstraints[orientation].append(c); // Remove old one removeAnchor_helper(first, last); + + if (item == q) { + layoutCentralVertex[orientation] = internalVertex(q, centerEdge); + } + } void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge, bool substitute) { + Q_Q(QGraphicsAnchorLayout); + Orientation orientation; switch (centerEdge) { case Qt::AnchorHorizontalCenter: @@ -1151,6 +1149,10 @@ void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( // by this time, the center vertex is deleted and merged into a non-centered internal anchor removeAnchor_helper(first, internalVertex(item, lastEdge)); } + + if (item == q) { + layoutCentralVertex[orientation] = 0; + } } @@ -1724,8 +1726,6 @@ QList<AnchorData *> getVariables(QList<QSimplexConstraint *> constraints) void QGraphicsAnchorLayoutPrivate::calculateGraphs( QGraphicsAnchorLayoutPrivate::Orientation orientation) { - Q_Q(QGraphicsAnchorLayout); - #if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT) lastCalculationUsedSimplex[orientation] = false; #endif @@ -1773,7 +1773,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // For minimum and maximum, use the path between the two layout sides as the // objective function. - AnchorVertex *v = internalVertex(q, pickEdge(Qt::AnchorRight, orientation)); + AnchorVertex *v = layoutLastVertex[orientation]; GraphPath trunkPath = graphPaths[orientation].value(v); bool feasible = calculateTrunk(orientation, trunkPath, trunkConstraints, trunkVariables); @@ -1836,27 +1836,19 @@ bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const if (feasible) { solvePreferred(allConstraints, variables); - // 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, + // Calculate and set the preferred size for the layout, // from the edge sizes that were calculated above. qreal pref(0.0); - qreal expanding(0.0); foreach (const AnchorData *ad, path.positives) { pref += ad->sizeAtPreferred; - expanding += ad->sizeAtExpanding; } foreach (const AnchorData *ad, path.negatives) { pref -= ad->sizeAtPreferred; - expanding -= ad->sizeAtExpanding; } sizeHints[orientation][Qt::MinimumSize] = min; sizeHints[orientation][Qt::PreferredSize] = pref; sizeHints[orientation][Qt::MaximumSize] = max; - sizeAtExpanding[orientation] = expanding; } qDeleteAll(sizeHintConstraints); @@ -1870,13 +1862,11 @@ bool QGraphicsAnchorLayoutPrivate::calculateTrunk(Orientation orientation, const AnchorData *ad = path.positives.toList()[0]; ad->sizeAtMinimum = ad->minSize; ad->sizeAtPreferred = ad->prefSize; - ad->sizeAtExpanding = ad->expSize; ad->sizeAtMaximum = ad->maxSize; sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum; sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred; sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum; - sizeAtExpanding[orientation] = ad->sizeAtExpanding; } #if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT) @@ -1902,7 +1892,6 @@ bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList<QSimplexConstra AnchorData *ad = variables[j]; Q_ASSERT(ad); ad->sizeAtMinimum = ad->sizeAtPreferred; - ad->sizeAtExpanding = ad->sizeAtPreferred; ad->sizeAtMaximum = ad->sizeAtPreferred; } } @@ -1955,7 +1944,7 @@ void QGraphicsAnchorLayoutPrivate::findPaths(Orientation orientation) QSet<AnchorData *> visited; - AnchorVertex *root = graph[orientation].rootVertex(); + AnchorVertex *root = layoutFirstVertex[orientation]; graphPaths[orientation].insert(root, GraphPath()); @@ -2041,16 +2030,45 @@ void QGraphicsAnchorLayoutPrivate::updateAnchorSizes(Orientation orientation) QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHints( const QList<AnchorData *> &anchors) { + if (anchors.isEmpty()) + return QList<QSimplexConstraint *>(); + + // Look for the layout edge. That can be either the first half in case the + // layout is split in two, or the whole layout anchor. + Orientation orient = Orientation(anchors.first()->orientation); + AnchorData *layoutEdge = 0; + if (layoutCentralVertex[orient]) { + layoutEdge = graph[orient].edgeData(layoutFirstVertex[orient], layoutCentralVertex[orient]); + } else { + layoutEdge = graph[orient].edgeData(layoutFirstVertex[orient], layoutLastVertex[orient]); + + // If maxSize is less then "infinite", that means there are other anchors + // grouped together with this one. We can't ignore its maximum value so we + // set back the variable to NULL to prevent the continue condition from being + // satisfied in the loop below. + if (layoutEdge->maxSize < QWIDGETSIZE_MAX) + layoutEdge = 0; + } + + // For each variable, create constraints based on size hints QList<QSimplexConstraint *> anchorConstraints; + bool unboundedProblem = true; for (int i = 0; i < anchors.size(); ++i) { AnchorData *ad = anchors[i]; + // Anchors that have their size directly linked to another one don't need constraints + // For exammple, the second half of an item has exactly the same size as the first half + // thus constraining the latter is enough. + if (ad->dependency == AnchorData::Slave) + continue; + if ((ad->minSize == ad->maxSize) || qFuzzyCompare(ad->minSize, ad->maxSize)) { QSimplexConstraint *c = new QSimplexConstraint; c->variables.insert(ad, 1.0); c->constant = ad->minSize; c->ratio = QSimplexConstraint::Equal; anchorConstraints += c; + unboundedProblem = false; } else { QSimplexConstraint *c = new QSimplexConstraint; c->variables.insert(ad, 1.0); @@ -2058,14 +2076,30 @@ QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHin c->ratio = QSimplexConstraint::MoreOrEqual; anchorConstraints += c; + // We avoid adding restrictions to the layout internal anchors. That's + // to prevent unnecessary fair distribution from happening due to this + // artificial restriction. + if (ad == layoutEdge) + continue; + c = new QSimplexConstraint; c->variables.insert(ad, 1.0); c->constant = ad->maxSize; c->ratio = QSimplexConstraint::LessOrEqual; anchorConstraints += c; + unboundedProblem = false; } } + // If no upper boundary restriction was added, add one to avoid unbounded problem + if (unboundedProblem) { + QSimplexConstraint *c = new QSimplexConstraint; + c->variables.insert(layoutEdge, 1.0); + c->constant = QWIDGETSIZE_MAX; + c->ratio = QSimplexConstraint::LessOrEqual; + anchorConstraints += c; + } + return anchorConstraints; } @@ -2075,30 +2109,18 @@ QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHin QList< QList<QSimplexConstraint *> > QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) { - Q_Q(QGraphicsAnchorLayout); - - // Find layout vertices and edges for the current orientation. - AnchorVertex *layoutFirstVertex = \ - internalVertex(q, pickEdge(Qt::AnchorLeft, orientation)); - - AnchorVertex *layoutCentralVertex = \ - internalVertex(q, pickEdge(Qt::AnchorHorizontalCenter, orientation)); - - AnchorVertex *layoutLastVertex = \ - internalVertex(q, pickEdge(Qt::AnchorRight, orientation)); + Q_ASSERT(layoutFirstVertex[orientation] && layoutLastVertex[orientation]); - Q_ASSERT(layoutFirstVertex && layoutLastVertex); - - AnchorData *edgeL1 = NULL; - AnchorData *edgeL2 = NULL; + AnchorData *edgeL1 = 0; + AnchorData *edgeL2 = 0; // The layout may have a single anchor between Left and Right or two half anchors // passing through the center - if (layoutCentralVertex) { - edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutCentralVertex); - edgeL2 = graph[orientation].edgeData(layoutCentralVertex, layoutLastVertex); + if (layoutCentralVertex[orientation]) { + edgeL1 = graph[orientation].edgeData(layoutFirstVertex[orientation], layoutCentralVertex[orientation]); + edgeL2 = graph[orientation].edgeData(layoutCentralVertex[orientation], layoutLastVertex[orientation]); } else { - edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutLastVertex); + edgeL1 = graph[orientation].edgeData(layoutFirstVertex[orientation], layoutLastVertex[orientation]); } QLinkedList<QSimplexConstraint *> remainingConstraints; @@ -2288,7 +2310,7 @@ void QGraphicsAnchorLayoutPrivate::calculateVertexPositions( QSet<AnchorVertex *> visited; // Get root vertex - AnchorVertex *root = graph[orientation].rootVertex(); + AnchorVertex *root = layoutFirstVertex[orientation]; root->distance = 0; visited.insert(root); @@ -2343,7 +2365,6 @@ void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( result = getFactor(current, sizeHints[orientation][Qt::MinimumSize], sizeHints[orientation][Qt::PreferredSize], - sizeAtExpanding[orientation], sizeHints[orientation][Qt::MaximumSize]); interpolationInterval[orientation] = result.first; @@ -2358,7 +2379,6 @@ void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( - minimum size, - preferred size, - - size when all expanding anchors are expanded, - maximum size. These three key values are calculated in advance using linear @@ -2378,7 +2398,7 @@ void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, interpolationProgress[orientation]); qreal edgeDistance = interpolate(factor, edge->sizeAtMinimum, edge->sizeAtPreferred, - edge->sizeAtExpanding, edge->sizeAtMaximum); + edge->sizeAtMaximum); Q_ASSERT(edge->from == base || edge->to == base); @@ -2473,8 +2493,9 @@ bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *> QList<AnchorData *> variables = getVariables(constraints); for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = static_cast<AnchorData *>(variables[i]); - Q_ASSERT(ad->result >= ad->minSize || qFuzzyCompare(ad->result, ad->minSize)); ad->sizeAtMinimum = ad->result; + Q_ASSERT(ad->sizeAtMinimum >= ad->minSize || + qAbs(ad->sizeAtMinimum - ad->minSize) < 0.00000001); } // Calculate maximum values @@ -2483,8 +2504,9 @@ bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *> // Save sizeAtMaximum results for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = static_cast<AnchorData *>(variables[i]); - Q_ASSERT(ad->result <= ad->maxSize || qFuzzyCompare(ad->result, ad->maxSize)); ad->sizeAtMaximum = ad->result; + // Q_ASSERT(ad->sizeAtMaximum <= ad->maxSize || + // qAbs(ad->sizeAtMaximum - ad->maxSize) < 0.00000001); } } return feasible; @@ -2563,139 +2585,6 @@ bool QGraphicsAnchorLayoutPrivate::solvePreferred(const QList<QSimplexConstraint /*! \internal - Calculate the "expanding" keyframe - - This new keyframe sits between the already existing sizeAtPreferred and - sizeAtMaximum keyframes. Its goal is to modify the interpolation between - the latter as to respect the "expanding" size policy of some anchors. - - Previously all items would be subject to a linear interpolation between - sizeAtPreferred and sizeAtMaximum values. This will change now, the - expanding anchors will change their size before the others. To calculate - this keyframe we use the following logic: - - 1) Ask each anchor for their desired expanding size (ad->expSize), this - value depends on the anchor expanding property in the following way: - - - Expanding normal anchors want to grow towards their maximum size - - Non-expanding normal anchors want to remain at their preferred size. - - Sequential anchors wants to grow towards a size that is calculated by: - summarizing it's child anchors, where it will use preferred size for non-expanding anchors - and maximum size for expanding anchors. - - Parallel anchors want to grow towards the smallest maximum size of all the expanding anchors. - - 2) Clamp their desired values to the value they assume in the neighbour - keyframes (sizeAtPreferred and sizeAtExpanding) - - 3) Run simplex with a setup that ensures the following: - - a. Anchors will change their value from their sizeAtPreferred towards - their sizeAtMaximum as much as required to ensure that ALL anchors - reach their respective "desired" expanding sizes. - - b. No anchors will change their value beyond what is NEEDED to satisfy - the requirement above. - - The final result is that, at the "expanding" keyframe expanding anchors - will grow and take with them all anchors that are parallel to them. - However, non-expanding anchors will remain at their preferred size unless - they are forced to grow by a parallel expanding anchor. - - Note: For anchors where the sizeAtPreferred is bigger than sizeAtMaximum, - the visual effect when the layout grows from its preferred size is - the following: Expanding anchors will keep their size while non - expanding ones will shrink. Only after non-expanding anchors have - shrinked all the way, the expanding anchors will start to shrink too. -*/ -void QGraphicsAnchorLayoutPrivate::solveExpanding(const QList<QSimplexConstraint *> &constraints, - const QList<AnchorData *> &variables) -{ - QList<QSimplexConstraint *> itemConstraints; - QSimplexConstraint *objective = new QSimplexConstraint; - bool hasExpanding = false; - - // Construct the simplex constraints and objective - for (int i = 0; i < variables.size(); ++i) { - // For each anchor - AnchorData *ad = variables[i]; - - // Clamp the desired expanding size - qreal upperBoundary = qMax(ad->sizeAtPreferred, ad->sizeAtMaximum); - qreal lowerBoundary = qMin(ad->sizeAtPreferred, ad->sizeAtMaximum); - qreal boundedExpSize = qBound(lowerBoundary, ad->expSize, upperBoundary); - - // Expanding anchors are those that want to move from their preferred size - if (boundedExpSize != ad->sizeAtPreferred) - hasExpanding = true; - - // Lock anchor between boundedExpSize and sizeAtMaximum (ensure 3.a) - if (boundedExpSize == ad->sizeAtMaximum || qFuzzyCompare(boundedExpSize, ad->sizeAtMaximum)) { - // The interval has only one possible value, we can use an "Equal" - // constraint and don't need to add this variable to the objective. - QSimplexConstraint *itemC = new QSimplexConstraint; - itemC->ratio = QSimplexConstraint::Equal; - itemC->variables.insert(ad, 1.0); - itemC->constant = boundedExpSize; - itemConstraints << itemC; - } else { - // Add MoreOrEqual and LessOrEqual constraints. - QSimplexConstraint *itemC = new QSimplexConstraint; - itemC->ratio = QSimplexConstraint::MoreOrEqual; - itemC->variables.insert(ad, 1.0); - itemC->constant = qMin(boundedExpSize, ad->sizeAtMaximum); - itemConstraints << itemC; - - itemC = new QSimplexConstraint; - itemC->ratio = QSimplexConstraint::LessOrEqual; - itemC->variables.insert(ad, 1.0); - itemC->constant = qMax(boundedExpSize, ad->sizeAtMaximum); - itemConstraints << itemC; - - // Create objective to avoid the anchors from moving away from - // the preferred size more than the needed amount. (ensure 3.b) - // The objective function is the distance between sizeAtPreferred - // and sizeAtExpanding, it will be minimized. - if (ad->sizeAtExpanding < ad->sizeAtMaximum) { - // Try to shrink this variable towards its sizeAtPreferred value - objective->variables.insert(ad, 1.0); - } else { - // Try to grow this variable towards its sizeAtPreferred value - objective->variables.insert(ad, -1.0); - } - } - } - - // Solve - if (hasExpanding == false) { - // If no anchors are expanding, we don't need to run the simplex - // Set all variables to their preferred size - for (int i = 0; i < variables.size(); ++i) { - variables[i]->sizeAtExpanding = variables[i]->sizeAtPreferred; - } - } else { - // Run simplex - QSimplex simplex; - - // Satisfy expanding (3.a) - bool feasible = simplex.setConstraints(constraints + itemConstraints); - Q_ASSERT(feasible); - - // Reduce damage (3.b) - simplex.setObjective(objective); - simplex.solveMin(); - - // Collect results - for (int i = 0; i < variables.size(); ++i) { - variables[i]->sizeAtExpanding = variables[i]->result; - } - } - - delete objective; - qDeleteAll(itemConstraints); -} - -/*! - \internal Returns true if there are no arrangement that satisfies all constraints. Otherwise returns false. diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 7dd0d65..1e11ee2 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -150,14 +150,21 @@ struct AnchorData : public QSimplexVariable { Parallel }; + enum Dependency { + Independent = 0, + Master, + Slave + }; + AnchorData() : QSimplexVariable(), item(0), from(0), to(0), - minSize(0), prefSize(0), expSize(0), maxSize(0), + minSize(0), prefSize(0), maxSize(0), sizeAtMinimum(0), sizeAtPreferred(0), - sizeAtExpanding(0), sizeAtMaximum(0), + sizeAtMaximum(0), graphicsAnchor(0), skipInPreferred(0), type(Normal), hasSize(true), isLayoutAnchor(false), - isCenterAnchor(false), orientation(0) {} + isCenterAnchor(false), orientation(0), + dependency(Independent) {} virtual void updateChildrenSizes() {} virtual bool refreshSizeHints(const QLayoutStyleInfo *styleInfo); @@ -194,7 +201,6 @@ struct AnchorData : public QSimplexVariable { // size. qreal minSize; qreal prefSize; - qreal expSize; qreal maxSize; // These attributes define which sizes should that anchor be in when the @@ -202,7 +208,6 @@ struct AnchorData : public QSimplexVariable { // calculated by the Simplex solver based on the current layout setup. qreal sizeAtMinimum; qreal sizeAtPreferred; - qreal sizeAtExpanding; qreal sizeAtMaximum; QGraphicsAnchor *graphicsAnchor; @@ -212,6 +217,7 @@ struct AnchorData : public QSimplexVariable { uint isLayoutAnchor : 1; // if this anchor is an internal layout anchor uint isCenterAnchor : 1; uint orientation : 1; + uint dependency : 2; // either Independent, Master or Slave }; #ifdef QT_DEBUG @@ -337,8 +343,7 @@ public: // Interval represents which interpolation interval are we operating in. enum Interval { MinToPreferred = 0, - PreferredToExpanding, - ExpandingToMax + PreferredToMax }; // Several structures internal to the layout are duplicated to handle @@ -479,8 +484,6 @@ public: GraphPath path, qreal *min, qreal *max); 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 @@ -491,7 +494,6 @@ public: qreal spacings[NOrientations]; // Size hints from simplex engine qreal sizeHints[2][3]; - qreal sizeAtExpanding[2]; // Items QVector<QGraphicsLayoutItem *> items; @@ -504,6 +506,10 @@ public: // Internal graph of anchorage points and anchors, for both orientations Graph<AnchorVertex, AnchorData> graph[2]; + AnchorVertex *layoutFirstVertex[2]; + AnchorVertex *layoutCentralVertex[2]; + AnchorVertex *layoutLastVertex[2]; + // Graph paths and constraints, for both orientations QMultiHash<AnchorVertex *, GraphPath> graphPaths[2]; QList<QSimplexConstraint *> constraints[2]; diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index 2fd499d..a236b14 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -386,6 +386,12 @@ introduced in Qt 4.6. \omitvalue ItemIsFocusScope Internal only (for now). + + \value ItemSendsScenePositionChanges The item enables itemChange() + notifications for ItemScenePositionHasChanged. For performance reasons, + these notifications are disabled by default. You must enable this flag + to receive notifications for scene position changes. This flag was + introduced in Qt 4.6. */ /*! @@ -562,6 +568,14 @@ \value ItemOpacityHasChanged The item's opacity has changed. The value argument is the new opacity (i.e., a double). Do not call setOpacity() as this notification is delivered. The return value is ignored. + + \value ItemScenePositionHasChanged The item's scene position has changed. + This notification is sent if the ItemSendsScenePositionChanges flag is + enabled, and after the item's scene position has changed (i.e., the + position or transformation of the item itself or the position or + transformation of any ancestor has changed). The value argument is the + new scene position (the same as scenePos()), and QGraphicsItem ignores + the return value for this notification (i.e., a read-only notification). */ /*! @@ -990,6 +1004,10 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) if (scene) { // Deliver the change to the index scene->d_func()->index->itemChange(q, QGraphicsItem::ItemParentChange, newParentVariant); + + // Disable scene pos notifications for old ancestors + if (scenePosDescendants || (flags & QGraphicsItem::ItemSendsScenePositionChanges)) + scene->d_func()->setScenePosItemEnabled(q, false); } if (subFocusItem && parent) { @@ -1084,10 +1102,15 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent) parent->d_ptr->addChild(q); parent->itemChange(QGraphicsItem::ItemChildAddedChange, thisPointerVariant); - if (!implicitUpdate && scene) { - scene->d_func()->markDirty(q_ptr, QRect(), - /*invalidateChildren=*/false, - /*maybeDirtyClipPath=*/true); + if (scene) { + if (!implicitUpdate) + scene->d_func()->markDirty(q_ptr, QRect(), + /*invalidateChildren=*/false, + /*maybeDirtyClipPath=*/true); + + // Re-enable scene pos notifications for new ancestors + if (scenePosDescendants || (flags & QGraphicsItem::ItemSendsScenePositionChanges)) + scene->d_func()->setScenePosItemEnabled(q, true); } // Inherit ancestor flags from the new parent. @@ -1336,7 +1359,9 @@ QGraphicsItem::~QGraphicsItem() d_ptr->setParentItemHelper(0); } +#ifndef QT_NO_GRAPHICSEFFECT delete d_ptr->graphicsEffect; +#endif //QT_NO_GRAPHICSEFFECT if (d_ptr->transformData) { for(int i = 0; i < d_ptr->transformData->graphicsTransforms.size(); ++i) { QGraphicsTransform *t = d_ptr->transformData->graphicsTransforms.at(i); @@ -1746,6 +1771,12 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags) } if (d_ptr->scene) { + if ((flags & ItemSendsScenePositionChanges) != (oldFlags & ItemSendsScenePositionChanges)) { + if (flags & ItemSendsScenePositionChanges) + d_ptr->scene->d_func()->registerScenePosItem(this); + else + d_ptr->scene->d_func()->unregisterScenePosItem(this); + } d_ptr->scene->d_func()->markDirty(this, QRectF(), /*invalidateChildren=*/true, /*maybeDirtyClipPath*/true); @@ -2506,7 +2537,9 @@ void QGraphicsItem::setOpacity(qreal opacity) // Update. if (d_ptr->scene) { +#ifndef QT_NO_GRAPHICSEFFECT d_ptr->invalidateGraphicsEffectsRecursively(); +#endif //QT_NO_GRAPHICSEFFECT d_ptr->scene->d_func()->markDirty(this, QRectF(), /*invalidateChildren=*/true, /*maybeDirtyClipPath=*/false, @@ -2523,6 +2556,7 @@ void QGraphicsItem::setOpacity(qreal opacity) \since 4.6 */ +#ifndef QT_NO_GRAPHICSEFFECT QGraphicsEffect *QGraphicsItem::graphicsEffect() const { return d_ptr->graphicsEffect; @@ -2569,6 +2603,7 @@ void QGraphicsItem::setGraphicsEffect(QGraphicsEffect *effect) prepareGeometryChange(); } +#endif //QT_NO_GRAPHICSEFFECT /*! \internal @@ -2582,6 +2617,7 @@ void QGraphicsItem::setGraphicsEffect(QGraphicsEffect *effect) */ QRectF QGraphicsItemPrivate::effectiveBoundingRect() const { +#ifndef QT_NO_GRAPHICSEFFECT QGraphicsEffect *effect = graphicsEffect; QRectF brect = effect && effect->isEnabled() ? effect->boundingRect() : q_ptr->boundingRect(); if (ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) @@ -2598,6 +2634,10 @@ QRectF QGraphicsItemPrivate::effectiveBoundingRect() const } return brect; +#else //QT_NO_GRAPHICSEFFECT + return q_ptr->boundingRect(); +#endif //QT_NO_GRAPHICSEFFECT + } /*! @@ -2951,7 +2991,7 @@ bool QGraphicsItem::hasFocus() const { if (d_ptr->focusProxy) return d_ptr->focusProxy->hasFocus(); - return (d_ptr->scene && d_ptr->scene->focusItem() == this); + return isActive() && (d_ptr->scene && d_ptr->scene->focusItem() == this); } /*! @@ -3412,6 +3452,7 @@ void QGraphicsItem::setPos(const QPointF &pos) // Send post-notification. itemChange(QGraphicsItem::ItemPositionHasChanged, newPosVariant); + d_ptr->sendScenePosChange(); } /*! @@ -4022,6 +4063,7 @@ void QGraphicsItem::setTransform(const QTransform &matrix, bool combine) // Send post-notification. itemChange(ItemTransformHasChanged, newTransformVariant); + d_ptr->sendScenePosChange(); } /*! @@ -4246,6 +4288,24 @@ void QGraphicsItemPrivate::ensureSequentialSiblingIndex() } /*! + \internal +*/ +inline void QGraphicsItemPrivate::sendScenePosChange() +{ + Q_Q(QGraphicsItem); + if (scene) { + if (flags & QGraphicsItem::ItemSendsScenePositionChanges) + q->itemChange(QGraphicsItem::ItemScenePositionHasChanged, q->scenePos()); + if (scenePosDescendants) { + foreach (QGraphicsItem *item, scene->d_func()->scenePosItems) { + if (q->isAncestorOf(item)) + item->itemChange(QGraphicsItem::ItemScenePositionHasChanged, item->scenePos()); + } + } + } +} + +/*! \since 4.6 Stacks this item before \a sibling, which must be a sibling item (i.e., the @@ -4296,6 +4356,12 @@ void QGraphicsItem::stackBefore(const QGraphicsItem *sibling) ++index; } d_ptr->siblingIndex = siblingIndex; + for (int i = 0; i < siblings->size(); ++i) { + int &index = siblings->at(i)->d_ptr->siblingIndex; + if (i != siblingIndex && index >= siblingIndex && index <= myIndex) + siblings->at(i)->d_ptr->siblingOrderChange(); + } + d_ptr->siblingOrderChange(); } } @@ -4977,6 +5043,7 @@ int QGraphicsItemPrivate::depth() const /*! \internal */ +#ifndef QT_NO_GRAPHICSEFFECT void QGraphicsItemPrivate::invalidateGraphicsEffectsRecursively() { QGraphicsItemPrivate *itemPrivate = this; @@ -4989,6 +5056,7 @@ void QGraphicsItemPrivate::invalidateGraphicsEffectsRecursively() } } while ((itemPrivate = itemPrivate->parent ? itemPrivate->parent->d_ptr.data() : 0)); } +#endif //QT_NO_GRAPHICSEFFECT /*! \internal @@ -5293,6 +5361,16 @@ void QGraphicsItemPrivate::subFocusItemChange() /*! \internal + Subclasses can reimplement this function to be notified when its + siblingIndex order is changed. +*/ +void QGraphicsItemPrivate::siblingOrderChange() +{ +} + +/*! + \internal + Tells us if it is a proxy widget */ bool QGraphicsItemPrivate::isProxyWidget() const @@ -5324,7 +5402,9 @@ void QGraphicsItem::update(const QRectF &rect) return; // Make sure we notify effects about invalidated source. +#ifndef QT_NO_GRAPHICSEFFECT d_ptr->invalidateGraphicsEffectsRecursively(); +#endif //QT_NO_GRAPHICSEFFECT if (CacheMode(d_ptr->cacheMode) != NoCache) { // Invalidate cache. @@ -10678,6 +10758,7 @@ int QGraphicsItemGroup::type() const return Type; } +#ifndef QT_NO_GRAPHICSEFFECT QRectF QGraphicsItemEffectSourcePrivate::boundingRect(Qt::CoordinateSystem system) const { const bool deviceCoordinates = (system == Qt::DeviceCoordinates); @@ -10817,6 +10898,7 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP return pixmap; } +#endif //QT_NO_GRAPHICSEFFECT #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, QGraphicsItem *item) @@ -10940,6 +11022,9 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemChange change) case QGraphicsItem::ItemOpacityHasChanged: str = "ItemOpacityHasChanged"; break; + case QGraphicsItem::ItemScenePositionHasChanged: + str = "ItemScenePositionHasChanged"; + break; } debug << str; return debug; @@ -10997,6 +11082,9 @@ QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag) case QGraphicsItem::ItemIsFocusScope: str = "ItemIsFocusScope"; break; + case QGraphicsItem::ItemSendsScenePositionChanges: + str = "ItemSendsScenePositionChanges"; + break; } debug << str; return debug; diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h index f091e34..8bbe9f1 100644 --- a/src/gui/graphicsview/qgraphicsitem.h +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -105,7 +105,8 @@ public: ItemAcceptsInputMethod = 0x1000, ItemNegativeZStacksBehindParent = 0x2000, ItemIsPanel = 0x4000, - ItemIsFocusScope = 0x8000 // internal + ItemIsFocusScope = 0x8000, // internal + ItemSendsScenePositionChanges = 0x10000 // NB! Don't forget to increase the d_ptr->flags bit field by 1 when adding a new flag. }; Q_DECLARE_FLAGS(GraphicsItemFlags, GraphicsItemFlag) @@ -137,7 +138,8 @@ public: ItemZValueChange, ItemZValueHasChanged, ItemOpacityChange, - ItemOpacityHasChanged + ItemOpacityHasChanged, + ItemScenePositionHasChanged }; enum CacheMode { @@ -225,9 +227,11 @@ public: qreal effectiveOpacity() const; void setOpacity(qreal opacity); +#ifndef QT_NO_GRAPHICSEFFECT // Effect QGraphicsEffect *graphicsEffect() const; void setGraphicsEffect(QGraphicsEffect *effect); +#endif //QT_NO_GRAPHICSEFFECT Qt::MouseButtons acceptedMouseButtons() const; void setAcceptedMouseButtons(Qt::MouseButtons buttons); diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h index 51d2ffd..afc2198 100644 --- a/src/gui/graphicsview/qgraphicsitem_p.h +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -179,6 +179,7 @@ public: holesInSiblingIndex(0), sequentialOrdering(1), updateDueToGraphicsEffect(0), + scenePosDescendants(0), globalStackingOrder(-1), q_ptr(0) { @@ -223,7 +224,9 @@ public: bool discardUpdateRequest(bool ignoreClipping = false, bool ignoreVisibleBit = false, bool ignoreDirtyBit = false, bool ignoreOpacity = false) const; int depth() const; +#ifndef QT_NO_GRAPHICSEFFECT void invalidateGraphicsEffectsRecursively(); +#endif //QT_NO_GRAPHICSEFFECT void invalidateDepthRecursively(); void resolveDepth(); void addChild(QGraphicsItem *child); @@ -429,6 +432,8 @@ public: inline void ensureSortedChildren(); static inline bool insertionOrder(QGraphicsItem *a, QGraphicsItem *b); void ensureSequentialSiblingIndex(); + inline void sendScenePosChange(); + virtual void siblingOrderChange(); QPainterPath cachedClipPath; QRectF childrenBoundingRect; @@ -483,7 +488,7 @@ public: // Packed 32 bits quint32 fullUpdatePending : 1; - quint32 flags : 16; + quint32 flags : 17; quint32 dirtyChildrenBoundingRect : 1; quint32 paintedViewBoundingRectsNeedRepaint : 1; quint32 dirtySceneTransform : 1; @@ -498,14 +503,15 @@ public: quint32 sceneTransformTranslateOnly : 1; quint32 notifyBoundingRectChanged : 1; quint32 notifyInvalidated : 1; - quint32 mouseSetsFocus : 1; // New 32 bits + quint32 mouseSetsFocus : 1; quint32 explicitActivate : 1; quint32 wantsActive : 1; quint32 holesInSiblingIndex : 1; quint32 sequentialOrdering : 1; quint32 updateDueToGraphicsEffect : 1; + quint32 scenePosDescendants : 1; // Optional stacking order int globalStackingOrder; @@ -577,6 +583,7 @@ struct QGraphicsItemPaintInfo quint32 drawItem : 1; }; +#ifndef QT_NO_GRAPHICSEFFECT class QGraphicsItemEffectSourcePrivate : public QGraphicsEffectSourcePrivate { public: @@ -632,7 +639,7 @@ public: QGraphicsItemPaintInfo *info; QTransform lastEffectTransform; }; - +#endif //QT_NO_GRAPHICSEFFECT /*! Returns true if \a item1 is on top of \a item2. diff --git a/src/gui/graphicsview/qgraphicslinearlayout.cpp b/src/gui/graphicsview/qgraphicslinearlayout.cpp index 5684f0e..cb68741 100644 --- a/src/gui/graphicsview/qgraphicslinearlayout.cpp +++ b/src/gui/graphicsview/qgraphicslinearlayout.cpp @@ -542,6 +542,9 @@ void QGraphicsLinearLayout::invalidate() QGraphicsLayout::invalidate(); } +/*! + \internal +*/ void QGraphicsLinearLayout::dump(int indent) const { #ifdef QT_DEBUG diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index dc036f8..d1845c2 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -294,6 +294,7 @@ QGraphicsScenePrivate::QGraphicsScenePrivate() needSortTopLevelItems(true), holesInTopLevelSiblingIndex(false), topLevelSequentialOrdering(true), + scenePosDescendantsUpdatePending(false), stickyFocus(false), hasFocus(false), focusItem(0), @@ -488,6 +489,55 @@ void QGraphicsScenePrivate::_q_processDirtyItems() /*! \internal +*/ +void QGraphicsScenePrivate::setScenePosItemEnabled(QGraphicsItem *item, bool enabled) +{ + QGraphicsItem *p = item->d_ptr->parent; + while (p) { + p->d_ptr->scenePosDescendants = enabled; + p = p->d_ptr->parent; + } + if (!enabled && !scenePosDescendantsUpdatePending) { + scenePosDescendantsUpdatePending = true; + QMetaObject::invokeMethod(q_func(), "_q_updateScenePosDescendants", Qt::QueuedConnection); + } +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::registerScenePosItem(QGraphicsItem *item) +{ + scenePosItems.insert(item); + setScenePosItemEnabled(item, true); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::unregisterScenePosItem(QGraphicsItem *item) +{ + scenePosItems.remove(item); + setScenePosItemEnabled(item, false); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::_q_updateScenePosDescendants() +{ + foreach (QGraphicsItem *item, scenePosItems) { + QGraphicsItem *p = item->d_ptr->parent; + while (p) { + p->d_ptr->scenePosDescendants = 1; + p = p->d_ptr->parent; + } + } + scenePosDescendantsUpdatePending = false; +} + +/*! + \internal Schedules an item for removal. This function leaves some stale indexes around in the BSP tree if called from the item's destructor; these will @@ -523,6 +573,9 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) widget->d_func()->fixFocusChainBeforeReparenting(0, 0); } + if (item->flags() & QGraphicsItem::ItemSendsScenePositionChanges) + unregisterScenePosItem(item); + item->d_func()->scene = 0; //We need to remove all children first because they might use their parent @@ -2293,8 +2346,9 @@ void QGraphicsScene::clear() // NB! We have to clear the index before deleting items; otherwise the // index might try to access dangling item pointers. d->index->clear(); - const QList<QGraphicsItem *> items = d->topLevelItems; - qDeleteAll(items); + // NB! QGraphicsScenePrivate::unregisterTopLevelItem() removes items + while (!d->topLevelItems.isEmpty()) + delete d->topLevelItems.first(); Q_ASSERT(d->topLevelItems.isEmpty()); d->lastItemCount = 0; d->allItemsIgnoreHoverEvents = true; @@ -2540,6 +2594,9 @@ void QGraphicsScene::addItem(QGraphicsItem *item) } } + if (item->flags() & QGraphicsItem::ItemSendsScenePositionChanges) + d->registerScenePosItem(item); + // Ensure that newly added items that have subfocus set, gain // focus automatically if there isn't a focus item already. if (!d->focusItem && item != d->lastFocusItem && item->focusItem() == item) @@ -2826,18 +2883,20 @@ void QGraphicsScene::removeItem(QGraphicsItem *item) } /*! - Returns the scene's current focus item, or 0 if no item currently has - focus. + When the scene is active, this functions returns the scene's current focus + item, or 0 if no item currently has focus. When the scene is inactive, this + functions returns the item that will gain input focus when the scene becomes + active. The focus item receives keyboard input when the scene receives a key event. - \sa setFocusItem(), QGraphicsItem::hasFocus() + \sa setFocusItem(), QGraphicsItem::hasFocus(), isActive() */ QGraphicsItem *QGraphicsScene::focusItem() const { Q_D(const QGraphicsScene); - return d->focusItem; + return isActive() ? d->focusItem : d->lastFocusItem; } /*! @@ -4577,6 +4636,7 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * if (itemHasChildren && itemClipsChildrenToShape) ENSURE_TRANSFORM_PTR; +#ifndef QT_NO_GRAPHICSEFFECT if (item->d_ptr->graphicsEffect && item->d_ptr->graphicsEffect->isEnabled()) { ENSURE_TRANSFORM_PTR; QGraphicsItemPaintInfo info(viewTransform, transformPtr, effectTransform, exposedRegion, widget, &styleOptionTmp, @@ -4599,7 +4659,9 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter * item->d_ptr->graphicsEffect->draw(painter, source); painter->setWorldTransform(restoreTransform); sourced->info = 0; - } else { + } else +#endif //QT_NO_GRAPHICSEFFECT + { draw(item, painter, viewTransform, transformPtr, exposedRegion, widget, opacity, effectTransform, wasDirtyParentSceneTransform, drawItem); } @@ -4768,10 +4830,12 @@ void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, b QGraphicsItem *p = item->d_ptr->parent; while (p) { p->d_ptr->dirtyChildren = 1; +#ifndef QT_NO_GRAPHICSEFFECT if (p->d_ptr->graphicsEffect && p->d_ptr->graphicsEffect->isEnabled()) { p->d_ptr->dirty = 1; p->d_ptr->fullUpdatePending = 1; } +#endif //QT_NO_GRAPHICSEFFECT p = p->d_ptr->parent; } } diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h index d6d48d7..a47574e 100644 --- a/src/gui/graphicsview/qgraphicsscene.h +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -299,6 +299,7 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_emitUpdated()) Q_PRIVATE_SLOT(d_func(), void _q_polishItems()) Q_PRIVATE_SLOT(d_func(), void _q_processDirtyItems()) + Q_PRIVATE_SLOT(d_func(), void _q_updateScenePosDescendants()) friend class QGraphicsItem; friend class QGraphicsItemPrivate; friend class QGraphicsView; diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index f8db084..fdec466 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -122,6 +122,13 @@ public: void _q_processDirtyItems(); + QSet<QGraphicsItem *> scenePosItems; + bool scenePosDescendantsUpdatePending; + void setScenePosItemEnabled(QGraphicsItem *item, bool enabled); + void registerScenePosItem(QGraphicsItem *item); + void unregisterScenePosItem(QGraphicsItem *item); + void _q_updateScenePosDescendants(); + void removeItemHelper(QGraphicsItem *item); QBrush backgroundBrush; @@ -234,6 +241,7 @@ public: item->d_ptr->fullUpdatePending = 0; item->d_ptr->ignoreVisible = 0; item->d_ptr->ignoreOpacity = 0; +#ifndef QT_NO_GRAPHICSEFFECT QGraphicsEffect::ChangeFlags flags; if (item->d_ptr->notifyBoundingRectChanged) { flags |= QGraphicsEffect::SourceBoundingRectChanged; @@ -243,12 +251,15 @@ public: flags |= QGraphicsEffect::SourceInvalidated; item->d_ptr->notifyInvalidated = 0; } +#endif //QT_NO_GRAPHICSEFFECT if (recursive) { for (int i = 0; i < item->d_ptr->children.size(); ++i) resetDirtyItem(item->d_ptr->children.at(i), recursive); } +#ifndef QT_NO_GRAPHICSEFFECT if (flags && item->d_ptr->graphicsEffect) item->d_ptr->graphicsEffect->sourceChanged(flags); +#endif //QT_NO_GRAPHICSEFFECT } inline void ensureSortedTopLevelItems() diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp index c88f678..87585a2 100644 --- a/src/gui/graphicsview/qgraphicsview.cpp +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -1512,6 +1512,11 @@ void QGraphicsView::setScene(QGraphicsScene *scene) this, SLOT(updateSceneRect(QRectF))); d->scene->d_func()->removeView(this); d->connectedToScene = false; + + if (isActiveWindow() && isVisible()) { + QEvent windowDeactivate(QEvent::WindowDeactivate); + QApplication::sendEvent(d->scene, &windowDeactivate); + } } // Assign the new scene and update the contents (scrollbars, etc.)). @@ -1533,6 +1538,11 @@ void QGraphicsView::setScene(QGraphicsScene *scene) // enable touch events if any items is interested in them if (!d->scene->d_func()->allItemsIgnoreTouchEvents) d->viewport->setAttribute(Qt::WA_AcceptTouchEvents); + + if (isActiveWindow() && isVisible()) { + QEvent windowActivate(QEvent::WindowActivate); + QApplication::sendEvent(d->scene, &windowActivate); + } } else { d->recalculateContentSize(); } @@ -2638,6 +2648,19 @@ bool QGraphicsView::viewportEvent(QEvent *event) d->scene->d_func()->removePopup(d->scene->d_func()->popupWidgets.first()); QApplication::sendEvent(d->scene, event); break; + case QEvent::Show: + if (d->scene && isActiveWindow()) { + QEvent windowActivate(QEvent::WindowActivate); + QApplication::sendEvent(d->scene, &windowActivate); + } + break; + case QEvent::Hide: + // spontaneous event will generate a WindowDeactivate. + if (!event->spontaneous() && d->scene && isActiveWindow()) { + QEvent windowDeactivate(QEvent::WindowDeactivate); + QApplication::sendEvent(d->scene, &windowDeactivate); + } + break; case QEvent::Leave: // ### This is a temporary fix for until we get proper mouse grab // events. activeMouseGrabberItem should be set to 0 if we lose the @@ -3252,10 +3275,13 @@ void QGraphicsView::paintEvent(QPaintEvent *event) // Determine the exposed region d->exposedRegion = event->region(); + if (d->exposedRegion.isEmpty()) + d->exposedRegion = viewport()->rect(); QRectF exposedSceneRect = mapToScene(d->exposedRegion.boundingRect()).boundingRect(); // Set up the painter QPainter painter(viewport()); + painter.setClipRect(event->rect(), Qt::IntersectClip); #ifndef QT_NO_RUBBERBAND if (d->rubberBanding && !d->rubberBandRect.isEmpty()) painter.save(); diff --git a/src/gui/graphicsview/qgraphicswidget.cpp b/src/gui/graphicsview/qgraphicswidget.cpp index d70a281..6f910c7 100644 --- a/src/gui/graphicsview/qgraphicswidget.cpp +++ b/src/gui/graphicsview/qgraphicswidget.cpp @@ -410,6 +410,27 @@ void QGraphicsWidget::setGeometry(const QRectF &rect) */ /*! + \property QGraphicsWidget::minimumSize + \brief the minimum size of the widget + + \sa setMinimumSize(), minimumSize(), preferredSize, maximumSize +*/ + +/*! + \property QGraphicsWidget::preferredSize + \brief the preferred size of the widget + + \sa setPreferredSize(), preferredSize(), minimumSize, maximumSize +*/ + +/*! + \property QGraphicsWidget::maximumSize + \brief the maximum size of the widget + + \sa setMaximumSize(), maximumSize(), minimumSize, preferredSize +*/ + +/*! Sets the widget's contents margins to \a left, \a top, \a right and \a bottom. diff --git a/src/gui/graphicsview/qgraphicswidget.h b/src/gui/graphicsview/qgraphicswidget.h index 9c71140..38f72f0 100644 --- a/src/gui/graphicsview/qgraphicswidget.h +++ b/src/gui/graphicsview/qgraphicswidget.h @@ -74,6 +74,9 @@ class Q_GUI_EXPORT QGraphicsWidget : public QGraphicsObject, public QGraphicsLay Q_PROPERTY(QFont font READ font WRITE setFont) Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection RESET unsetLayoutDirection) Q_PROPERTY(QSizeF size READ size WRITE resize) + Q_PROPERTY(QSizeF minimumSize READ minimumSize WRITE setMinimumSize) + Q_PROPERTY(QSizeF preferredSize READ preferredSize WRITE setPreferredSize) + Q_PROPERTY(QSizeF maximumSize READ maximumSize WRITE setMaximumSize) Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy) Q_PROPERTY(Qt::WindowFlags windowFlags READ windowFlags WRITE setWindowFlags) Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle) diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp index 86b10b4..cd40f9e 100644 --- a/src/gui/graphicsview/qsimplex_p.cpp +++ b/src/gui/graphicsview/qsimplex_p.cpp @@ -288,7 +288,7 @@ bool QSimplex::setConstraints(const QList<QSimplexConstraint *> newConstraints) // original problem. // Otherwise, we clean up our structures and report there is // no feasible solution. - if (valueAt(0, columns - 1) != 0.0) { + if ((valueAt(0, columns - 1) != 0.0) && (qAbs(valueAt(0, columns - 1)) > 0.00001)) { qWarning() << "QSimplex: No feasible solution!"; clearDataStructures(); return false; diff --git a/src/gui/graphicsview/qsimplex_p.h b/src/gui/graphicsview/qsimplex_p.h index 084ad7f..a5816d1 100644 --- a/src/gui/graphicsview/qsimplex_p.h +++ b/src/gui/graphicsview/qsimplex_p.h @@ -107,7 +107,7 @@ struct QSimplexConstraint Q_ASSERT(constant > 0 || qFuzzyCompare(1, 1 + constant)); - if ((leftHandSide == constant) || qFuzzyCompare(1000 + leftHandSide, 1000 + constant)) + if ((leftHandSide == constant) || qAbs(leftHandSide - constant) < 0.00000001) return true; switch (ratio) { |