summaryrefslogtreecommitdiffstats
path: root/src/gui/graphicsview
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar@trolltech.com>2009-11-04 06:38:36 (GMT)
committerGunnar Sletta <gunnar@trolltech.com>2009-11-04 06:38:36 (GMT)
commit764f558846b8ee6f115004fc939b890991c40bfa (patch)
tree9bd47140cedbc9d8d28702618e6ad86fa6f2f666 /src/gui/graphicsview
parent9b0c0faf73dbdc382f1c02a42b10018bf9961d72 (diff)
parent1400ce5b85fbe7c67899f5f62bfd276eecb21ae0 (diff)
downloadQt-764f558846b8ee6f115004fc939b890991c40bfa.zip
Qt-764f558846b8ee6f115004fc939b890991c40bfa.tar.gz
Qt-764f558846b8ee6f115004fc939b890991c40bfa.tar.bz2
Merge branch '4.6' of c:\dev\qt-graphics-4.6
Conflicts: tools/qdoc3/test/qt-build-docs.qdocconf tools/qdoc3/test/qt-inc.qdocconf
Diffstat (limited to 'src/gui/graphicsview')
-rw-r--r--src/gui/graphicsview/qgraphicsanchorlayout.cpp157
-rw-r--r--src/gui/graphicsview/qgraphicsanchorlayout.h3
-rw-r--r--src/gui/graphicsview/qgraphicsanchorlayout_p.cpp392
-rw-r--r--src/gui/graphicsview/qgraphicsanchorlayout_p.h53
-rw-r--r--src/gui/graphicsview/qgraphicsitem.cpp146
-rw-r--r--src/gui/graphicsview/qgraphicsitem.h3
-rw-r--r--src/gui/graphicsview/qgraphicsitem_p.h89
-rw-r--r--src/gui/graphicsview/qgraphicslayout_p.h51
-rw-r--r--src/gui/graphicsview/qgraphicslinearlayout.cpp10
-rw-r--r--src/gui/graphicsview/qgraphicslinearlayout.h2
-rw-r--r--src/gui/graphicsview/qgraphicsproxywidget.cpp15
-rw-r--r--src/gui/graphicsview/qgraphicsscene.cpp276
-rw-r--r--src/gui/graphicsview/qgraphicsscene_p.h7
-rw-r--r--src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp68
-rw-r--r--src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h2
-rw-r--r--src/gui/graphicsview/qgraphicstransform.cpp8
-rw-r--r--src/gui/graphicsview/qgraphicstransform.h2
-rw-r--r--src/gui/graphicsview/qgraphicstransform_p.h3
-rw-r--r--src/gui/graphicsview/qgraphicsview.cpp29
-rw-r--r--src/gui/graphicsview/qgraphicswidget.cpp2
-rw-r--r--src/gui/graphicsview/qgridlayoutengine_p.h25
-rw-r--r--src/gui/graphicsview/qsimplex_p.cpp111
-rw-r--r--src/gui/graphicsview/qsimplex_p.h38
23 files changed, 1098 insertions, 394 deletions
diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp
index c39e8a6..56d70e1 100644
--- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp
+++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp
@@ -48,36 +48,60 @@
\ingroup geomanagement
\ingroup graphicsview-api
- The anchor layout is a layout where one can specify how widgets should be placed relative to
- each other. The specification is called an anchor, and it is set up by calling anchor().
+ The anchor layout allows developers to specify how widgets should be placed relative to
+ each other, and to the layout itself. The specification is made by adding anchors to the
+ layout by calling addAnchor(), addAnchors() or addCornerAnchors().
+
+ Existing anchors in the layout can be accessed with the anchor() function.
+ Items that are anchored are automatically added to the layout, and if items
+ are removed, all their anchors will be automatically removed.
+
+ \beginfloatleft
+ \inlineimage simpleanchorlayout-example.png Using an anchor layout to align simple colored widgets.
+ \endfloat
+
Anchors are always set up between edges of an item, where the "center" is also considered to
- be an edge. Considering this example:
- \code
- QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout;
- QGraphicsWidget *a = new QGraphicsWidget;
- QGraphicsWidget *b = new QGraphicsWidget;
- l->anchor(a, Qt::AnchorRight, b, Qt::AnchorLeft);
- \endcode
-
- Here is the right edge of item A anchored to the left edge of item B, with the result that
- item B will be placed to the right of item A, with a spacing between A and B. If the
- spacing is negative, the items will overlap to some extent. Items that are anchored are
- automatically added to the layout, and if items are removed, all their anchors will be
- automatically removed
-
- \section1 Size Hints and Size Policies in QGraphicsAnchorLayout
+ be an edge. Consider the following example:
+
+ \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding anchors
+
+ Here, the right edge of item \c a is anchored to the left edge of item \c b and the bottom
+ edge of item \c a is anchored to the top edge of item \c b, with the result that
+ item \c b will be placed diagonally to the right and below item \c b.
+
+ The addCornerAnchors() function provides a simpler way of anchoring the corners
+ of two widgets than the two individual calls to addAnchor() shown in the code
+ above. Here, we see how a widget can be anchored to the top-left corner of the enclosing
+ layout:
+
+ \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding a corner anchor
+
+ In cases where anchors are used to match the widths or heights of widgets, it is
+ convenient to use the addAnchors() function. As with the other functions for specifying
+ anchors, it can also be used to anchor a widget to a layout.
+
+ \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 respect stretch factors currently. This might change in the future, so please refrain
- from using stretch factors in anchor layout to avoid any future regressions.
+ 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.
+
+ \section1 Spacing within an Anchor Layout
+
+ The layout may distribute some space between the items. If the spacing has not been
+ explicitly specified, the actual amount of space will usually be 0.
+
+ However, if the first edge is the \e opposite of the second edge (e.g., the right edge
+ of the first widget is anchored to the left edge of the second widget), the size of the
+ anchor will be queried from the style through a pixel metric:
+ \l{QStyle::}{PM_LayoutHorizontalSpacing} for horizontal anchors and
+ \l{QStyle::}{PM_LayoutVerticalSpacing} for vertical anchors.
- \section1 Spacing within QGraphicsAnchorLayout
+ If the spacing is negative, the items will overlap to some extent.
- Between the items, the layout can distribute some space. If the spacing has not been
- explicitly specified, the actual amount of space will usually be 0, but if the first edge
- is the "opposite" of the second edge (i.e. Right is anchored to Left or vice-versa), the
- size of the anchor will be queried from the style through the pixelMetric
- PM_LayoutHorizontalSpacing (or PM_LayoutVerticalSpacing for vertical anchors).
+ \sa QGraphicsLinearLayout, QGraphicsGridLayout, QGraphicsLayout
*/
/*!
@@ -99,7 +123,7 @@
*/
#include "qgraphicsanchorlayout_p.h"
-
+#ifndef QT_NO_GRAPHICSVIEW
QT_BEGIN_NAMESPACE
QGraphicsAnchor::QGraphicsAnchor(QGraphicsAnchorLayout *parentLayout)
@@ -118,16 +142,26 @@ QGraphicsAnchor::~QGraphicsAnchor()
}
/*!
- Sets the size policy of the anchor to \a policy.
+ \property QGraphicsAnchor::sizePolicy
+ \brief the size policy for the QGraphicsAnchor.
+
+ By setting the size policy on an anchor you can configure how the item can resize itself
+ from its preferred spacing. For instance, if the anchor has the size policy
+ QSizePolicy::Minimum, the spacing is the minimum size of the anchor. However, its size
+ can grow up to the anchors maximum size. If the default size policy is QSizePolicy::Fixed,
+ the anchor can neither grow or shrink, which means that the only size the anchor can have
+ is the spacing. QSizePolicy::Fixed is the default size policy.
+ QGraphicsAnchor always has a minimum spacing of 0 and a very large maximum spacing.
+
+ \sa QGraphicsAnchor::spacing
*/
+
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);
@@ -136,12 +170,12 @@ QSizePolicy::Policy QGraphicsAnchor::sizePolicy() const
/*!
\property QGraphicsAnchor::spacing
- \brief the space between items in the QGraphicsAnchorLayout.
+ \brief the preferred space between items in the QGraphicsAnchorLayout.
Depending on the anchor type, the default spacing is either
0 or a value returned from the style.
- \sa QGraphicsAnchorLayout::anchor()
+ \sa QGraphicsAnchorLayout::addAnchor()
*/
void QGraphicsAnchor::setSpacing(qreal spacing)
{
@@ -205,7 +239,7 @@ QGraphicsAnchorLayout::~QGraphicsAnchorLayout()
If there is already an anchor between the edges, the the new anchor will replace the old one.
\a firstItem and \a secondItem are automatically added to the layout if they are not part
- of the layout. This means that count() can increase with up to 2.
+ of the layout. This means that count() can increase by up to 2.
The spacing an anchor will get depends on the type of anchor. For instance, anchors from the
Right edge of one item to the Left edge of another (or vice versa) will use the default
@@ -215,7 +249,10 @@ QGraphicsAnchorLayout::~QGraphicsAnchorLayout()
The spacing can also be set manually by using QGraphicsAnchor::setSpacing() method.
- \sa addCornerAnchors(), addAnchors()
+ Calling this function where \a firstItem or \a secondItem are ancestors of the layout have
+ undefined behaviour.
+
+ \sa addAnchors(), addCornerAnchors()
*/
QGraphicsAnchor *
QGraphicsAnchorLayout::addAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge,
@@ -240,29 +277,26 @@ QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint fi
}
/*!
- Creates two anchors between \a firstItem and \a secondItem, where one is for the horizontal
- edge and another one for the vertical edge that the corners \a firstCorner and \a
- secondCorner specifies.
- The magnitude of the anchors is picked up from the style.
+ Creates two anchors between \a firstItem and \a secondItem specified by the corners,
+ \a firstCorner and \a secondCorner, where one is for the horizontal edge and another
+ one for the vertical edge.
+
+ This is a convenience function, since anchoring corners can be expressed as anchoring
+ two edges. For instance:
- This is a convenience function, since anchoring corners can be expressed as anchoring two edges.
- For instance,
- \code
- layout->addAnchor(layout, Qt::AnchorTop, b, Qt::AnchorTop);
- layout->addAnchor(layout, Qt::AnchorLeft, b, Qt::AnchorLeft);
- \endcode
+ \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding a corner anchor in two steps
- has the same effect as
+ This can also be achieved with the following line of code:
- \code
- layout->addCornerAnchors(layout, Qt::TopLeft, b, Qt::TopLeft);
- \endcode
+ \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding a corner anchor
If there is already an anchor between the edge pairs, it will be replaced by the anchors that
this function specifies.
\a firstItem and \a secondItem are automatically added to the layout if they are not part of the
- layout. This means that count() can increase with up to 2.
+ layout. This means that count() can increase by up to 2.
+
+ \sa addAnchor(), addAnchors()
*/
void QGraphicsAnchorLayout::addCornerAnchors(QGraphicsLayoutItem *firstItem,
Qt::Corner firstCorner,
@@ -289,17 +323,16 @@ void QGraphicsAnchorLayout::addCornerAnchors(QGraphicsLayoutItem *firstItem,
edges of \a secondItem, so that \a firstItem has the same size as
\a secondItem in the dimensions specified by \a orientations.
- Calling this convenience function with the following arguments
- \code
- l->addAnchors(firstItem, secondItem, Qt::Horizontal)
- \endcode
+ For example, the following example anchors the left and right edges of two items
+ to match their widths:
+
+ \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding anchors to match sizes in two steps
+
+ This can also be achieved using the following line of code:
- is the same as
+ \snippet examples/graphicsview/simpleanchorlayout/main.cpp adding anchors to match sizes
- \code
- l->addAnchor(firstItem, Qt::AnchorLeft, secondItem, Qt::AnchorLeft);
- l->addAnchor(firstItem, Qt::AnchorRight, secondItem, Qt::AnchorRight);
- \endcode
+ \sa addAnchor(), addCornerAnchors()
*/
void QGraphicsAnchorLayout::addAnchors(QGraphicsLayoutItem *firstItem,
QGraphicsLayoutItem *secondItem,
@@ -387,7 +420,7 @@ void QGraphicsAnchorLayout::setSpacing(qreal spacing)
qreal QGraphicsAnchorLayout::horizontalSpacing() const
{
Q_D(const QGraphicsAnchorLayout);
- return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Horizontal);
+ return d->styleInfo().defaultSpacing(Qt::Horizontal);
}
/*!
@@ -398,7 +431,7 @@ qreal QGraphicsAnchorLayout::horizontalSpacing() const
qreal QGraphicsAnchorLayout::verticalSpacing() const
{
Q_D(const QGraphicsAnchorLayout);
- return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Vertical);
+ return d->styleInfo().defaultSpacing(Qt::Vertical);
}
/*!
@@ -468,7 +501,8 @@ void QGraphicsAnchorLayout::invalidate()
{
Q_D(QGraphicsAnchorLayout);
QGraphicsLayout::invalidate();
- d->calculateGraphCacheDirty = 1;
+ d->calculateGraphCacheDirty = true;
+ d->styleInfoDirty = true;
}
/*!
@@ -502,3 +536,4 @@ QSizeF QGraphicsAnchorLayout::sizeHint(Qt::SizeHint which, const QSizeF &constra
}
QT_END_NAMESPACE
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h
index f09ac43..01c3a86 100644
--- a/src/gui/graphicsview/qgraphicsanchorlayout.h
+++ b/src/gui/graphicsview/qgraphicsanchorlayout.h
@@ -62,12 +62,13 @@ class Q_GUI_EXPORT QGraphicsAnchor : public QObject
{
Q_OBJECT
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing RESET unsetSpacing)
+ Q_PROPERTY(QSizePolicy::Policy sizePolicy READ sizePolicy WRITE setSizePolicy)
public:
void setSpacing(qreal spacing);
void unsetSpacing();
+ qreal spacing() const;
void setSizePolicy(QSizePolicy::Policy policy);
QSizePolicy::Policy sizePolicy() const;
- qreal spacing() const;
~QGraphicsAnchor();
private:
QGraphicsAnchor(QGraphicsAnchorLayout *parent);
diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp
index 8c8c303..41aa8aa 100644
--- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp
+++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp
@@ -40,6 +40,7 @@
****************************************************************************/
#include <QtGui/qwidget.h>
+#include <QtGui/qapplication.h>
#include <QtCore/qlinkedlist.h>
#include <QtCore/qstack.h>
@@ -48,7 +49,7 @@
#endif
#include "qgraphicsanchorlayout_p.h"
-
+#ifndef QT_NO_GRAPHICSVIEW
QT_BEGIN_NAMESPACE
@@ -141,34 +142,25 @@ static void internalSizeHints(QSizePolicy::Policy policy,
*expSize = *prefSize;
}
-void AnchorData::refreshSizeHints(qreal effectiveSpacing)
+bool AnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo)
{
- 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);
-
+ // It is an internal anchor
+ if (item) {
if (isLayoutAnchor) {
minSize = 0;
prefSize = 0;
expSize = 0;
maxSize = QWIDGETSIZE_MAX;
- if (hasCenter)
+ if (isCenterAnchor)
maxSize /= 2;
- return;
+ return true;
} else {
-
- QGraphicsLayoutItem *item = from->m_item;
- if (orient == QGraphicsAnchorLayoutPrivate::Horizontal) {
+ if (orientation == QGraphicsAnchorLayoutPrivate::Horizontal) {
policy = item->sizePolicy().horizontalPolicy();
minSizeHint = item->effectiveSizeHint(Qt::MinimumSize).width();
prefSizeHint = item->effectiveSizeHint(Qt::PreferredSize).width();
@@ -180,7 +172,7 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing)
maxSizeHint = item->effectiveSizeHint(Qt::MaximumSize).height();
}
- if (hasCenter) {
+ if (isCenterAnchor) {
minSizeHint /= 2;
prefSizeHint /= 2;
maxSizeHint /= 2;
@@ -196,7 +188,20 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing)
// their effective size hints might be narrowed down due to their size policies.
prefSizeHint = prefSize;
} else {
- prefSizeHint = effectiveSpacing;
+ const Qt::Orientation orient = Qt::Orientation(QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge) + 1);
+ qreal s = styleInfo->defaultSpacing(orient);
+ if (s < 0) {
+ QSizePolicy::ControlType controlTypeFrom = from->m_item->sizePolicy().controlType();
+ QSizePolicy::ControlType controlTypeTo = to->m_item->sizePolicy().controlType();
+ s = styleInfo->perItemSpacing(controlTypeFrom, controlTypeTo, orient);
+
+ // ### 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;
+ }
+ prefSizeHint = s;
}
maxSizeHint = QWIDGETSIZE_MAX;
}
@@ -214,6 +219,8 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing)
sizeAtPreferred = prefSize;
sizeAtExpanding = prefSize;
sizeAtMaximum = prefSize;
+
+ return true;
}
void ParallelAnchorData::updateChildrenSizes()
@@ -227,26 +234,29 @@ void ParallelAnchorData::updateChildrenSizes()
secondEdge->updateChildrenSizes();
}
-void ParallelAnchorData::refreshSizeHints(qreal effectiveSpacing)
+bool ParallelAnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo)
{
- refreshSizeHints_helper(effectiveSpacing);
+ return refreshSizeHints_helper(styleInfo);
}
-void ParallelAnchorData::refreshSizeHints_helper(qreal effectiveSpacing,
+bool ParallelAnchorData::refreshSizeHints_helper(const QLayoutStyleInfo *styleInfo,
bool refreshChildren)
{
- if (refreshChildren) {
- firstEdge->refreshSizeHints(effectiveSpacing);
- secondEdge->refreshSizeHints(effectiveSpacing);
+ if (refreshChildren && (!firstEdge->refreshSizeHints(styleInfo)
+ || !secondEdge->refreshSizeHints(styleInfo))) {
+ return false;
}
- // ### should we warn if the parallel connection is invalid?
- // e.g. 1-2-3 with 10-20-30, the minimum of the latter is
- // bigger than the maximum of the former.
-
minSize = qMax(firstEdge->minSize, secondEdge->minSize);
maxSize = qMin(firstEdge->maxSize, secondEdge->maxSize);
+ // This condition means that the maximum size of one anchor being simplified is smaller than
+ // the minimum size of the other anchor. The consequence is that there won't be a valid size
+ // for this parallel setup.
+ if (minSize > maxSize) {
+ return false;
+ }
+
expSize = qMax(firstEdge->expSize, secondEdge->expSize);
expSize = qMin(expSize, maxSize);
@@ -258,6 +268,8 @@ void ParallelAnchorData::refreshSizeHints_helper(qreal effectiveSpacing,
sizeAtPreferred = prefSize;
sizeAtExpanding = prefSize;
sizeAtMaximum = prefSize;
+
+ return true;
}
/*!
@@ -362,12 +374,12 @@ void SequentialAnchorData::updateChildrenSizes()
}
}
-void SequentialAnchorData::refreshSizeHints(qreal effectiveSpacing)
+bool SequentialAnchorData::refreshSizeHints(const QLayoutStyleInfo *styleInfo)
{
- refreshSizeHints_helper(effectiveSpacing);
+ return refreshSizeHints_helper(styleInfo);
}
-void SequentialAnchorData::refreshSizeHints_helper(qreal effectiveSpacing,
+bool SequentialAnchorData::refreshSizeHints_helper(const QLayoutStyleInfo *styleInfo,
bool refreshChildren)
{
minSize = 0;
@@ -379,8 +391,8 @@ void SequentialAnchorData::refreshSizeHints_helper(qreal effectiveSpacing,
AnchorData *edge = m_edges.at(i);
// If it's the case refresh children information first
- if (refreshChildren)
- edge->refreshSizeHints(effectiveSpacing);
+ if (refreshChildren && !edge->refreshSizeHints(styleInfo))
+ return false;
minSize += edge->minSize;
prefSize += edge->prefSize;
@@ -393,6 +405,8 @@ void SequentialAnchorData::refreshSizeHints_helper(qreal effectiveSpacing,
sizeAtPreferred = prefSize;
sizeAtExpanding = prefSize;
sizeAtMaximum = prefSize;
+
+ return true;
}
#ifdef QT_DEBUG
@@ -458,7 +472,7 @@ QString GraphPath::toString() const
#endif
QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate()
- : calculateGraphCacheDirty(1)
+ : calculateGraphCacheDirty(true), styleInfoDirty(true)
{
for (int i = 0; i < NOrientations; ++i) {
for (int j = 0; j < 3; ++j) {
@@ -510,18 +524,49 @@ inline static qreal checkAdd(qreal a, qreal b)
}
/*!
- * \internal
- *
- * Takes the sequence of vertices described by (\a before, \a vertices, \a after) and replaces
- * all anchors connected to the vertices in \a vertices with one simplified anchor between
- * \a before and \a after. The simplified anchor will be a placeholder for all the previous
- * anchors between \a before and \a after, and can be restored back to the anchors it is a
- * placeholder for.
- */
-static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *graph,
- AnchorVertex *before,
- const QVector<AnchorVertex*> &vertices,
- AnchorVertex *after)
+ \internal
+
+ 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.
+*/
+static AnchorData *addAnchorMaybeParallel(Graph<AnchorVertex, AnchorData> *g,
+ AnchorData *newAnchor)
+{
+ bool feasible = true;
+
+ // If already exists one anchor where newAnchor is supposed to be, we create a parallel
+ // anchor.
+ if (AnchorData *oldAnchor = g->takeEdge(newAnchor->from, newAnchor->to)) {
+ ParallelAnchorData *parallel = new ParallelAnchorData(oldAnchor, newAnchor);
+
+ // 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);
+ newAnchor = parallel;
+ }
+
+ g->createEdge(newAnchor->from, newAnchor->to, newAnchor);
+ return feasible ? newAnchor : 0;
+}
+
+
+/*!
+ \internal
+
+ Takes the sequence of vertices described by (\a before, \a vertices, \a after) and removes
+ all anchors connected to the vertices in \a vertices, returning one simplified anchor between
+ \a before and \a after.
+
+ Note that this function doesn't add the created anchor to the graph. This should be done by
+ the caller.
+*/
+static AnchorData *createSequence(Graph<AnchorVertex, AnchorData> *graph,
+ AnchorVertex *before,
+ const QVector<AnchorVertex*> &vertices,
+ AnchorVertex *after)
{
AnchorData *data = graph->edgeData(before, vertices.first());
Q_ASSERT(data);
@@ -546,41 +591,24 @@ static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *graph,
qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString()));
#endif
- SequentialAnchorData *sequence = new SequentialAnchorData;
AnchorVertex *prev = before;
+ QVector<AnchorData *> edges;
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);
+ edges.append(ad);
prev = next;
}
- sequence->setVertices(orderedVertices);
+ SequentialAnchorData *sequence = new SequentialAnchorData(orderedVertices, edges);
sequence->from = before;
sequence->to = after;
sequence->refreshSizeHints_helper(0, false);
- // 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.
- sequence->isLayoutAnchor = (sequence->m_edges.first()->isLayoutAnchor
- || sequence->m_edges.last()->isLayoutAnchor);
-
- AnchorData *newAnchor = sequence;
- if (AnchorData *oldAnchor = graph->takeEdge(before, after)) {
- ParallelAnchorData *parallel = new ParallelAnchorData(oldAnchor, sequence);
- parallel->isLayoutAnchor = (oldAnchor->isLayoutAnchor
- || sequence->isLayoutAnchor);
- parallel->refreshSizeHints_helper(0, false);
- newAnchor = parallel;
- }
- graph->createEdge(before, after, newAnchor);
-
- // True if we created a parallel anchor
- return newAnchor != sequence;
+ return sequence;
}
/*!
@@ -617,15 +645,17 @@ static bool simplifySequentialChunk(Graph<AnchorVertex, AnchorData> *graph,
2. Go to (1)
3. Done
+ When creating the parallel anchors, the algorithm might identify unfeasible situations. In this
+ case the simplification process stops and returns false. Otherwise returns true.
*/
-void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation)
+bool QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation)
{
static bool noSimplification = !qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty();
if (noSimplification || items.isEmpty())
- return;
+ return true;
if (graphSimplified[orientation])
- return;
+ return true;
graphSimplified[orientation] = true;
#if 0
@@ -634,12 +664,18 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation)
#endif
if (!graph[orientation].rootVertex())
- return;
+ return true;
bool dirty;
+ bool feasible = true;
do {
- dirty = simplifyGraphIteration(orientation);
- } while (dirty);
+ dirty = simplifyGraphIteration(orientation, &feasible);
+ } while (dirty && feasible);
+
+ if (!feasible)
+ graphSimplified[orientation] = false;
+
+ return feasible;
}
/*!
@@ -656,7 +692,8 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation)
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)
+bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutPrivate::Orientation orientation,
+ bool *feasible)
{
Q_Q(QGraphicsAnchorLayout);
Graph<AnchorVertex, AnchorData> &g = graph[orientation];
@@ -667,8 +704,6 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP
QVector<AnchorVertex*> candidates;
bool candidatesForward;
- const Qt::AnchorPoint centerEdge = pickEdge(Qt::AnchorHorizontalCenter, orientation);
-
// Walk depth-first, in the stack we store start of the candidate sequence (beforeSequence)
// and the vertex to be visited.
while (!stack.isEmpty()) {
@@ -777,7 +812,8 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP
// 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) {
+ const AnchorData *firstAnchor = g.edgeData(beforeSequence, candidates.first());
+ if (firstAnchor->isCenterAnchor) {
beforeSequence = candidates.first();
candidates.remove(0);
@@ -786,7 +822,8 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP
continue;
}
- if (afterSequence->m_edge == centerEdge && afterSequence->m_item == candidates.last()->m_item) {
+ const AnchorData *lastAnchor = g.edgeData(candidates.last(), afterSequence);
+ if (lastAnchor->isCenterAnchor) {
afterSequence = candidates.last();
candidates.remove(candidates.count() - 1);
@@ -794,11 +831,26 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP
continue;
}
- // 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))
+ //
+ // Add the sequence to the graph.
+ //
+
+ 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);
+
+ if (!newAnchor) {
+ *feasible = false;
+ return false;
+ }
+
+ // When a new parallel anchor is create in the graph, we finish the iteration and return
+ // true to indicate a new iteration is needed. This happens because a parallel anchor
+ // changes the number of adjacents one vertex has, possibly opening up oportunities for
+ // building candidate lists (when adjacents == 2).
+ if (newAnchor != sequence)
return true;
// If there was no parallel simplification, we'll keep walking the graph. So we clear the
@@ -1008,11 +1060,13 @@ void QGraphicsAnchorLayoutPrivate::createCenterAnchors(
AnchorData *data = new AnchorData;
c->variables.insert(data, 1.0);
addAnchor_helper(item, firstEdge, item, centerEdge, data);
+ data->isCenterAnchor = true;
data->refreshSizeHints(0);
data = new AnchorData;
c->variables.insert(data, -1.0);
addAnchor_helper(item, centerEdge, item, lastEdge, data);
+ data->isCenterAnchor = true;
data->refreshSizeHints(0);
itemCenterConstraints[orientation].append(c);
@@ -1234,9 +1288,11 @@ void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstIt
{
Q_Q(QGraphicsAnchorLayout);
+ const Orientation orientation = edgeOrientation(firstEdge);
+
// Guarantee that the graph is no simplified when adding this anchor,
// anchor manipulation always happen in the full graph
- restoreSimplifiedGraph(edgeOrientation(firstEdge));
+ restoreSimplifiedGraph(orientation);
// Is the Vertex (firstItem, firstEdge) already represented in our
// internal structure?
@@ -1245,10 +1301,16 @@ void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstIt
// Remove previous anchor
// ### Could we update the existing edgeData rather than creating a new one?
- if (graph[edgeOrientation(firstEdge)].edgeData(v1, v2)) {
+ if (graph[orientation].edgeData(v1, v2)) {
removeAnchor_helper(v1, v2);
}
+ // If its an internal anchor, set the associated item
+ if (firstItem == secondItem)
+ data->item = firstItem;
+
+ data->orientation = orientation;
+
// Create a bi-directional edge in the sense it can be transversed both
// from v1 or v2. "data" however is shared between the two references
// so we still know that the anchor direction is from 1 to 2.
@@ -1257,10 +1319,11 @@ void QGraphicsAnchorLayoutPrivate::addAnchor_helper(QGraphicsLayoutItem *firstIt
#ifdef QT_DEBUG
data->name = QString::fromAscii("%1 --to--> %2").arg(v1->toString()).arg(v2->toString());
#endif
- // Keep track of anchors that are connected to the layout 'edges'
- data->isLayoutAnchor = (v1->m_item == q || v2->m_item == q);
+ // ### bit to track internal anchors, since inside AnchorData methods
+ // we don't have access to the 'q' pointer.
+ data->isLayoutAnchor = (data->item == q);
- graph[edgeOrientation(firstEdge)].createEdge(v1, v2, data);
+ graph[orientation].createEdge(v1, v2, data);
}
QGraphicsAnchor *QGraphicsAnchorLayoutPrivate::getAnchor(QGraphicsLayoutItem *firstItem,
@@ -1425,7 +1488,7 @@ void QGraphicsAnchorLayoutPrivate::anchorSize(const AnchorData *data,
Q_ASSERT(minSize || prefSize || maxSize);
Q_ASSERT(data);
QGraphicsAnchorLayoutPrivate *that = const_cast<QGraphicsAnchorLayoutPrivate *>(this);
- that->restoreSimplifiedGraph(edgeOrientation(data->from->m_edge));
+ that->restoreSimplifiedGraph(Orientation(data->orientation));
if (minSize)
*minSize = data->minSize;
@@ -1565,34 +1628,32 @@ void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&fi
}
}
-qreal QGraphicsAnchorLayoutPrivate::effectiveSpacing(Orientation orientation) const
+QLayoutStyleInfo &QGraphicsAnchorLayoutPrivate::styleInfo() const
{
- Q_Q(const QGraphicsAnchorLayout);
- qreal s = spacings[orientation];
- if (s < 0) {
- // ### make sure behaviour is the same as in QGraphicsGridLayout
+ if (styleInfoDirty) {
+ Q_Q(const QGraphicsAnchorLayout);
+ //### Fix this if QGV ever gets support for Metal style or different Aqua sizes.
+ QWidget *wid = 0;
+
QGraphicsLayoutItem *parent = q->parentLayoutItem();
while (parent && parent->isLayout()) {
parent = parent->parentLayoutItem();
}
+ QGraphicsWidget *w = 0;
if (parent) {
QGraphicsItem *parentItem = parent->graphicsItem();
- if (parentItem && parentItem->isWidget()) {
- QGraphicsWidget *w = static_cast<QGraphicsWidget*>(parentItem);
- s = w->style()->pixelMetric(orientation == Horizontal
- ? QStyle::PM_LayoutHorizontalSpacing
- : QStyle::PM_LayoutVerticalSpacing);
- }
+ if (parentItem && parentItem->isWidget())
+ w = static_cast<QGraphicsWidget*>(parentItem);
}
- }
- // ### 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;
+ QStyle *style = w ? w->style() : QApplication::style();
+ cachedStyleInfo = QLayoutStyleInfo(style, wid);
+ cachedStyleInfo.setDefaultSpacing(Qt::Horizontal, spacings[0]);
+ cachedStyleInfo.setDefaultSpacing(Qt::Vertical, spacings[1]);
- return s;
+ styleInfoDirty = false;
+ }
+ return cachedStyleInfo;
}
/*!
@@ -1620,7 +1681,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs()
dumpGraph(QString::fromAscii("%1-after").arg(count));
#endif
- calculateGraphCacheDirty = 0;
+ calculateGraphCacheDirty = false;
}
// ### Maybe getGraphParts could return the variables when traversing, at least
@@ -1638,38 +1699,52 @@ QList<AnchorData *> getVariables(QList<QSimplexConstraint *> constraints)
}
/*!
- \internal
+ \internal
- Calculate graphs is the method that puts together all the helper routines
- so that the AnchorLayout can calculate the sizes of each item.
+ Calculate graphs is the method that puts together all the helper routines
+ so that the AnchorLayout can calculate the sizes of each item.
- In a nutshell it should do:
+ In a nutshell it should do:
- 1) Update anchor nominal sizes, that is, the size that each anchor would
- have if no other restrictions applied. This is done by quering the
- layout style and the sizeHints of the items belonging to the layout.
+ 1) Refresh anchor nominal sizes, that is, the size that each anchor would
+ have if no other restrictions applied. This is done by quering the
+ layout style and the sizeHints of the items belonging to the layout.
- 2) Simplify the graph by grouping together parallel and sequential anchors
- into "group anchors". These have equivalent minimum, preferred and maximum
- sizeHints as the anchors they replace.
+ 2) Simplify the graph by grouping together parallel and sequential anchors
+ into "group anchors". These have equivalent minimum, preferred and maximum
+ sizeHints as the anchors they replace.
- 3) Check if we got to a trivial case. In some cases, the whole graph can be
- simplified into a single anchor. If so, use this information. If not,
- then call the Simplex solver to calculate the anchors sizes.
+ 3) Check if we got to a trivial case. In some cases, the whole graph can be
+ simplified into a single anchor. If so, use this information. If not,
+ then call the Simplex solver to calculate the anchors sizes.
- 4) Once the root anchors had its sizes calculated, propagate that to the
- anchors they represent.
+ 4) Once the root anchors had its sizes calculated, propagate that to the
+ anchors they represent.
*/
void QGraphicsAnchorLayoutPrivate::calculateGraphs(
QGraphicsAnchorLayoutPrivate::Orientation orientation)
{
Q_Q(QGraphicsAnchorLayout);
- // Simplify the graph
- simplifyGraph(orientation);
+#if defined(QT_DEBUG) || defined(Q_AUTOTEST_EXPORT)
+ lastCalculationUsedSimplex[orientation] = false;
+#endif
- // Reset the nominal sizes of each anchor based on the current item sizes
- setAnchorSizeHintsFromItems(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.
+ if (!refreshAllSizeHints(orientation)) {
+ qWarning("QGraphicsAnchorLayout: anchor setup is not feasible.");
+ graphHasConflicts[orientation] = true;
+ return;
+ }
+
+ // Simplify the graph
+ if (!simplifyGraph(orientation)) {
+ qWarning("QGraphicsAnchorLayout: anchor setup is not feasible.");
+ graphHasConflicts[orientation] = true;
+ return;
+ }
// Traverse all graph edges and store the possible paths to each vertex
findPaths(orientation);
@@ -1837,23 +1912,31 @@ bool QGraphicsAnchorLayoutPrivate::calculateNonTrunk(const QList<QSimplexConstra
}
/*!
- \internal
+ \internal
+
+ Traverse the graph refreshing the size hints. Complex anchors will call the
+ refresh method of their children anchors. Simple anchors, if are internal
+ anchors, will query the associated item for their size hints.
- For graph edges ("anchors") that represent items, this method updates their
- intrinsic size restrictions, based on the item size hints.
+ Returns false if some unfeasibility was found in the graph regarding the
+ complex anchors.
*/
-void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orientation)
+bool QGraphicsAnchorLayoutPrivate::refreshAllSizeHints(Orientation orientation)
{
Graph<AnchorVertex, AnchorData> &g = graph[orientation];
QList<QPair<AnchorVertex *, AnchorVertex *> > vertices = g.connections();
- qreal spacing = effectiveSpacing(orientation);
-
+ QLayoutStyleInfo styleInf = styleInfo();
for (int i = 0; i < vertices.count(); ++i) {
AnchorData *data = g.edgeData(vertices.at(i).first, vertices.at(i).second);;
Q_ASSERT(data->from && data->to);
- data->refreshSizeHints(spacing);
+
+ // During the traversal we check the feasibility of the complex anchors.
+ if (!data->refreshSizeHints(&styleInf))
+ return false;
}
+
+ return true;
}
/*!
@@ -1960,17 +2043,27 @@ QList<QSimplexConstraint *> QGraphicsAnchorLayoutPrivate::constraintsFromSizeHin
{
QList<QSimplexConstraint *> anchorConstraints;
for (int i = 0; i < anchors.size(); ++i) {
- QSimplexConstraint *c = new QSimplexConstraint;
- c->variables.insert(anchors[i], 1.0);
- c->constant = anchors[i]->minSize;
- c->ratio = QSimplexConstraint::MoreOrEqual;
- anchorConstraints += c;
-
- c = new QSimplexConstraint;
- c->variables.insert(anchors[i], 1.0);
- c->constant = anchors[i]->maxSize;
- c->ratio = QSimplexConstraint::LessOrEqual;
- anchorConstraints += c;
+ AnchorData *ad = anchors[i];
+
+ 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;
+ } else {
+ QSimplexConstraint *c = new QSimplexConstraint;
+ c->variables.insert(ad, 1.0);
+ c->constant = ad->minSize;
+ c->ratio = QSimplexConstraint::MoreOrEqual;
+ anchorConstraints += c;
+
+ c = new QSimplexConstraint;
+ c->variables.insert(ad, 1.0);
+ c->constant = ad->maxSize;
+ c->ratio = QSimplexConstraint::LessOrEqual;
+ anchorConstraints += c;
+ }
}
return anchorConstraints;
@@ -2110,8 +2203,8 @@ void QGraphicsAnchorLayoutPrivate::identifyNonFloatItems_helper(const AnchorData
switch(ad->type) {
case AnchorData::Normal:
- if (ad->from->m_item == ad->to->m_item && ad->to->m_item != q)
- nonFloatingItemsIdentifiedSoFar->insert(ad->to->m_item);
+ if (ad->item && ad->item != q)
+ nonFloatingItemsIdentifiedSoFar->insert(ad->item);
break;
case AnchorData::Sequential:
foreach (const AnchorData *d, static_cast<const SequentialAnchorData *>(ad)->m_edges)
@@ -2377,7 +2470,7 @@ bool QGraphicsAnchorLayoutPrivate::solveMinMax(const QList<QSimplexConstraint *>
*min = simplex.solveMin();
// Save sizeAtMinimum results
- QList<QSimplexVariable *> variables = simplex.constraintsVariables();
+ 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));
@@ -2536,7 +2629,7 @@ void QGraphicsAnchorLayoutPrivate::solveExpanding(const QList<QSimplexConstraint
hasExpanding = true;
// Lock anchor between boundedExpSize and sizeAtMaximum (ensure 3.a)
- if (boundedExpSize == ad->sizeAtMaximum) {
+ 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;
@@ -2635,3 +2728,4 @@ void QGraphicsAnchorLayoutPrivate::dumpGraph(const QString &name)
#endif
QT_END_NAMESPACE
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h
index d45c004..7dd0d65 100644
--- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h
+++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h
@@ -60,7 +60,7 @@
#include "qgraphicsanchorlayout.h"
#include "qgraph_p.h"
#include "qsimplex_p.h"
-
+#ifndef QT_NO_GRAPHICSVIEW
QT_BEGIN_NAMESPACE
/*
@@ -151,15 +151,16 @@ struct AnchorData : public QSimplexVariable {
};
AnchorData()
- : QSimplexVariable(), from(0), to(0),
+ : QSimplexVariable(), item(0), from(0), to(0),
minSize(0), prefSize(0), expSize(0), maxSize(0),
sizeAtMinimum(0), sizeAtPreferred(0),
sizeAtExpanding(0), sizeAtMaximum(0),
graphicsAnchor(0), skipInPreferred(0),
- type(Normal), hasSize(true), isLayoutAnchor(false) {}
+ type(Normal), hasSize(true), isLayoutAnchor(false),
+ isCenterAnchor(false), orientation(0) {}
virtual void updateChildrenSizes() {}
- virtual void refreshSizeHints(qreal effectiveSpacing);
+ virtual bool refreshSizeHints(const QLayoutStyleInfo *styleInfo);
virtual ~AnchorData() {}
@@ -180,6 +181,9 @@ struct AnchorData : public QSimplexVariable {
hasSize = false;
}
+ // Internal anchors have associated items
+ QGraphicsLayoutItem *item;
+
// Anchor is semantically directed
AnchorVertex *from;
AnchorVertex *to;
@@ -205,7 +209,9 @@ struct AnchorData : public QSimplexVariable {
uint skipInPreferred : 1;
uint type : 2; // either Normal, Sequential or Parallel
uint hasSize : 1; // if false, get size from style.
- uint isLayoutAnchor : 1; // if this anchor is connected to a layout 'edge'
+ uint isLayoutAnchor : 1; // if this anchor is an internal layout anchor
+ uint isCenterAnchor : 1;
+ uint orientation : 1;
};
#ifdef QT_DEBUG
@@ -217,26 +223,20 @@ inline QString AnchorData::toString() const
struct SequentialAnchorData : public AnchorData
{
- SequentialAnchorData() : AnchorData()
+ SequentialAnchorData(const QVector<AnchorVertex *> &vertices, const QVector<AnchorData *> &edges)
+ : AnchorData(), m_children(vertices), m_edges(edges)
{
type = AnchorData::Sequential;
+ orientation = m_edges.at(0)->orientation;
#ifdef QT_DEBUG
- name = QLatin1String("SequentialAnchorData");
+ name = QString::fromAscii("%1 -- %2").arg(vertices.first()->toString(), vertices.last()->toString());
#endif
}
virtual void updateChildrenSizes();
- virtual void refreshSizeHints(qreal effectiveSpacing);
+ virtual bool refreshSizeHints(const QLayoutStyleInfo *styleInfo);
- void refreshSizeHints_helper(qreal effectiveSpacing, bool refreshChildren = true);
-
- void setVertices(const QVector<AnchorVertex*> &vertices)
- {
- m_children = vertices;
-#ifdef QT_DEBUG
- name = QString::fromAscii("%1 -- %2").arg(vertices.first()->toString(), vertices.last()->toString());
-#endif
- }
+ bool refreshSizeHints_helper(const QLayoutStyleInfo *styleInfo, bool refreshChildren = true);
QVector<AnchorVertex*> m_children; // list of vertices in the sequence
QVector<AnchorData*> m_edges; // keep the list of edges too.
@@ -248,6 +248,7 @@ struct ParallelAnchorData : public AnchorData
: AnchorData(), firstEdge(first), secondEdge(second)
{
type = AnchorData::Parallel;
+ orientation = first->orientation;
// ### Those asserts force that both child anchors have the same direction,
// but can't we simplify a pair of anchors in opposite directions?
@@ -261,9 +262,9 @@ struct ParallelAnchorData : public AnchorData
}
virtual void updateChildrenSizes();
- virtual void refreshSizeHints(qreal effectiveSpacing);
+ virtual bool refreshSizeHints(const QLayoutStyleInfo *styleInfo);
- void refreshSizeHints_helper(qreal effectiveSpacing, bool refreshChildren = true);
+ bool refreshSizeHints_helper(const QLayoutStyleInfo *styleInfo, bool refreshChildren = true);
AnchorData* firstEdge;
AnchorData* secondEdge;
@@ -423,13 +424,12 @@ public:
Qt::AnchorPoint &firstEdge,
QGraphicsLayoutItem *&secondItem,
Qt::AnchorPoint &secondEdge);
- // for getting the actual spacing (will query the style if the
- // spacing is not explicitly set).
- qreal effectiveSpacing(Orientation orientation) const;
+
+ QLayoutStyleInfo &styleInfo() const;
// Activation methods
- void simplifyGraph(Orientation orientation);
- bool simplifyGraphIteration(Orientation orientation);
+ bool simplifyGraph(Orientation orientation);
+ bool simplifyGraphIteration(Orientation orientation, bool *feasible);
void restoreSimplifiedGraph(Orientation orientation);
void calculateGraphs();
@@ -441,7 +441,7 @@ public:
bool calculateNonTrunk(const QList<QSimplexConstraint *> &constraints,
const QList<AnchorData *> &variables);
- void setAnchorSizeHintsFromItems(Orientation orientation);
+ bool refreshAllSizeHints(Orientation orientation);
void findPaths(Orientation orientation);
void constraintsFromPaths(Orientation orientation);
void updateAnchorSizes(Orientation orientation);
@@ -524,10 +524,13 @@ public:
#endif
uint calculateGraphCacheDirty : 1;
+ mutable uint styleInfoDirty : 1;
+ mutable QLayoutStyleInfo cachedStyleInfo;
friend class QGraphicsAnchorPrivate;
};
QT_END_NAMESPACE
+#endif //QT_NO_GRAPHICSVIEW
#endif
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp
index 45627f6..d70b039 100644
--- a/src/gui/graphicsview/qgraphicsitem.cpp
+++ b/src/gui/graphicsview/qgraphicsitem.cpp
@@ -1039,13 +1039,31 @@ void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent)
}
// Update focus scope item ptr in new scope.
- if (newParent) {
+ QGraphicsItem *newFocusScopeItem = subFocusItem ? subFocusItem : parentFocusScopeItem;
+ if (newFocusScopeItem && newParent) {
+ if (subFocusItem) {
+ // Find the subFocusItem's topmost focus scope.
+ QGraphicsItem *ancestorScope = 0;
+ QGraphicsItem *p = subFocusItem->d_ptr->parent;
+ while (p) {
+ if (p->flags() & QGraphicsItem::ItemIsFocusScope)
+ ancestorScope = p;
+ if (p->isPanel())
+ break;
+ p = p->parentItem();
+ }
+ if (ancestorScope)
+ newFocusScopeItem = ancestorScope;
+ }
+
QGraphicsItem *p = newParent;
while (p) {
if (p->flags() & QGraphicsItem::ItemIsFocusScope) {
- p->d_ptr->focusScopeItem = subFocusItem ? subFocusItem : parentFocusScopeItem;
- // ### The below line might not make sense...
- if (subFocusItem)
+ p->d_ptr->focusScopeItem = newFocusScopeItem;
+ // Ensure the new item is no longer the subFocusItem. The
+ // only way to set focus on a child of a focus scope is
+ // by setting focus on the scope itself.
+ if (subFocusItem && !p->focusItem())
subFocusItem->d_ptr->clearSubFocus();
break;
}
@@ -1228,7 +1246,8 @@ void QGraphicsItemCache::purge()
}
/*!
- Constructs a QGraphicsItem with the given \a parent.
+ Constructs a QGraphicsItem with the given \a parent item.
+ It does not modify the parent object returned by QObject::parent().
If \a parent is 0, you can add the item to a scene by calling
QGraphicsScene::addItem(). The item will then become a top-level item.
@@ -1286,6 +1305,8 @@ QGraphicsItem::QGraphicsItem(QGraphicsItemPrivate &dd, QGraphicsItem *parent,
*/
QGraphicsItem::~QGraphicsItem()
{
+ if (d_ptr->isObject)
+ QObjectPrivate::get(static_cast<QGraphicsObject *>(this))->wasDeleted = true;
d_ptr->inDestructor = 1;
d_ptr->removeExtraItemCache();
@@ -1511,6 +1532,8 @@ const QGraphicsObject *QGraphicsItem::toGraphicsObject() const
the parent. You should not \l{QGraphicsScene::addItem()}{add} the
item to the scene yourself.
+ Calling this function on an item that is an ancestor of \a parent have undefined behaviour.
+
\sa parentItem(), childItems()
*/
void QGraphicsItem::setParentItem(QGraphicsItem *parent)
@@ -1655,7 +1678,7 @@ void QGraphicsItem::setFlags(GraphicsItemFlags flags)
d_ptr->scene->d_func()->index->itemChange(this, ItemFlagsChange, quint32(flags));
// Flags that alter the geometry of the item (or its children).
- const quint32 geomChangeFlagsMask = (ItemClipsChildrenToShape | ItemClipsToShape | ItemIgnoresTransformations);
+ const quint32 geomChangeFlagsMask = (ItemClipsChildrenToShape | ItemClipsToShape | ItemIgnoresTransformations | ItemIsSelectable);
bool fullUpdate = (quint32(flags) & geomChangeFlagsMask) != (d_ptr->flags & geomChangeFlagsMask);
if (fullUpdate)
d_ptr->paintedViewBoundingRectsNeedRepaint = 1;
@@ -2482,12 +2505,14 @@ void QGraphicsItem::setOpacity(qreal opacity)
itemChange(ItemOpacityHasChanged, newOpacityVariant);
// Update.
- if (d_ptr->scene)
+ if (d_ptr->scene) {
+ d_ptr->invalidateGraphicsEffectsRecursively();
d_ptr->scene->d_func()->markDirty(this, QRectF(),
/*invalidateChildren=*/true,
/*maybeDirtyClipPath=*/false,
/*force=*/false,
/*ignoreOpacity=*/true);
+ }
if (d_ptr->isObject)
emit static_cast<QGraphicsObject *>(this)->opacityChanged();
@@ -2977,8 +3002,11 @@ void QGraphicsItemPrivate::setFocusHelper(Qt::FocusReason focusReason, bool clim
while (p) {
if (p->flags() & QGraphicsItem::ItemIsFocusScope) {
p->d_ptr->focusScopeItem = q_ptr;
- if (!q_ptr->isActive() || !p->focusItem())
+ if (!p->focusItem()) {
+ // If you call setFocus on a child of a focus scope that
+ // doesn't currently have a focus item, then stop.
return;
+ }
break;
}
p = p->d_ptr->parent;
@@ -4259,7 +4287,7 @@ void QGraphicsItem::stackBefore(const QGraphicsItem *sibling)
// Only move items with the same Z value, and that need moving.
int siblingIndex = sibling->d_ptr->siblingIndex;
int myIndex = d_ptr->siblingIndex;
- if (myIndex >= siblingIndex && d_ptr->z == sibling->d_ptr->z) {
+ if (myIndex >= siblingIndex) {
siblings->move(myIndex, siblingIndex);
// Fixup the insertion ordering.
for (int i = 0; i < siblings->size(); ++i) {
@@ -4735,7 +4763,7 @@ bool QGraphicsItem::isObscuredBy(const QGraphicsItem *item) const
{
if (!item)
return false;
- return QGraphicsSceneBspTreeIndexPrivate::closestItemFirst_withoutCache(item, this)
+ return qt_closestItemFirst(item, this)
&& qt_QGraphicsItem_isObscured(this, item, boundingRect());
}
@@ -4949,6 +4977,22 @@ int QGraphicsItemPrivate::depth() const
/*!
\internal
*/
+void QGraphicsItemPrivate::invalidateGraphicsEffectsRecursively()
+{
+ QGraphicsItemPrivate *itemPrivate = this;
+ do {
+ if (itemPrivate->graphicsEffect) {
+ itemPrivate->notifyInvalidated = 1;
+
+ if (!itemPrivate->updateDueToGraphicsEffect)
+ static_cast<QGraphicsItemEffectSourcePrivate *>(itemPrivate->graphicsEffect->d_func()->source->d_func())->invalidateCache();
+ }
+ } while ((itemPrivate = itemPrivate->parent ? itemPrivate->parent->d_ptr.data() : 0));
+}
+
+/*!
+ \internal
+*/
void QGraphicsItemPrivate::invalidateDepthRecursively()
{
if (itemDepth == -1)
@@ -5280,11 +5324,7 @@ void QGraphicsItem::update(const QRectF &rect)
return;
// Make sure we notify effects about invalidated source.
- QGraphicsItem *item = this;
- do {
- if (item->d_ptr->graphicsEffect)
- item->d_ptr->notifyInvalidated = 1;
- } while ((item = item->d_ptr->parent));
+ d_ptr->invalidateGraphicsEffectsRecursively();
if (CacheMode(d_ptr->cacheMode) != NoCache) {
// Invalidate cache.
@@ -7267,6 +7307,21 @@ static void qt_graphicsItem_highlightSelected(
The class extends a QGraphicsItem with QObject's signal/slot and property mechanisms.
It maps many of QGraphicsItem's basic setters and getters to properties and adds notification
signals for many of them.
+
+ \section1 Parents and Children
+
+ Each graphics object can be constructed with a parent item. This ensures that the
+ item will be destroyed when its parent item is destroyed. Although QGraphicsObject
+ inherits from both QObject and QGraphicsItem, you should use the functions provided
+ by QGraphicsItem, \e not QObject, to manage the relationships between parent and
+ child items.
+
+ The relationships between items can be explored using the parentItem() and childItems()
+ functions. In the hierarchy of items in a scene, the parentObject() and parentWidget()
+ functions are the equivalent of the QWidget::parent() and QWidget::parentWidget()
+ functions for QWidget subclasses.
+
+ \sa QGraphicsWidget
*/
/*!
@@ -7304,6 +7359,9 @@ void QGraphicsObject::grabGesture(Qt::GestureType gesture, Qt::GestureContext co
\property QGraphicsObject::parent
\brief the parent of the item
+ \note The item's parent is set independently of the parent object returned
+ by QObject::parent().
+
\sa QGraphicsItem::setParentItem(), QGraphicsItem::parentObject()
*/
@@ -9122,10 +9180,14 @@ void QGraphicsPixmapItem::setOffset(const QPointF &offset)
QRectF QGraphicsPixmapItem::boundingRect() const
{
Q_D(const QGraphicsPixmapItem);
- qreal pw = 1.0;
if (d->pixmap.isNull())
return QRectF();
- return QRectF(d->offset, d->pixmap.size()).adjusted(-pw/2, -pw/2, pw/2, pw/2);
+ if (d->flags & ItemIsSelectable) {
+ qreal pw = 1.0;
+ return QRectF(d->offset, d->pixmap.size()).adjusted(-pw/2, -pw/2, pw/2, pw/2);
+ } else {
+ return QRectF(d->offset, d->pixmap.size());
+ }
}
/*!
@@ -9635,12 +9697,14 @@ bool QGraphicsTextItem::sceneEvent(QEvent *event)
// Reset the focus widget's input context, regardless
// of how this item gained or lost focus.
if (QWidget *fw = qApp->focusWidget()) {
+#ifndef QT_NO_IM
if (QInputContext *qic = fw->inputContext()) {
if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut)
qic->reset();
else
qic->update();
}
+#endif //QT_NO_IM
}
break;
default:
@@ -10644,7 +10708,8 @@ void QGraphicsItemEffectSourcePrivate::draw(QPainter *painter)
}
}
-QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset) const
+QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset,
+ QGraphicsEffectSource::PixmapPadMode mode) const
{
const bool deviceCoordinates = (system == Qt::DeviceCoordinates);
if (!info && deviceCoordinates) {
@@ -10658,7 +10723,17 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP
QGraphicsScenePrivate *scened = item->d_ptr->scene->d_func();
const QRectF sourceRect = boundingRect(system);
- QRect effectRect = item->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect();
+ QRect effectRect;
+
+ if (mode == QGraphicsEffectSource::ExpandToEffectRectPadMode) {
+ effectRect = item->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect();
+ } else if (mode == QGraphicsEffectSource::ExpandToTransparentBorderPadMode) {
+ // adjust by 1.5 to account for cosmetic pens
+ effectRect = sourceRect.adjusted(-1.5, -1.5, 1.5, 1.5).toAlignedRect();
+ } else {
+ effectRect = sourceRect.toAlignedRect();
+ }
+
if (offset)
*offset = effectRect.topLeft();
@@ -10686,10 +10761,15 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP
effectRect.setBottom(deviceHeight -1);
}
-
if (effectRect.isEmpty())
return QPixmap();
+ if (system == Qt::LogicalCoordinates
+ && effectRect.size() == sourceRect.size()
+ && isPixmap()) {
+ return static_cast<QGraphicsPixmapItem *>(item)->pixmap();
+ }
+
QPixmap pixmap(effectRect.size());
pixmap.fill(Qt::transparent);
QPainter pixmapPainter(&pixmap);
@@ -10721,6 +10801,7 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP
}
pixmapPainter.end();
+
return pixmap;
}
@@ -10732,14 +10813,35 @@ QDebug operator<<(QDebug debug, QGraphicsItem *item)
return debug;
}
- debug << "QGraphicsItem(this =" << ((void*)item)
- << ", parent =" << ((void*)item->parentItem())
+ if (QGraphicsObject *o = item->toGraphicsObject())
+ debug << o->metaObject()->className();
+ else
+ debug << "QGraphicsItem";
+ debug << "(this =" << (void*)item
+ << ", parent =" << (void*)item->parentItem()
<< ", pos =" << item->pos()
<< ", z =" << item->zValue() << ", flags = "
<< item->flags() << ")";
return debug;
}
+QDebug operator<<(QDebug debug, QGraphicsObject *item)
+{
+ if (!item) {
+ debug << "QGraphicsObject(0)";
+ return debug;
+ }
+
+ debug.nospace() << item->metaObject()->className() << '(' << (void*)item;
+ if (!item->objectName().isEmpty())
+ debug << ", name = " << item->objectName();
+ debug.nospace() << ", parent = " << ((void*)item->parentItem())
+ << ", pos = " << item->pos()
+ << ", z = " << item->zValue() << ", flags = "
+ << item->flags() << ')';
+ return debug.space();
+}
+
QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemChange change)
{
const char *str = "UnknownChange";
diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h
index 2665235..f3fe99c 100644
--- a/src/gui/graphicsview/qgraphicsitem.h
+++ b/src/gui/graphicsview/qgraphicsitem.h
@@ -555,7 +555,7 @@ public:
using QObject::children;
#endif
- void grabGesture(Qt::GestureType type, Qt::GestureContext context = Qt::WidgetWithChildrenGesture);
+ void grabGesture(Qt::GestureType type, Qt::GestureContext context = Qt::ItemWithChildrenGesture);
Q_SIGNALS:
void parentChanged();
@@ -1120,6 +1120,7 @@ template <class T> inline T qgraphicsitem_cast(const QGraphicsItem *item)
#ifndef QT_NO_DEBUG_STREAM
Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem *item);
+Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsObject *item);
Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemChange change);
Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag);
Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlags flags);
diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h
index 6550362..ca56c18 100644
--- a/src/gui/graphicsview/qgraphicsitem_p.h
+++ b/src/gui/graphicsview/qgraphicsitem_p.h
@@ -61,6 +61,7 @@
#include <private/qgraphicstransform_p.h>
#include <private/qgraphicseffect_p.h>
+#include <qgraphicseffect.h>
#include <QtCore/qpoint.h>
@@ -177,6 +178,7 @@ public:
wantsActive(0),
holesInSiblingIndex(0),
sequentialOrdering(1),
+ updateDueToGraphicsEffect(0),
globalStackingOrder(-1),
q_ptr(0)
{
@@ -221,6 +223,7 @@ public:
bool discardUpdateRequest(bool ignoreClipping = false, bool ignoreVisibleBit = false,
bool ignoreDirtyBit = false, bool ignoreOpacity = false) const;
int depth() const;
+ void invalidateGraphicsEffectsRecursively();
void invalidateDepthRecursively();
void resolveDepth();
void addChild(QGraphicsItem *child);
@@ -502,6 +505,7 @@ public:
quint32 wantsActive : 1;
quint32 holesInSiblingIndex : 1;
quint32 sequentialOrdering : 1;
+ quint32 updateDueToGraphicsEffect : 1;
// Optional stacking order
int globalStackingOrder;
@@ -589,16 +593,21 @@ public:
inline const QWidget *widget() const
{ return 0; }
- inline void update()
- { item->update(); }
+ inline void update() {
+ item->d_ptr->updateDueToGraphicsEffect = true;
+ item->update();
+ item->d_ptr->updateDueToGraphicsEffect = false;
+ }
inline void effectBoundingRectChanged()
{ item->prepareGeometryChange(); }
inline bool isPixmap() const
{
- return (item->type() == QGraphicsPixmapItem::Type);
- //|| (item->d_ptr->isObject && qobject_cast<QFxImage *>(q_func()));
+ return item->type() == QGraphicsPixmapItem::Type
+ && !(item->flags() & QGraphicsItem::ItemIsSelectable)
+ && item->d_ptr->children.size() == 0;
+ //|| (item->d_ptr->isObject && qobject_cast<QmlGraphicsImage *>(q_func()));
}
inline const QStyleOption *styleOption() const
@@ -615,14 +624,82 @@ public:
QRectF boundingRect(Qt::CoordinateSystem system) const;
void draw(QPainter *);
- QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset) const;
+ QPixmap pixmap(Qt::CoordinateSystem system,
+ QPoint *offset,
+ QGraphicsEffectSource::PixmapPadMode mode) const;
QGraphicsItem *item;
QGraphicsItemPaintInfo *info;
+ QTransform lastEffectTransform;
};
/*!
+ Returns true if \a item1 is on top of \a item2.
+ The items dont need to be siblings.
+
+ \internal
+*/
+inline bool qt_closestItemFirst(const QGraphicsItem *item1, const QGraphicsItem *item2)
+{
+ // Siblings? Just check their z-values.
+ const QGraphicsItemPrivate *d1 = item1->d_ptr.data();
+ const QGraphicsItemPrivate *d2 = item2->d_ptr.data();
+ if (d1->parent == d2->parent)
+ return qt_closestLeaf(item1, item2);
+
+ // Find common ancestor, and each item's ancestor closest to the common
+ // ancestor.
+ int item1Depth = d1->depth();
+ int item2Depth = d2->depth();
+ const QGraphicsItem *p = item1;
+ const QGraphicsItem *t1 = item1;
+ while (item1Depth > item2Depth && (p = p->d_ptr->parent)) {
+ if (p == item2) {
+ // item2 is one of item1's ancestors; item1 is on top
+ return !(t1->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent);
+ }
+ t1 = p;
+ --item1Depth;
+ }
+ p = item2;
+ const QGraphicsItem *t2 = item2;
+ while (item2Depth > item1Depth && (p = p->d_ptr->parent)) {
+ if (p == item1) {
+ // item1 is one of item2's ancestors; item1 is not on top
+ return (t2->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent);
+ }
+ t2 = p;
+ --item2Depth;
+ }
+
+ // item1Ancestor is now at the same level as item2Ancestor, but not the same.
+ const QGraphicsItem *p1 = t1;
+ const QGraphicsItem *p2 = t2;
+ while (t1 && t1 != t2) {
+ p1 = t1;
+ p2 = t2;
+ t1 = t1->d_ptr->parent;
+ t2 = t2->d_ptr->parent;
+ }
+
+ // in case we have a common ancestor, we compare the immediate children in the ancestor's path.
+ // otherwise we compare the respective items' topLevelItems directly.
+ return qt_closestLeaf(p1, p2);
+}
+
+/*!
+ Returns true if \a item2 is on top of \a item1.
+ The items dont need to be siblings.
+
+ \internal
+*/
+inline bool qt_closestItemLast(const QGraphicsItem *item1, const QGraphicsItem *item2)
+{
+ return qt_closestItemFirst(item2, item1);
+}
+
+/*!
\internal
*/
inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2)
@@ -642,7 +719,7 @@ inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item
/*!
\internal
*/
-static inline bool qt_notclosestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2)
+inline bool qt_notclosestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2)
{ return qt_closestLeaf(item2, item1); }
/*
diff --git a/src/gui/graphicsview/qgraphicslayout_p.h b/src/gui/graphicsview/qgraphicslayout_p.h
index 59c6dba..4aeae39 100644
--- a/src/gui/graphicsview/qgraphicslayout_p.h
+++ b/src/gui/graphicsview/qgraphicslayout_p.h
@@ -60,6 +60,8 @@
#include "qgraphicslayout.h"
#include "qgraphicslayoutitem_p.h"
#include <QtGui/qstyle.h>
+#include <QtGui/qwidget.h>
+#include <QtGui/qstyleoption.h>
QT_BEGIN_NAMESPACE
@@ -76,6 +78,55 @@ inline bool qt_graphicsLayoutDebug()
}
#endif
+
+class QLayoutStyleInfo
+{
+public:
+ inline QLayoutStyleInfo() { invalidate(); }
+ inline QLayoutStyleInfo(QStyle *style, QWidget *widget)
+ : m_valid(true), m_style(style), m_widget(widget)
+ {
+ Q_ASSERT(style);
+ if (widget) //###
+ m_styleOption.initFrom(widget);
+ m_defaultSpacing[0] = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
+ m_defaultSpacing[1] = style->pixelMetric(QStyle::PM_LayoutVerticalSpacing);
+ }
+
+ inline void invalidate() { m_valid = false; m_style = 0; m_widget = 0; }
+
+ inline QStyle *style() const { return m_style; }
+ inline QWidget *widget() const { return m_widget; }
+
+ inline bool operator==(const QLayoutStyleInfo &other)
+ { return m_style == other.m_style && m_widget == other.m_widget; }
+ inline bool operator!=(const QLayoutStyleInfo &other)
+ { return !(*this == other); }
+
+ inline void setDefaultSpacing(Qt::Orientation o, qreal spacing){
+ if (spacing >= 0)
+ m_defaultSpacing[o - 1] = spacing;
+ }
+
+ inline qreal defaultSpacing(Qt::Orientation o) const {
+ return m_defaultSpacing[o - 1];
+ }
+
+ inline qreal perItemSpacing(QSizePolicy::ControlType control1,
+ QSizePolicy::ControlType control2,
+ Qt::Orientation orientation) const
+ {
+ Q_ASSERT(style());
+ return style()->layoutSpacing(control1, control2, orientation, &m_styleOption, widget());
+ }
+private:
+ bool m_valid;
+ QStyle *m_style;
+ QWidget *m_widget;
+ QStyleOption m_styleOption;
+ qreal m_defaultSpacing[2];
+};
+
class Q_AUTOTEST_EXPORT QGraphicsLayoutPrivate : public QGraphicsLayoutItemPrivate
{
Q_DECLARE_PUBLIC(QGraphicsLayout)
diff --git a/src/gui/graphicsview/qgraphicslinearlayout.cpp b/src/gui/graphicsview/qgraphicslinearlayout.cpp
index 7ff7c9b..5684f0e 100644
--- a/src/gui/graphicsview/qgraphicslinearlayout.cpp
+++ b/src/gui/graphicsview/qgraphicslinearlayout.cpp
@@ -59,7 +59,7 @@
You can add widgets, layouts, stretches (addStretch(), insertStretch() or
setStretchFactor()), and spacings (setItemSpacing()) to a linear
- layout. The layout takes ownership of the items. In some cases when the layout
+ layout. The layout takes ownership of the items. In some cases when the layout
item also inherits from QGraphicsItem (such as QGraphicsWidget) there will be a
ambiguity in ownership because the layout item belongs to two ownership hierarchies.
See the documentation of QGraphicsLayoutItem::setOwnedByLayout() how to handle
@@ -208,7 +208,7 @@ QGraphicsLinearLayout::~QGraphicsLinearLayout()
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) {
@@ -542,18 +542,18 @@ void QGraphicsLinearLayout::invalidate()
QGraphicsLayout::invalidate();
}
-#ifdef QT_DEBUG
void QGraphicsLinearLayout::dump(int indent) const
{
+#ifdef QT_DEBUG
if (qt_graphicsLayoutDebug()) {
Q_D(const QGraphicsLinearLayout);
qDebug("%*s%s layout", indent, "",
d->orientation == Qt::Horizontal ? "Horizontal" : "Vertical");
d->engine.dump(indent + 1);
}
-}
#endif
+}
QT_END_NAMESPACE
-
+
#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicslinearlayout.h b/src/gui/graphicsview/qgraphicslinearlayout.h
index 742392e..15fe81a 100644
--- a/src/gui/graphicsview/qgraphicslinearlayout.h
+++ b/src/gui/graphicsview/qgraphicslinearlayout.h
@@ -97,9 +97,7 @@ public:
Q5SizePolicy::ControlTypes controlTypes(LayoutSide side) const;
#endif
-#ifdef QT_DEBUG
void dump(int indent = 0) const;
-#endif
protected:
#if 0
diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp
index b7a3962..e9173a9 100644
--- a/src/gui/graphicsview/qgraphicsproxywidget.cpp
+++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp
@@ -57,6 +57,9 @@
#include <QtGui/qpainter.h>
#include <QtGui/qstyleoption.h>
#include <QtGui/qgraphicsview.h>
+#include <QtGui/qlistview.h>
+#include <QtGui/qlineedit.h>
+#include <QtGui/qtextedit.h>
QT_BEGIN_NAMESPACE
@@ -86,7 +89,9 @@ QT_BEGIN_NAMESPACE
of embedded widgets through creating a child proxy for each popup. This
means that when an embedded QComboBox shows its popup list, a new
QGraphicsProxyWidget is created automatically, embedding the popup, and
- positioning it correctly.
+ positioning it correctly. This only works if the popup is child of the
+ embedded widget (for example QToolButton::setMenu() requires the QMenu instance
+ to be child of the QToolButton).
\section1 Embedding a Widget with QGraphicsProxyWidget
@@ -184,6 +189,7 @@ QT_BEGIN_NAMESPACE
*/
extern bool qt_sendSpontaneousEvent(QObject *, QEvent *);
+extern bool qt_tab_all_widgets;
/*!
\internal
@@ -369,6 +375,7 @@ QVariant QGraphicsProxyWidgetPrivate::inputMethodQueryHelper(Qt::InputMethodQuer
/*!
\internal
+ Some of the logic is shared with QApplicationPrivate::focusNextPrevChild_helper
*/
QWidget *QGraphicsProxyWidgetPrivate::findFocusChild(QWidget *child, bool next) const
{
@@ -382,14 +389,16 @@ QWidget *QGraphicsProxyWidgetPrivate::findFocusChild(QWidget *child, bool next)
child = next ? child->d_func()->focus_next : child->d_func()->focus_prev;
if ((next && child == widget) || (!next && child == widget->d_func()->focus_prev)) {
return 0;
- }
+ }
}
QWidget *oldChild = child;
+ uint focus_flag = qt_tab_all_widgets ? Qt::TabFocus : Qt::StrongFocus;
do {
if (child->isEnabled()
&& child->isVisibleTo(widget)
- && (child->focusPolicy() & Qt::TabFocus)) {
+ && ((child->focusPolicy() & focus_flag) == focus_flag)
+ && !(child->d_func()->extra && child->d_func()->extra->focus_proxy)) {
return child;
}
child = next ? child->d_func()->focus_next : child->d_func()->focus_prev;
diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp
index a624b10..f982f4b 100644
--- a/src/gui/graphicsview/qgraphicsscene.cpp
+++ b/src/gui/graphicsview/qgraphicsscene.cpp
@@ -242,7 +242,6 @@
#include <QtGui/qstyleoption.h>
#include <QtGui/qtooltip.h>
#include <QtGui/qtransform.h>
-#include <QtGui/qgesture.h>
#include <QtGui/qinputcontext.h>
#include <QtGui/qgraphicseffect.h>
#include <private/qapplication_p.h>
@@ -251,6 +250,14 @@
#include <private/qt_x11_p.h>
#endif
#include <private/qgraphicseffect_p.h>
+#include <private/qgesturemanager_p.h>
+
+// #define GESTURE_DEBUG
+#ifndef GESTURE_DEBUG
+# define DEBUG if (0) qDebug
+#else
+# define DEBUG qDebug
+#endif
QT_BEGIN_NAMESPACE
@@ -518,6 +525,14 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item)
item->d_func()->scene = 0;
+ //We need to remove all children first because they might use their parent
+ //attributes (e.g. sceneTransform).
+ if (!item->d_ptr->inDestructor) {
+ // Remove all children recursively
+ for (int i = 0; i < item->d_ptr->children.size(); ++i)
+ q->removeItem(item->d_ptr->children.at(i));
+ }
+
// Unregister focus proxy.
item->d_ptr->resetFocusProxy();
@@ -564,12 +579,6 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item)
++iterator;
}
- if (!item->d_ptr->inDestructor) {
- // Remove all children recursively
- for (int i = 0; i < item->d_ptr->children.size(); ++i)
- q->removeItem(item->d_ptr->children.at(i));
- }
-
if (item->isPanel() && item->isVisible() && item->panelModality() != QGraphicsItem::NonModal)
leaveModal(item);
@@ -684,6 +693,7 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item,
focusItem = 0;
sendEvent(lastFocusItem, &event);
+#ifndef QT_NO_IM
if (lastFocusItem
&& (lastFocusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod)) {
// Reset any visible preedit text
@@ -699,6 +709,7 @@ void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item,
views.at(i)->inputContext()->reset();
}
}
+#endif //QT_NO_IM
}
if (item) {
@@ -1052,6 +1063,14 @@ bool QGraphicsScenePrivate::filterEvent(QGraphicsItem *item, QEvent *event)
*/
bool QGraphicsScenePrivate::sendEvent(QGraphicsItem *item, QEvent *event)
{
+ if (QGraphicsObject *object = item->toGraphicsObject()) {
+ QApplicationPrivate *qAppPriv = QApplicationPrivate::instance();
+ if (qAppPriv->gestureManager) {
+ if (qAppPriv->gestureManager->filterEvent(object, event))
+ return true;
+ }
+ }
+
if (filterEvent(item, event))
return false;
if (filterDescendantEvent(item, event))
@@ -3365,6 +3384,10 @@ bool QGraphicsScene::event(QEvent *event)
case QEvent::TouchEnd:
d->touchEventHandler(static_cast<QTouchEvent *>(event));
break;
+ case QEvent::Gesture:
+ case QEvent::GestureOverride:
+ d->gestureEventHandler(static_cast<QGestureEvent *>(event));
+ break;
default:
return QObject::event(event);
}
@@ -4569,6 +4592,11 @@ void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *
else
painter->setWorldTransform(*transformPtr);
painter->setOpacity(opacity);
+
+ if (sourced->lastEffectTransform != painter->worldTransform()) {
+ sourced->lastEffectTransform = painter->worldTransform();
+ sourced->invalidateCache();
+ }
item->d_ptr->graphicsEffect->draw(painter, source);
painter->setWorldTransform(restoreTransform);
sourced->info = 0;
@@ -4886,7 +4914,7 @@ void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool
continue;
}
- if (item->d_ptr->paintedViewBoundingRectsNeedRepaint && !paintedViewBoundingRect.isEmpty()) {
+ if (item->d_ptr->paintedViewBoundingRectsNeedRepaint) {
paintedViewBoundingRect.translate(viewPrivate->dirtyScrollOffset);
if (!viewPrivate->updateRect(paintedViewBoundingRect))
paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport.
@@ -5699,6 +5727,238 @@ void QGraphicsScenePrivate::leaveModal(QGraphicsItem *panel)
dispatchHoverEvent(&hoverEvent);
}
+void QGraphicsScenePrivate::getGestureTargets(const QSet<QGesture *> &gestures,
+ QWidget *viewport,
+ QMap<Qt::GestureType, QGesture *> *conflictedGestures,
+ QList<QList<QGraphicsObject *> > *conflictedItems,
+ QHash<QGesture *, QGraphicsObject *> *normalGestures)
+{
+ foreach (QGesture *gesture, gestures) {
+ Qt::GestureType gestureType = gesture->gestureType();
+ if (gesture->hasHotSpot()) {
+ QPoint screenPos = gesture->hotSpot().toPoint();
+ QList<QGraphicsItem *> items = itemsAtPosition(screenPos, QPointF(), viewport);
+ QList<QGraphicsObject *> result;
+ for (int j = 0; j < items.size(); ++j) {
+ QGraphicsObject *item = items.at(j)->toGraphicsObject();
+ if (!item)
+ continue;
+ QGraphicsItemPrivate *d = item->QGraphicsItem::d_func();
+ if (d->gestureContext.contains(gestureType)) {
+ result.append(item);
+ }
+ }
+ DEBUG() << "QGraphicsScenePrivate::getGestureTargets:"
+ << gesture << result;
+ if (result.size() == 1) {
+ normalGestures->insert(gesture, result.first());
+ } else if (!result.isEmpty()) {
+ conflictedGestures->insert(gestureType, gesture);
+ conflictedItems->append(result);
+ }
+ }
+ }
+}
+
+void QGraphicsScenePrivate::gestureEventHandler(QGestureEvent *event)
+{
+ QWidget *viewport = event->widget();
+ if (!viewport)
+ return;
+ QList<QGesture *> allGestures = event->allGestures();
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "Delivering gestures:" << allGestures;
+
+ typedef QHash<QGraphicsObject *, QList<QGesture *> > GesturesPerItem;
+ GesturesPerItem gesturesPerItem;
+
+ QSet<QGesture *> startedGestures;
+ foreach (QGesture *gesture, allGestures) {
+ QGraphicsObject *target = gestureTargets.value(gesture, 0);
+ if (!target) {
+ // when we are not in started mode but don't have a target
+ // then the only one interested in gesture is the view/scene
+ if (gesture->state() == Qt::GestureStarted)
+ startedGestures.insert(gesture);
+ } else {
+ gesturesPerItem[target].append(gesture);
+ }
+ }
+
+ QMap<Qt::GestureType, QGesture *> conflictedGestures;
+ QList<QList<QGraphicsObject *> > conflictedItems;
+ QHash<QGesture *, QGraphicsObject *> normalGestures;
+ getGestureTargets(startedGestures, viewport, &conflictedGestures, &conflictedItems,
+ &normalGestures);
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "Conflicting gestures:" << conflictedGestures.values() << conflictedItems;
+ Q_ASSERT((conflictedGestures.isEmpty() && conflictedItems.isEmpty()) ||
+ (!conflictedGestures.isEmpty() && !conflictedItems.isEmpty()));
+
+ // gestures that were sent as override events, but no one accepted them
+ QHash<QGesture *, QGraphicsObject *> ignoredConflictedGestures;
+
+ // deliver conflicted gestures as override events first
+ while (!conflictedGestures.isEmpty() && !conflictedItems.isEmpty()) {
+ // get the topmost item to deliver the override event
+ Q_ASSERT(!conflictedItems.isEmpty());
+ Q_ASSERT(!conflictedItems.first().isEmpty());
+ QGraphicsObject *topmost = conflictedItems.first().first();
+ for (int i = 1; i < conflictedItems.size(); ++i) {
+ QGraphicsObject *item = conflictedItems.at(i).first();
+ if (qt_closestItemFirst(item, topmost)) {
+ topmost = item;
+ }
+ }
+ // get a list of gestures to send to the item
+ QList<Qt::GestureType> grabbedGestures =
+ topmost->QGraphicsItem::d_func()->gestureContext.keys();
+ QList<QGesture *> gestures;
+ for (int i = 0; i < grabbedGestures.size(); ++i) {
+ if (QGesture *g = conflictedGestures.value(grabbedGestures.at(i), 0)) {
+ gestures.append(g);
+ if (!ignoredConflictedGestures.contains(g))
+ ignoredConflictedGestures.insert(g, topmost);
+ }
+ }
+
+ // send gesture override to the topmost item
+ QGestureEvent ev(gestures);
+ ev.t = QEvent::GestureOverride;
+ ev.setWidget(event->widget());
+ // mark event and individual gestures as ignored
+ ev.ignore();
+ foreach(QGesture *g, gestures)
+ ev.setAccepted(g, false);
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "delivering override to"
+ << topmost << gestures;
+ sendEvent(topmost, &ev);
+ // mark all accepted gestures to deliver them as normal gesture events
+ foreach (QGesture *g, gestures) {
+ if (ev.isAccepted() || ev.isAccepted(g)) {
+ conflictedGestures.remove(g->gestureType());
+ gestureTargets.remove(g);
+ // add the gesture to the list of normal delivered gestures
+ normalGestures.insert(g, topmost);
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "override was accepted:"
+ << g << topmost;
+ ignoredConflictedGestures.remove(g);
+ }
+ }
+ // remove the item that we've already delivered from the list
+ for (int i = 0; i < conflictedItems.size(); ) {
+ QList<QGraphicsObject *> &items = conflictedItems[i];
+ if (items.first() == topmost) {
+ items.removeFirst();
+ if (items.isEmpty()) {
+ conflictedItems.removeAt(i);
+ continue;
+ }
+ }
+ ++i;
+ }
+ }
+
+ // put back those started gestures that are not in the conflicted state
+ // and remember their targets
+ QHash<QGesture *, QGraphicsObject *>::const_iterator it = normalGestures.begin(),
+ e = normalGestures.end();
+ for (; it != e; ++it) {
+ QGesture *g = it.key();
+ QGraphicsObject *receiver = it.value();
+ Q_ASSERT(!gestureTargets.contains(g));
+ gestureTargets.insert(g, receiver);
+ gesturesPerItem[receiver].append(g);
+ }
+ it = ignoredConflictedGestures.begin();
+ e = ignoredConflictedGestures.end();
+ for (; it != e; ++it) {
+ QGesture *g = it.key();
+ QGraphicsObject *receiver = it.value();
+ Q_ASSERT(!gestureTargets.contains(g));
+ gestureTargets.insert(g, receiver);
+ gesturesPerItem[receiver].append(g);
+ }
+
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "Started gestures:" << normalGestures.keys()
+ << "All gestures:" << gesturesPerItem.values();
+
+ // deliver all events
+ QList<QGesture *> alreadyIgnoredGestures;
+ QHash<QGraphicsObject *, QSet<QGesture *> > itemIgnoredGestures;
+ QList<QGraphicsObject *> targetItems = gesturesPerItem.keys();
+ qSort(targetItems.begin(), targetItems.end(), qt_closestItemFirst);
+ for (int i = 0; i < targetItems.size(); ++i) {
+ QGraphicsObject *item = targetItems.at(i);
+ QList<QGesture *> gestures = gesturesPerItem.value(item);
+ // remove gestures that were already delivered once and were ignored
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "already ignored gestures for item"
+ << item << ":" << itemIgnoredGestures.value(item);
+
+ if (itemIgnoredGestures.contains(item)) // don't deliver twice to the same item
+ continue;
+
+ QGraphicsItemPrivate *gid = item->QGraphicsItem::d_func();
+ foreach(QGesture *g, alreadyIgnoredGestures) {
+ if (gid->gestureContext.contains(g->gestureType()))
+ gestures += g;
+ }
+ if (gestures.isEmpty())
+ continue;
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "delivering to"
+ << item << gestures;
+ QGestureEvent ev(gestures);
+ ev.setWidget(event->widget());
+ sendEvent(item, &ev);
+ QSet<QGesture *> ignoredGestures;
+ foreach (QGesture *g, gestures) {
+ if (!ev.isAccepted() && !ev.isAccepted(g))
+ ignoredGestures.insert(g);
+ }
+ if (!ignoredGestures.isEmpty()) {
+ // get a list of items under the (current) hotspot of each ignored
+ // gesture and start delivery again from the beginning
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "item has ignored the event, will propagate."
+ << item << ignoredGestures;
+ itemIgnoredGestures[item] += ignoredGestures;
+ QMap<Qt::GestureType, QGesture *> conflictedGestures;
+ QList<QList<QGraphicsObject *> > itemsForConflictedGestures;
+ QHash<QGesture *, QGraphicsObject *> normalGestures;
+ getGestureTargets(ignoredGestures, viewport,
+ &conflictedGestures, &itemsForConflictedGestures,
+ &normalGestures);
+ QSet<QGraphicsObject *> itemsSet = targetItems.toSet();
+ for (int k = 0; k < itemsForConflictedGestures.size(); ++k)
+ itemsSet += itemsForConflictedGestures.at(k).toSet();
+ targetItems = itemsSet.toList();
+ qSort(targetItems.begin(), targetItems.end(), qt_closestItemFirst);
+ alreadyIgnoredGestures = conflictedGestures.values();
+ DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
+ << "new targets:" << targetItems;
+ i = -1; // start delivery again
+ continue;
+ }
+ }
+
+ // forget about targets for gestures that have ended
+ foreach (QGesture *g, allGestures) {
+ switch (g->state()) {
+ case Qt::GestureFinished:
+ case Qt::GestureCanceled:
+ gestureTargets.remove(g);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
QT_END_NAMESPACE
#include "moc_qgraphicsscene.cpp"
diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h
index 8073695..cd20fd0 100644
--- a/src/gui/graphicsview/qgraphicsscene_p.h
+++ b/src/gui/graphicsview/qgraphicsscene_p.h
@@ -282,6 +282,13 @@ public:
bool allItemsIgnoreTouchEvents;
void enableTouchEventsOnViews();
+ QHash<QGesture *, QGraphicsObject *> gestureTargets;
+ void gestureEventHandler(QGestureEvent *event);
+ void getGestureTargets(const QSet<QGesture *> &gestures, QWidget *viewport,
+ QMap<Qt::GestureType, QGesture *> *conflictedGestures,
+ QList<QList<QGraphicsObject *> > *conflictedItems,
+ QHash<QGesture *, QGraphicsObject *> *normalGestures);
+
void updateInputMethodSensitivityInViews();
QList<QGraphicsItem *> modalPanels;
diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp
index e21183a..47ae3f1 100644
--- a/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp
+++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex.cpp
@@ -405,70 +405,6 @@ QList<QGraphicsItem *> QGraphicsSceneBspTreeIndexPrivate::estimateItems(const QR
}
/*!
- Returns true if \a item1 is on top of \a item2.
-
- \internal
-*/
-bool QGraphicsSceneBspTreeIndexPrivate::closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2)
-{
- // Siblings? Just check their z-values.
- const QGraphicsItemPrivate *d1 = item1->d_ptr.data();
- const QGraphicsItemPrivate *d2 = item2->d_ptr.data();
- if (d1->parent == d2->parent)
- return qt_closestLeaf(item1, item2);
-
- // Find common ancestor, and each item's ancestor closest to the common
- // ancestor.
- int item1Depth = d1->depth();
- int item2Depth = d2->depth();
- const QGraphicsItem *p = item1;
- const QGraphicsItem *t1 = item1;
- while (item1Depth > item2Depth && (p = p->d_ptr->parent)) {
- if (p == item2) {
- // item2 is one of item1's ancestors; item1 is on top
- return !(t1->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent);
- }
- t1 = p;
- --item1Depth;
- }
- p = item2;
- const QGraphicsItem *t2 = item2;
- while (item2Depth > item1Depth && (p = p->d_ptr->parent)) {
- if (p == item1) {
- // item1 is one of item2's ancestors; item1 is not on top
- return (t2->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent);
- }
- t2 = p;
- --item2Depth;
- }
-
- // item1Ancestor is now at the same level as item2Ancestor, but not the same.
- const QGraphicsItem *a1 = t1;
- const QGraphicsItem *a2 = t2;
- while (a1) {
- const QGraphicsItem *p1 = a1;
- const QGraphicsItem *p2 = a2;
- a1 = a1->parentItem();
- a2 = a2->parentItem();
- if (a1 && a1 == a2)
- return qt_closestLeaf(p1, p2);
- }
-
- // No common ancestor? Then just compare the items' toplevels directly.
- return qt_closestLeaf(t1->topLevelItem(), t2->topLevelItem());
-}
-
-/*!
- Returns true if \a item2 is on top of \a item1.
-
- \internal
-*/
-bool QGraphicsSceneBspTreeIndexPrivate::closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2)
-{
- return closestItemFirst_withoutCache(item2, item1);
-}
-
-/*!
Sort a list of \a itemList in a specific \a order and use the cache if requested.
\internal
@@ -495,9 +431,9 @@ void QGraphicsSceneBspTreeIndexPrivate::sortItems(QList<QGraphicsItem *> *itemLi
}
} else {
if (order == Qt::DescendingOrder) {
- qSort(itemList->begin(), itemList->end(), closestItemFirst_withoutCache);
+ qSort(itemList->begin(), itemList->end(), qt_closestItemFirst);
} else if (order == Qt::AscendingOrder) {
- qSort(itemList->begin(), itemList->end(), closestItemLast_withoutCache);
+ qSort(itemList->begin(), itemList->end(), qt_closestItemLast);
}
}
}
diff --git a/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h b/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h
index 0a86bb7..c130190 100644
--- a/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h
+++ b/src/gui/graphicsview/qgraphicsscenebsptreeindex_p.h
@@ -145,8 +145,6 @@ public:
QList<QGraphicsItem *> estimateItems(const QRectF &, Qt::SortOrder, bool b = false);
static void climbTree(QGraphicsItem *item, int *stackingOrder);
- static bool closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2);
- static bool closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2);
static inline bool closestItemFirst_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2)
{
diff --git a/src/gui/graphicsview/qgraphicstransform.cpp b/src/gui/graphicsview/qgraphicstransform.cpp
index ec1a2f5..83bc9e1 100644
--- a/src/gui/graphicsview/qgraphicstransform.cpp
+++ b/src/gui/graphicsview/qgraphicstransform.cpp
@@ -69,6 +69,9 @@
objects are applied to a QGraphicsItem, all of the transformations
are computed in true 3D space, with the projection back to 2D
only occurring after the last QGraphicsTransform is applied.
+ The exception to this is QGraphicsRotation, which projects back to
+ 2D after each rotation to preserve the perspective effect around
+ the X and Y axes.
If you want to create your own configurable transformation, you can create
a subclass of QGraphicsTransform (or any or the existing subclasses), and
@@ -90,8 +93,8 @@
#include <QDebug>
#include <QtCore/qmath.h>
+#ifndef QT_NO_GRAPHICSVIEW
QT_BEGIN_NAMESPACE
-
void QGraphicsTransformPrivate::setItem(QGraphicsItem *i)
{
if (item == i)
@@ -547,7 +550,7 @@ void QGraphicsRotation::applyTo(QMatrix4x4 *matrix) const
return;
matrix->translate(d->origin);
- matrix->rotate(d->angle, d->axis.x(), d->axis.y(), d->axis.z());
+ matrix->projectedRotate(d->angle, d->axis.x(), d->axis.y(), d->axis.z());
matrix->translate(-d->origin);
}
@@ -562,3 +565,4 @@ void QGraphicsRotation::applyTo(QMatrix4x4 *matrix) const
#include "moc_qgraphicstransform.cpp"
QT_END_NAMESPACE
+#endif //QT_NO_GRAPHICSVIEW
diff --git a/src/gui/graphicsview/qgraphicstransform.h b/src/gui/graphicsview/qgraphicstransform.h
index 58075aa..58e3077 100644
--- a/src/gui/graphicsview/qgraphicstransform.h
+++ b/src/gui/graphicsview/qgraphicstransform.h
@@ -47,6 +47,7 @@
#include <QtGui/QTransform>
#include <QtGui/QMatrix4x4>
+#ifndef QT_NO_GRAPHICSVIEW
QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE
@@ -150,5 +151,6 @@ private:
QT_END_NAMESPACE
QT_END_HEADER
+#endif //QT_NO_GRAPHICSVIEW
#endif // QFXTRANSFORM_H
diff --git a/src/gui/graphicsview/qgraphicstransform_p.h b/src/gui/graphicsview/qgraphicstransform_p.h
index 9e708b2..ddf99bb 100644
--- a/src/gui/graphicsview/qgraphicstransform_p.h
+++ b/src/gui/graphicsview/qgraphicstransform_p.h
@@ -54,7 +54,7 @@
//
#include "private/qobject_p.h"
-
+#ifndef QT_NO_GRAPHICSVIEW
QT_BEGIN_NAMESPACE
class QGraphicsItem;
@@ -73,5 +73,6 @@ public:
};
QT_END_NAMESPACE
+#endif //QT_NO_GRAPHCISVIEW
#endif // QGRAPHICSTRANSFORM_P_H
diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp
index 32747cc..c88f678 100644
--- a/src/gui/graphicsview/qgraphicsview.cpp
+++ b/src/gui/graphicsview/qgraphicsview.cpp
@@ -281,6 +281,7 @@ static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime <
#include <QtGui/qstyleoption.h>
#include <QtGui/qinputcontext.h>
#ifdef Q_WS_X11
+#include <QtGui/qpaintengine.h>
#include <private/qt_x11_p.h>
#endif
@@ -2701,6 +2702,19 @@ bool QGraphicsView::viewportEvent(QEvent *event)
return true;
}
+ case QEvent::Gesture:
+ case QEvent::GestureOverride:
+ {
+ if (!isEnabled())
+ return false;
+
+ if (d->scene && d->sceneInteractionAllowed) {
+ QGestureEvent *gestureEvent = static_cast<QGestureEvent *>(event);
+ gestureEvent->setWidget(viewport());
+ (void) QApplication::sendEvent(d->scene, gestureEvent);
+ }
+ return true;
+ }
default:
break;
}
@@ -3281,7 +3295,12 @@ void QGraphicsView::paintEvent(QPaintEvent *event)
backgroundPainter.setClipRegion(d->backgroundPixmapExposed, Qt::ReplaceClip);
if (viewTransformed)
backgroundPainter.setTransform(viewTransform);
- backgroundPainter.setCompositionMode(QPainter::CompositionMode_Source);
+#ifdef Q_WS_X11
+#undef X11
+ if (backgroundPainter.paintEngine()->type() != QPaintEngine::X11)
+#define X11 qt_x11Data
+#endif
+ backgroundPainter.setCompositionMode(QPainter::CompositionMode_Source);
drawBackground(&backgroundPainter, exposedSceneRect);
d->backgroundPixmapExposed = QRegion();
}
@@ -3306,6 +3325,14 @@ void QGraphicsView::paintEvent(QPaintEvent *event)
if (!(d->optimizationFlags & IndirectPainting)) {
d->scene->d_func()->drawItems(&painter, viewTransformed ? &viewTransform : 0,
&d->exposedRegion, viewport());
+ // Make sure the painter's world transform is restored correctly when
+ // drawing without painter state protection (DontSavePainterState).
+ // We only change the worldTransform() so there's no need to do a full-blown
+ // save() and restore(). Also note that we don't have to do this in case of
+ // IndirectPainting (the else branch), because in that case we always save()
+ // and restore() in QGraphicsScene::drawItems().
+ if (!d->scene->d_func()->painterStateProtection)
+ painter.setWorldTransform(viewTransform);
} else {
// Find all exposed items
bool allItems = false;
diff --git a/src/gui/graphicsview/qgraphicswidget.cpp b/src/gui/graphicsview/qgraphicswidget.cpp
index 35a3c13..d70a281 100644
--- a/src/gui/graphicsview/qgraphicswidget.cpp
+++ b/src/gui/graphicsview/qgraphicswidget.cpp
@@ -1352,6 +1352,8 @@ void QGraphicsWidget::changeEvent(QEvent *event)
case QEvent::StyleChange:
// ### Don't unset if the margins are explicitly set.
unsetWindowFrameMargins();
+ if (d->layout)
+ d->layout->invalidate();
case QEvent::FontChange:
update();
updateGeometry();
diff --git a/src/gui/graphicsview/qgridlayoutengine_p.h b/src/gui/graphicsview/qgridlayoutengine_p.h
index a42a537..ed335a8 100644
--- a/src/gui/graphicsview/qgridlayoutengine_p.h
+++ b/src/gui/graphicsview/qgridlayoutengine_p.h
@@ -59,7 +59,7 @@
#include "qmap.h"
#include "qpair.h"
#include "qvector.h"
-
+#include "qgraphicslayout_p.h"
#include <float.h>
QT_BEGIN_NAMESPACE
@@ -128,29 +128,6 @@ public:
};
-class QLayoutStyleInfo
-{
-public:
- inline QLayoutStyleInfo() { invalidate(); }
- inline QLayoutStyleInfo(QStyle *style, QWidget *widget)
- : q_valid(true), q_style(style), q_widget(widget) {}
-
- inline void invalidate() { q_valid = false; q_style = 0; q_widget = 0; }
-
- inline QStyle *style() const { return q_style; }
- inline QWidget *widget() const { return q_widget; }
-
- inline bool operator==(const QLayoutStyleInfo &other)
- { return q_style == other.q_style && q_widget == other.q_widget; }
- inline bool operator!=(const QLayoutStyleInfo &other)
- { return !(*this == other); }
-
-private:
- bool q_valid;
- QStyle *q_style;
- QWidget *q_widget;
-};
-
class QGridLayoutBox
{
public:
diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp
index b8f8fb4..86b10b4 100644
--- a/src/gui/graphicsview/qsimplex_p.cpp
+++ b/src/gui/graphicsview/qsimplex_p.cpp
@@ -108,10 +108,8 @@ void QSimplex::clearDataStructures()
// Constraints
for (int i = 0; i < constraints.size(); ++i) {
delete constraints[i]->helper.first;
- constraints[i]->helper.first = 0;
- constraints[i]->helper.second = 0.0;
delete constraints[i]->artificial;
- constraints[i]->artificial = 0;
+ delete constraints[i];
}
constraints.clear();
@@ -137,7 +135,23 @@ bool QSimplex::setConstraints(const QList<QSimplexConstraint *> newConstraints)
if (newConstraints.isEmpty())
return true; // we are ok with no constraints
- constraints = newConstraints;
+
+ // Make deep copy of constraints. We need this copy because we may change
+ // them in the simplification method.
+ for (int i = 0; i < newConstraints.size(); ++i) {
+ QSimplexConstraint *c = new QSimplexConstraint;
+ c->constant = newConstraints[i]->constant;
+ c->ratio = newConstraints[i]->ratio;
+ c->variables = newConstraints[i]->variables;
+ constraints << c;
+ }
+
+ // Remove constraints of type Var == K and replace them for their value.
+ if (!simplifyConstraints(&constraints)) {
+ qWarning() << "QSimplex: No feasible solution!";
+ clearDataStructures();
+ return false;
+ }
///////////////////////////////////////
// Prepare variables and constraints //
@@ -508,11 +522,21 @@ qreal QSimplex::solver(solverFactor factor)
// Remove old objective
clearRow(0);
- // Set new objective
+ // Set new objective in the first row of the simplex matrix
+ qreal resultOffset = 0;
QHash<QSimplexVariable *, qreal>::const_iterator iter;
for (iter = objective->variables.constBegin();
iter != objective->variables.constEnd();
++iter) {
+
+ // Check if the variable was removed in the simplification process.
+ // If so, we save its offset to the objective function and skip adding
+ // it to the matrix.
+ if (iter.key()->index == -1) {
+ resultOffset += iter.value() * iter.key()->result;
+ continue;
+ }
+
setValueAt(0, iter.key()->index, -1 * factor * iter.value());
}
@@ -525,7 +549,9 @@ qreal QSimplex::solver(solverFactor factor)
}
#endif
- return factor * valueAt(0, columns - 1);
+ // Return the value calculated by the simplex plus the value of the
+ // fixed variables.
+ return (factor * valueAt(0, columns - 1)) + resultOffset;
}
/*!
@@ -571,4 +597,77 @@ void QSimplex::collectResults()
}
}
+/*!
+ \internal
+
+ Looks for single-valued variables and remove them from the constraints list.
+*/
+bool QSimplex::simplifyConstraints(QList<QSimplexConstraint *> *constraints)
+{
+ QHash<QSimplexVariable *, qreal> results; // List of single-valued variables
+ bool modified = true; // Any chance more optimization exists?
+
+ while (modified) {
+ modified = false;
+
+ // For all constraints
+ QList<QSimplexConstraint *>::iterator iter = constraints->begin();
+ while (iter != constraints->end()) {
+ QSimplexConstraint *c = *iter;
+ if ((c->ratio == QSimplexConstraint::Equal) && (c->variables.count() == 1)) {
+ // Check whether this is a constraint of type Var == K
+ // If so, save its value to "results".
+ QSimplexVariable *variable = c->variables.constBegin().key();
+ qreal result = c->constant / c->variables.value(variable);
+
+ results.insert(variable, result);
+ variable->result = result;
+ variable->index = -1;
+ modified = true;
+
+ }
+
+ // Replace known values among their variables
+ QHash<QSimplexVariable *, qreal>::const_iterator r;
+ for (r = results.constBegin(); r != results.constEnd(); ++r) {
+ if (c->variables.contains(r.key())) {
+ c->constant -= r.value() * c->variables.take(r.key());
+ modified = true;
+ }
+ }
+
+ // Keep it normalized
+ if (c->constant < 0)
+ c->invert();
+
+ if (c->variables.isEmpty()) {
+ // If constraint became empty due to substitution, delete it.
+ if (c->isSatisfied() == false)
+ // We must ensure that the constraint soon to be deleted would not
+ // make the problem unfeasible if left behind. If that's the case,
+ // we return false so the simplex solver can properly report that.
+ return false;
+
+ delete c;
+ iter = constraints->erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+ }
+
+ return true;
+}
+
+void QSimplexConstraint::invert()
+{
+ constant = -constant;
+ ratio = Ratio(2 - ratio);
+
+ QHash<QSimplexVariable *, qreal>::iterator iter;
+ for (iter = variables.begin(); iter != variables.end(); ++iter) {
+ iter.value() = -iter.value();
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/gui/graphicsview/qsimplex_p.h b/src/gui/graphicsview/qsimplex_p.h
index 51991a9..084ad7f 100644
--- a/src/gui/graphicsview/qsimplex_p.h
+++ b/src/gui/graphicsview/qsimplex_p.h
@@ -63,7 +63,7 @@ struct QSimplexVariable
QSimplexVariable() : result(0), index(0) {}
qreal result;
- uint index;
+ int index;
};
@@ -95,7 +95,8 @@ struct QSimplexConstraint
QPair<QSimplexVariable *, qreal> helper;
QSimplexVariable * artificial;
-#ifdef QT_DEBUG
+ void invert();
+
bool isSatisfied() {
qreal leftHandSide(0);
@@ -106,7 +107,7 @@ struct QSimplexConstraint
Q_ASSERT(constant > 0 || qFuzzyCompare(1, 1 + constant));
- if (qFuzzyCompare(1000 + leftHandSide, 1000 + constant))
+ if ((leftHandSide == constant) || qFuzzyCompare(1000 + leftHandSide, 1000 + constant))
return true;
switch (ratio) {
@@ -118,6 +119,30 @@ struct QSimplexConstraint
return false;
}
}
+
+#ifdef QT_DEBUG
+ QString toString() {
+ QString result;
+ result += QString::fromAscii("-- QSimplexConstraint %1 --").arg(quintptr(this), 0, 16);
+
+ QHash<QSimplexVariable *, qreal>::const_iterator iter;
+ for (iter = variables.constBegin(); iter != variables.constEnd(); ++iter) {
+ result += QString::fromAscii(" %1 x %2").arg(iter.value()).arg(quintptr(iter.key()), 0, 16);
+ }
+
+ switch (ratio) {
+ case LessOrEqual:
+ result += QString::fromAscii(" (less) <= %1").arg(constant);
+ break;
+ case MoreOrEqual:
+ result += QString::fromAscii(" (more) >= %1").arg(constant);
+ break;
+ default:
+ result += QString::fromAscii(" (eqal) == %1").arg(constant);
+ }
+
+ return result;
+ }
#endif
};
@@ -129,7 +154,6 @@ public:
qreal solveMin();
qreal solveMax();
- QList<QSimplexVariable *> constraintsVariables();
bool setConstraints(const QList<QSimplexConstraint *> constraints);
void setObjective(QSimplexConstraint *objective);
@@ -145,6 +169,7 @@ private:
void combineRows(int toIndex, int fromIndex, qreal factor);
// Simplex
+ bool simplifyConstraints(QList<QSimplexConstraint *> *constraints);
int findPivotColumn();
int pivotRowForColumn(int column);
void reducedRowEchelon();
@@ -168,11 +193,6 @@ private:
qreal *matrix;
};
-inline QList<QSimplexVariable *> QSimplex::constraintsVariables()
-{
- return variables;
-}
-
inline qreal QSimplex::valueAt(int rowIndex, int columnIndex)
{
return matrix[rowIndex * columns + columnIndex];