From a3b7580944d22b4f3f59bc6a4828a7b01a26407f Mon Sep 17 00:00:00 2001 From: Jesus Sanchez-Palencia Date: Thu, 28 May 2009 11:45:33 -0300 Subject: QGraph: Adding the graph structure This file was originally included by Alexis Menard but now it has been fully updated to the new QGraphicsAnchorLayout needs. Signed-off-by: Anselmo Lacerda S. de Melo Signed-off-by: Caio Marcelo de Oliveira Filho Signed-off-by: Eduardo M. Fleury Signed-off-by: Jesus Sanchez-Palencia --- src/gui/graphicsview/graphicsview.pri | 3 +- src/gui/graphicsview/qgraph_p.h | 179 ++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 src/gui/graphicsview/qgraph_p.h diff --git a/src/gui/graphicsview/graphicsview.pri b/src/gui/graphicsview/graphicsview.pri index 0c0747e..ce35721 100644 --- a/src/gui/graphicsview/graphicsview.pri +++ b/src/gui/graphicsview/graphicsview.pri @@ -20,7 +20,8 @@ HEADERS += graphicsview/qgraphicsgridlayout.h \ graphicsview/qgraphicsview_p.h \ graphicsview/qgraphicswidget.h \ graphicsview/qgraphicswidget_p.h \ - graphicsview/qgridlayoutengine_p.h + graphicsview/qgridlayoutengine_p.h \ + graphicsview/qgraph_p.h SOURCES += graphicsview/qgraphicsgridlayout.cpp \ graphicsview/qgraphicsitem.cpp \ diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h new file mode 100644 index 0000000..4a6bc8f --- /dev/null +++ b/src/gui/graphicsview/qgraph_p.h @@ -0,0 +1,179 @@ +#include +#include +#include +#include + +#include + +template +class Graph +{ +public: + Graph() {} + + class iterator { + public: + iterator(Graph *graph, bool begin) : g(graph){ + if (begin) { + row = g->m_graph.begin(); + //test if the graph is empty + if (row != g->m_graph.end()) + { + column = (*row)->begin(); + } + } else { + row = g->m_graph.end(); + } + } + + inline Vertex *operator*() { + return column.key(); + } + + inline bool operator==(const iterator &o) const { return !(*this != o); } + inline bool operator!=(const iterator &o) const { + if (row == g->m_graph.end()) { + return row != o.row;} + else { + return row != o.row || column != o.column; + } + } + inline iterator& operator=(const iterator &o) const { row = o.row; column = o.column; return *this;} + + // prefix + iterator &operator++() { + if (row != g->m_graph.end()) { + ++column; + if (column == (*row)->end()) { + ++row; + if (row != g->m_graph.end()) { + column = (*row)->begin(); + } + } + } + return *this; + } + + private: + Graph *g; + Q_TYPENAME QHash * >::iterator row; + Q_TYPENAME QHash::iterator column; + }; + + iterator begin() { + return iterator(this,true); + } + + iterator end() { + return iterator(this,false); + } + + EdgeData *edgeData(Vertex *first, Vertex *second) { + return m_graph.value(first)->value(second); + } + + void createEdge(Vertex *first, Vertex *second, EdgeData *data) + { + // Creates a bidirectional edge + createDirectedEdge(first, second, data); + createDirectedEdge(second, first, data); + } + + void removeEdge(Vertex *first, Vertex *second) + { + // Creates a bidirectional edge + EdgeData *data = edgeData(first, second); + if (data) delete data; + removeDirectedEdge(first, second); + removeDirectedEdge(second, first); + } + + QList adjacentVertices(Vertex *vertex) const + { + QHash *row = m_graph.value(vertex); + QList l; + if (row) + l = row->keys(); + return l; + } + + void setRootVertex(Vertex *vertex) + { + userVertex = vertex; + } + + QString serializeToDot() { // traversal + QString vertices; + QString edges; + QQueue queue; + QSet visited; + bool ok; + Vertex *v = firstVertex(&ok); + if (ok) { + queue.enqueue(v); + } + while (!queue.isEmpty()) { + Vertex *v = queue.dequeue(); + vertices += QString::fromAscii("%1 [label=\"%2\"]\n").arg(v->toString()).arg(v->toString()); + visited.insert(v); + // visit it here + QList vertices = adjacentVertices(v); + for (int i = 0; i < vertices.count(); ++i) { + Vertex *v1 = vertices.at(i); + EdgeData *data = edgeData(v, v1); + edges+=QString::fromAscii("%1->%2 [label=\"[%3,%4]\"]\n").arg(v->toString()).arg(v1->toString()).arg(data->minSize).arg(data->maxSize); + if (!queue.contains(v1) && !visited.contains(v1)) { + queue.enqueue(v1); + } else { + // a cycle.... + } + } + } + return QString::fromAscii("digraph anchorlayout {\nnode [shape=\"rect\"]\n%1%2}").arg(vertices).arg(edges); + } + + Vertex *firstVertex(bool *ok) + { + if (userVertex) { + *ok = true; + return userVertex; + } + + Vertex *v = 0; + *ok = false; + Q_TYPENAME Graph::iterator it = Graph::begin(); + if (it != Graph::end()) { + v = *it; + *ok = true; + } + return v; + } + +protected: + void createDirectedEdge(Vertex *from, Vertex *to, EdgeData *data) + { + QHash *adjacentToFirst = m_graph.value(from); + if (!adjacentToFirst) { + adjacentToFirst = new QHash(); + m_graph.insert(from, adjacentToFirst); + } + adjacentToFirst->insert(to, data); + } + + void removeDirectedEdge(Vertex *from, Vertex *to) + { + QHash *adjacentToFirst = m_graph.value(from); + adjacentToFirst->remove(to); + if (adjacentToFirst->isEmpty()) { + //nobody point to 'from' so we can remove it from the graph + QHash *adjacentToFirst = m_graph.take(from); + delete adjacentToFirst; + delete from; + } + } + +private: + Vertex *userVertex; + + QHash *> m_graph; +}; -- cgit v0.12 From 26a785e3c65a5f60bcf5bb2e07e58bc6b544dc30 Mon Sep 17 00:00:00 2001 From: Jesus Sanchez-Palencia Date: Thu, 28 May 2009 14:42:36 -0300 Subject: QSimplex: Adding the Simplex solver for linear systems This implementation is based on the iterative algorithm presented in http://en.wikibooks.org/wiki/Operations_Research/The_Simplex_Method. It is capable of giving us the solution to any n variable LP model. Although we focused on QGraphicsAnchorLayout, the solver is general enough for being reused in any other Qt classes if needed. Signed-off-by: Anselmo Lacerda S. de Melo Signed-off-by: Caio Marcelo de Oliveira Filho Signed-off-by: Eduardo M. Fleury Signed-off-by: Jesus Sanchez-Palencia --- src/gui/graphicsview/graphicsview.pri | 6 +- src/gui/graphicsview/qsimplex_p.cpp | 364 ++++++++++++++++++++++++++++++++++ src/gui/graphicsview/qsimplex_p.h | 120 +++++++++++ 3 files changed, 488 insertions(+), 2 deletions(-) create mode 100644 src/gui/graphicsview/qsimplex_p.cpp create mode 100644 src/gui/graphicsview/qsimplex_p.h diff --git a/src/gui/graphicsview/graphicsview.pri b/src/gui/graphicsview/graphicsview.pri index ce35721..49ba70b 100644 --- a/src/gui/graphicsview/graphicsview.pri +++ b/src/gui/graphicsview/graphicsview.pri @@ -21,7 +21,8 @@ HEADERS += graphicsview/qgraphicsgridlayout.h \ graphicsview/qgraphicswidget.h \ graphicsview/qgraphicswidget_p.h \ graphicsview/qgridlayoutengine_p.h \ - graphicsview/qgraph_p.h + graphicsview/qgraph_p.h \ + graphicsview/qsimplex_p.h SOURCES += graphicsview/qgraphicsgridlayout.cpp \ graphicsview/qgraphicsitem.cpp \ @@ -40,4 +41,5 @@ SOURCES += graphicsview/qgraphicsgridlayout.cpp \ graphicsview/qgraphicsview.cpp \ graphicsview/qgraphicswidget.cpp \ graphicsview/qgraphicswidget_p.cpp \ - graphicsview/qgridlayoutengine.cpp + graphicsview/qgridlayoutengine.cpp \ + graphicsview/qsimplex_p.cpp diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp new file mode 100644 index 0000000..4ed11c4 --- /dev/null +++ b/src/gui/graphicsview/qsimplex_p.cpp @@ -0,0 +1,364 @@ +#include "qsimplex_p.h" + +#include +#include + +#include + +QSimplex::QSimplex() : rows(0), columns(0), firstArtificial(0), matrix(0) +{ + +} + +QSimplex::~QSimplex() +{ + clearDataStructures(); +} + +void QSimplex::clearDataStructures() +{ + if (matrix == 0) + return; + + // Matrix + rows = 0; + columns = 0; + firstArtificial = 0; + free(matrix); + matrix = 0; + + // 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; + } + constraints.clear(); + + // Other + variables.clear(); + objective = 0; +} + +void QSimplex::setConstraints(const QList newConstraints) +{ + clearDataStructures(); + + if (newConstraints.isEmpty()) + return; + constraints = newConstraints; + + // Set Variables direct mapping + QSet variablesSet; + for (int i = 0; i < constraints.size(); ++i) + variablesSet += \ + QSet::fromList(constraints[i]->variables.keys()); + variables = variablesSet.toList(); + + // Set Variables reverse mapping + for (int i = 0; i < variables.size(); ++i) { + // The variable "0" goes at the column "1", etc... + variables[i]->index = i + 1; + } + + // Normalize Constraints + int variableIndex = variables.size(); + QList artificialList; + + for (int i = 0; i < constraints.size(); ++i) { + QSimplexVariable *slack; + QSimplexVariable *surplus; + QSimplexVariable *artificial; + + Q_ASSERT(constraints[i]->helper.first == 0); + Q_ASSERT(constraints[i]->artificial == 0); + + switch(constraints[i]->ratio) { + case QSimplexConstraint::LessOrEqual: + slack = new QSimplexVariable; + slack->index = ++variableIndex; + constraints[i]->helper.first = slack; + constraints[i]->helper.second = 1.0; + break; + case QSimplexConstraint::MoreOrEqual: + surplus = new QSimplexVariable; + surplus->index = ++variableIndex; + constraints[i]->helper.first = surplus; + constraints[i]->helper.second = -1.0; + // fall through + case QSimplexConstraint::Equal: + artificial = new QSimplexVariable; + constraints[i]->artificial = artificial; + artificialList += constraints[i]->artificial; + break; + } + } + + firstArtificial = variableIndex + 1; + for (int i = 0; i < artificialList.size(); ++i) + artificialList[i]->index = ++variableIndex; + artificialList.clear(); + + // Matrix + + // One for each variable plus the Basic and BFS columns (first and last) + columns = variableIndex + 2; + // One for each constraint plus the objective function + rows = constraints.size() + 1; + + matrix = (qreal *)malloc(sizeof(qreal) * columns * rows); + if (!matrix) { + qWarning() << "QSimplex: Unable to allocate memory!"; + return; + } + for (int i = columns * rows - 1; i >= 0; --i) + matrix[i] = 0.0; + + // Fill Matrix + for (int i = 1; i <= constraints.size(); ++i) { + QSimplexConstraint *c = constraints[i - 1]; + + if (c->artificial) { + // Will use artificial basic variable + setValueAt(i, 0, c->artificial->index); + setValueAt(i, c->artificial->index, 1.0); + + if (c->helper.second != 0.0) { + // Surplus variable + setValueAt(i, c->helper.first->index, c->helper.second); + } + } else { + // Slack is used as the basic variable + Q_ASSERT(c->helper.second == 1.0); + setValueAt(i, 0, c->helper.first->index); + setValueAt(i, c->helper.first->index, 1.0); + } + + QHash::const_iterator iter; + for (iter = c->variables.constBegin(); + iter != c->variables.constEnd(); + ++iter) { + setValueAt(i, iter.key()->index, iter.value()); + } + + setValueAt(i, columns - 1, c->constant); + } + + // Set temporary objective: -1 * sum_of_artificial_vars + for (int j = firstArtificial; j < columns - 1; ++j) + setValueAt(0, j, 1.0); + + // Maximize our objective (artificial vars go to zero) + solveMaxHelper(); + + if (valueAt(0, columns - 1) != 0.0) { + qWarning() << "QSimplex: No feaseable solution!"; + clearDataStructures(); + return; + } + + // Remove artificial variables + clearColumns(firstArtificial, columns - 2); +} + +void QSimplex::solveMaxHelper() +{ + reducedRowEchelon(); + while (iterate()) ; +} + +void QSimplex::setObjective(QSimplexConstraint *newObjective) +{ + objective = newObjective; +} + +void QSimplex::clearRow(int rowIndex) +{ + qreal *item = matrix + rowIndex * columns; + for (int i = 0; i < columns; ++i) + item[i] = 0.0; +} + +void QSimplex::clearColumns(int first, int last) +{ + for (int i = 0; i < rows; ++i) { + qreal *row = matrix + i * columns; + for (int j = first; j <= last; ++j) + row[j] = 0.0; + } +} + +void QSimplex::dumpMatrix() +{ + printf("---- Simplex Matrix ----\n"); + + printf(" "); + for (int j = 0; j < columns; ++j) + printf(" <% 2d >", j); + printf("\n"); + + for (int i = 0; i < rows; ++i) { + printf("Row %2d:", i); + + qreal *row = matrix + i * columns; + for (int j = 0; j < columns; ++j) { + printf(" % 2.2f", row[j]); + } + printf("\n"); + } + printf("------------------------\n\n"); +} + +void QSimplex::combineRows(int toIndex, int fromIndex, qreal factor) +{ + if (!factor) + return; + + qreal *from = matrix + fromIndex * columns; + qreal *to = matrix + toIndex * columns; + + for (int j = 1; j < columns; ++j) { + to[j] += factor * from[j]; + + // ### Avoid Numerical errors + if (qAbs(to[j]) < 0.0000000001) + to[j] = 0.0; + } +} + +int QSimplex::findPivotColumn() +{ + qreal min = 0; + int minIndex = -1; + + for (int j = 0; j < columns-1; ++j) { + if (valueAt(0, j) < min) { + min = valueAt(0, j); + minIndex = j; + } + } + + return minIndex; +} + +int QSimplex::pivotRowForColumn(int column) +{ + qreal min = 999999999999.0; // ### + int minIndex = -1; + + for (int i = 1; i < rows; ++i) { + qreal divisor = valueAt(i, column); + if (divisor <= 0) + continue; + + qreal quotient = valueAt(i, columns - 1) / divisor; + if (quotient < min) { + min = quotient; + minIndex = i; + } + } + + return minIndex; +} + +void QSimplex::reducedRowEchelon() +{ + for (int i = 1; i < rows; ++i) { + int factorInObjectiveRow = valueAt(i, 0); + combineRows(0, i, -1 * valueAt(0, factorInObjectiveRow)); + } +} + +bool QSimplex::iterate() +{ + // Find Pivot column + int pivotColumn = findPivotColumn(); + if (pivotColumn == -1) + return false; + + // Find Pivot row for column + int pivotRow = pivotRowForColumn(pivotColumn); + if (pivotRow == -1) { + qWarning() << "QSimplex: Unbounded problem!"; + return false; + } + + // Normalize Pivot Row + qreal pivot = valueAt(pivotRow, pivotColumn); + if (pivot != 1.0) + combineRows(pivotRow, pivotRow, (1.0 - pivot) / pivot); + + // Update other rows + for (int row=0; row < rows; ++row) { + if (row == pivotRow) + continue; + + combineRows(row, pivotRow, -1 * valueAt(row, pivotColumn)); + } + + // Update first column + setValueAt(pivotRow, 0, pivotColumn); + + // dumpMatrix(); + // printf("------------ end of iteration --------------\n"); + return true; +} + +qreal QSimplex::solveMin() +{ + // Remove old objective + clearRow(0); + + // Set new objective + QHash::const_iterator iter; + for (iter = objective->variables.constBegin(); + iter != objective->variables.constEnd(); + ++iter) { + setValueAt(0, iter.key()->index, iter.value()); + } + + solveMaxHelper(); + collectResults(); + + return -1 * valueAt(0, columns - 1); +} + +qreal QSimplex::solveMax() +{ + // Remove old objective + clearRow(0); + + // Set new objective + QHash::const_iterator iter; + for (iter = objective->variables.constBegin(); + iter != objective->variables.constEnd(); + ++iter) { + setValueAt(0, iter.key()->index, -1 * iter.value()); + } + + solveMaxHelper(); + collectResults(); + + return valueAt(0, columns - 1); +} + +void QSimplex::collectResults() +{ + // All variables are zero unless overridden below. + + // ### Is this really needed? Is there any chance that an + // important variable remains as non-basic at the end of simplex? + for (int i = 0; i < variables.size(); ++i) + variables[i]->result = 0; + + // Basic variables + // Update the variable indicated in the first column with the value + // in the last column. + for (int i = 1; i < rows; ++i) { + int index = valueAt(i, 0) - 1; + if (index < variables.size()) + variables[index]->result = valueAt(i, columns - 1); + } +} diff --git a/src/gui/graphicsview/qsimplex_p.h b/src/gui/graphicsview/qsimplex_p.h new file mode 100644 index 0000000..3881893 --- /dev/null +++ b/src/gui/graphicsview/qsimplex_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 1992-$THISYEAR$ $TROLLTECH$. All rights reserved. +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#ifndef QSIMPLEX_P_H +#define QSIMPLEX_P_H + +#include +#include + +struct QSimplexVariable +{ + QSimplexVariable() : result(0), index(0) {}; + + qreal result; + uint index; +}; + + +/*! + \internal + + Representation of a LP constraint like: + + (c1 * X1) + (c2 * X2) + ... = K + or <= K + or >= K + + Where (ci, Xi) are the pairs in "variables" and K the real "constant". +*/ +struct QSimplexConstraint +{ + QSimplexConstraint() : constant(0), ratio(Equal), artificial(0) {}; + + enum Ratio { + LessOrEqual = 0, + Equal, + MoreOrEqual + }; + + QHash variables; + qreal constant; + Ratio ratio; + + QPair helper; + QSimplexVariable * artificial; +}; + + +class QSimplex +{ +public: + QSimplex(); + virtual ~QSimplex(); + + qreal solveMin(); + qreal solveMax(); + QList constraintsVariables(); + + void setConstraints(const QList constraints); + void setObjective(QSimplexConstraint *objective); + + void dumpMatrix(); + +private: + // Matrix handling + qreal valueAt(int row, int column); + void setValueAt(int row, int column, qreal value); + void clearRow(int rowIndex); + void clearColumns(int first, int last); + void combineRows(int toIndex, int fromIndex, qreal factor); + + // Simplex + int findPivotColumn(); + int pivotRowForColumn(int column); + void reducedRowEchelon(); + bool iterate(); + + // Helpers + void clearDataStructures(); + void solveMaxHelper(); + void collectResults(); + + QList constraints; + QList variables; + QSimplexConstraint *objective; + + int rows; + int columns; + int firstArtificial; + + qreal *matrix; +}; + +inline QList QSimplex::constraintsVariables() +{ + return variables; +} + +inline qreal QSimplex::valueAt(int rowIndex, int columnIndex) +{ + return matrix[rowIndex * columns + columnIndex]; +} + +inline void QSimplex::setValueAt(int rowIndex, int columnIndex, qreal value) +{ + matrix[rowIndex * columns + columnIndex] = value; +} + + +#endif -- cgit v0.12 From b1bd07d163d335ab05b878b907965e43124d8da1 Mon Sep 17 00:00:00 2001 From: Jesus Sanchez-Palencia Date: Thu, 28 May 2009 15:28:23 -0300 Subject: QGraphicsAnchorLayout: Adding QGraphicsAnchorLayout public and private classes This is a total re-written implementation of QGraphicsAnchorLayout using a numerical approach. We use QGraph and QSimplex in order to achieve this. This first commit gives us a just ready-to-use qgraphicslayout, but the private class still need to inherit from qgraphicslayoutprivate. Optimizations and documentation are all work in progress. Signed-off-by: Anselmo Lacerda S. de Melo Signed-off-by: Caio Marcelo de Oliveira Filho Signed-off-by: Eduardo M. Fleury Signed-off-by: Jesus Sanchez-Palencia --- src/gui/graphicsview/graphicsview.pri | 8 +- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 235 +++++ src/gui/graphicsview/qgraphicsanchorlayout.h | 109 +++ src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 1056 ++++++++++++++++++++++ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 301 ++++++ 5 files changed, 1707 insertions(+), 2 deletions(-) create mode 100644 src/gui/graphicsview/qgraphicsanchorlayout.cpp create mode 100644 src/gui/graphicsview/qgraphicsanchorlayout.h create mode 100644 src/gui/graphicsview/qgraphicsanchorlayout_p.cpp create mode 100644 src/gui/graphicsview/qgraphicsanchorlayout_p.h diff --git a/src/gui/graphicsview/graphicsview.pri b/src/gui/graphicsview/graphicsview.pri index 49ba70b..6e32171 100644 --- a/src/gui/graphicsview/graphicsview.pri +++ b/src/gui/graphicsview/graphicsview.pri @@ -22,7 +22,9 @@ HEADERS += graphicsview/qgraphicsgridlayout.h \ graphicsview/qgraphicswidget_p.h \ graphicsview/qgridlayoutengine_p.h \ graphicsview/qgraph_p.h \ - graphicsview/qsimplex_p.h + graphicsview/qsimplex_p.h \ + graphicsview/qgraphicsanchorlayout_p.h \ + graphicsview/qgraphicsanchorlayout.h SOURCES += graphicsview/qgraphicsgridlayout.cpp \ graphicsview/qgraphicsitem.cpp \ @@ -42,4 +44,6 @@ SOURCES += graphicsview/qgraphicsgridlayout.cpp \ graphicsview/qgraphicswidget.cpp \ graphicsview/qgraphicswidget_p.cpp \ graphicsview/qgridlayoutengine.cpp \ - graphicsview/qsimplex_p.cpp + graphicsview/qsimplex_p.cpp \ + graphicsview/qgraphicsanchorlayout_p.cpp \ + graphicsview/qgraphicsanchorlayout.cpp diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp new file mode 100644 index 0000000..d737e36 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgraphicsanchorlayout_p.h" + +QGraphicsAnchorLayout::QGraphicsAnchorLayout(QGraphicsLayoutItem *parent) + : QGraphicsLayout(parent), d_ptr(new QGraphicsAnchorLayoutPrivate()) +{ + // ### REMOVE THAT + d_ptr->q_ptr = this; + + d_ptr->createLayoutEdges(); +} + +QGraphicsAnchorLayout::~QGraphicsAnchorLayout() +{ + Q_D(QGraphicsAnchorLayout); + + for (int i = count() - 1; i >= 0; --i) { + QGraphicsLayoutItem *item = d->items.at(i); + removeAt(i); + if (item) { + if (item->ownedByLayout()) + delete item; + } + } +} + +void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, + Edge firstEdge, + QGraphicsLayoutItem *secondItem, + Edge secondEdge, qreal spacing) +{ + Q_D(QGraphicsAnchorLayout); + if ((firstItem == 0) || (secondItem == 0)) { + qWarning("QGraphicsAnchorLayout::anchor: " + "Cannot anchor NULL items"); + return; + } + + if (firstItem == secondItem) { + qWarning("QGraphicsAnchorLayout::anchor: " + "Cannot anchor the item to itself"); + return; + } + + if (d->edgeOrientation(secondEdge) != d->edgeOrientation(firstEdge)) { + qWarning("QGraphicsAnchorLayout::anchor: " + "Cannot anchor edges of different orientations"); + return; + } + + // In QGraphicsAnchorLayout, items are represented in its internal + // graph as four anchors that connect: + // - Left -> HCenter + // - HCenter-> Right + // - Top -> VCenter + // - VCenter -> Bottom + + // Ensure that the internal anchors have been created for both items. + if (firstItem != this && !d->items.contains(firstItem)) { + d->createItemEdges(firstItem); + d->addChildItem(firstItem); + } + if (secondItem != this && !d->items.contains(secondItem)) { + d->createItemEdges(secondItem); + d->addChildItem(secondItem); + } + + // Use heuristics to find out what the user meant with this anchor. + d->correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge); + + // Create actual anchor between firstItem and secondItem. + AnchorData *data; + if (spacing >= 0) { + data = new AnchorData(spacing); + d->addAnchor(firstItem, firstEdge, secondItem, secondEdge, data); + } else { + data = new AnchorData(-spacing); + d->addAnchor(secondItem, secondEdge, firstItem, firstEdge, data); + } + + invalidate(); +} + +void QGraphicsAnchorLayout::removeAnchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, + QGraphicsLayoutItem *secondItem, Edge secondEdge) +{ + Q_D(QGraphicsAnchorLayout); + if ((firstItem == 0) || (secondItem == 0)) { + qWarning("QGraphicsAnchorLayout::removeAnchor: " + "Cannot remove anchor between NULL items"); + return; + } + + if (firstItem == secondItem) { + qWarning("QGraphicsAnchorLayout::removeAnchor: " + "Cannot remove anchor from the item to itself"); + return; + } + + if (d->edgeOrientation(secondEdge) != d->edgeOrientation(firstEdge)) { + qWarning("QGraphicsAnchorLayout::removeAnchor: " + "Cannot remove anchor from edges of different orientations"); + return; + } + + d->removeAnchor(firstItem, firstEdge, secondItem, secondEdge); + + invalidate(); +} + +void QGraphicsAnchorLayout::setGeometry(const QRectF &geom) +{ + Q_D(QGraphicsAnchorLayout); + QGraphicsLayout::setGeometry(geom); + d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Horizontal); + d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Vertical); + d->setItemsGeometries(); +} + +void QGraphicsAnchorLayout::removeAt(int index) +{ + Q_D(QGraphicsAnchorLayout); + QGraphicsLayoutItem *item = d->items.value(index); + + if (item) { + d->items.remove(index); + d->removeAnchors(item); + item->setParentLayoutItem(0); + } +} + +int QGraphicsAnchorLayout::count() const +{ + Q_D(const QGraphicsAnchorLayout); + return d->items.size(); +} + +QGraphicsLayoutItem *QGraphicsAnchorLayout::itemAt(int index) const +{ + Q_D(const QGraphicsAnchorLayout); + return d->items.value(index); +} + +void QGraphicsAnchorLayout::invalidate() +{ + Q_D(QGraphicsAnchorLayout); + QGraphicsLayout::invalidate(); + d->calculateGraphCacheDirty = 1; +} + +QSizeF QGraphicsAnchorLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_UNUSED(which); + Q_UNUSED(constraint); + Q_D(const QGraphicsAnchorLayout); + + // Some setup calculations are delayed until the information is + // actually needed, avoiding unnecessary recalculations when + // adding multiple anchors. + + // sizeHint() / effectiveSizeHint() already have a cache + // mechanism, using invalidate() to force recalculation. However + // sizeHint() is called three times after invalidation (for max, + // min and pref), but we just need do our setup once. + + const_cast(d)->calculateGraphs(); + + // ### apply constraint! + QSizeF engineSizeHint( + d->sizeHints[QGraphicsAnchorLayoutPrivate::Horizontal][which], + d->sizeHints[QGraphicsAnchorLayoutPrivate::Vertical][which]); + + qreal left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + + return engineSizeHint + QSizeF(left + right, top + bottom); +} + +//////// DEBUG ///////// +#include +void QGraphicsAnchorLayout::dumpGraph() +{ + Q_D(QGraphicsAnchorLayout); + + QFile file(QString::fromAscii("anchorlayout.dot")); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) + qWarning("Could not write to %s", file.fileName().toLocal8Bit().constData()); + + QString dotContents = d->graph[0].serializeToDot(); + file.write(dotContents.toLocal8Bit()); + dotContents = d->graph[1].serializeToDot(); + file.write(dotContents.toLocal8Bit()); + + file.close(); +} diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h new file mode 100644 index 0000000..288aec1 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRAPHICSANCHORLAYOUT_H +#define QGRAPHICSANCHORLAYOUT_H + +#include +#include + +/* +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW +*/ + +class QGraphicsAnchorLayoutPrivate; + +class Q_GUI_EXPORT QGraphicsAnchorLayout : public QGraphicsLayout +{ +public: + enum Edge { + Left = 0, + HCenter, + Right, + Top, + VCenter, + Bottom + }; + + QGraphicsAnchorLayout(QGraphicsLayoutItem *parent = 0); + virtual ~QGraphicsAnchorLayout(); + + void anchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, + QGraphicsLayoutItem *secondItem, Edge secondEdge, + qreal spacing = 0); + + void removeAnchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, + QGraphicsLayoutItem *secondItem, Edge secondEdge); + + void removeAt(int index); + void setGeometry(const QRectF &rect); + int count() const; + QGraphicsLayoutItem *itemAt(int index) const; + + void invalidate(); + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + + ///////// DEBUG ///////// + void dumpGraph(); + QGraphicsAnchorLayoutPrivate *d_ptr; //### TO REMOVE +protected: + +private: + +// Q_DISABLE_COPY(QGraphicsAnchorLayout) //### TO UNCOMMENT + Q_DECLARE_PRIVATE(QGraphicsAnchorLayout) +}; + +/* +#endif + +QT_END_NAMESPACE + +QT_END_HEADER +*/ + +#endif diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp new file mode 100644 index 0000000..84d1c31 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -0,0 +1,1056 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qgraphicsanchorlayout_p.h" + +QSimplexConstraint *GraphPath::constraint(const GraphPath &path) const +{ + // Calculate + QSet cPositives; + QSet cNegatives; + QSet intersection; + + cPositives = positives + path.negatives; + cNegatives = negatives + path.positives; + + intersection = cPositives & cNegatives; + + cPositives -= intersection; + cNegatives -= intersection; + + // Fill + QSimplexConstraint *c = new QSimplexConstraint; + QSet::iterator i; + for (i = cPositives.begin(); i != cPositives.end(); ++i) + c->variables.insert(*i, 1.0); + + for (i = cNegatives.begin(); i != cNegatives.end(); ++i) + c->variables.insert(*i, -1.0); + + return c; +} + +QString GraphPath::toString() const +{ + QString string("Path: "); + foreach(AnchorData *edge, positives) + string += QString(" (+++) %1").arg(edge->toString()); + + foreach(AnchorData *edge, negatives) + string += QString(" (---) %1").arg(edge->toString()); + + return string; +} + + +QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate() + : calculateGraphCacheDirty(1) +{ +} + +QGraphicsAnchorLayout::Edge QGraphicsAnchorLayoutPrivate::oppositeEdge( + QGraphicsAnchorLayout::Edge edge) +{ + switch (edge) { + case QGraphicsAnchorLayout::Left: + edge = QGraphicsAnchorLayout::Right; + break; + case QGraphicsAnchorLayout::Right: + edge = QGraphicsAnchorLayout::Left; + break; + case QGraphicsAnchorLayout::Top: + edge = QGraphicsAnchorLayout::Bottom; + break; + case QGraphicsAnchorLayout::Bottom: + edge = QGraphicsAnchorLayout::Top; + break; + default: + break; + } + return edge; +} + +QGraphicsAnchorLayoutPrivate::Orientation +QGraphicsAnchorLayoutPrivate::edgeOrientation(QGraphicsAnchorLayout::Edge edge) +{ + return edge > QGraphicsAnchorLayout::Right ? Vertical : Horizontal; +} + +/*! + \internal + + Create two internal anchors to connect the layout edges and its + central anchorage point. + These anchors doesn't have size restrictions other than the fact they + should always have the same size, which is something we enforce later, + when creating restrictions for the Simplex solver. +*/ +void QGraphicsAnchorLayoutPrivate::createLayoutEdges() +{ + Q_Q(QGraphicsAnchorLayout); + QGraphicsLayoutItem *layout = q; + + // Horizontal + QSimplexConstraint *c = new QSimplexConstraint; + AnchorData *data = new AnchorData(0, 0, QWIDGETSIZE_MAX); + addAnchor(layout, QGraphicsAnchorLayout::Left, layout, + QGraphicsAnchorLayout::HCenter, data); + data->skipInPreferred = 1; + c->variables.insert(data, 1.0); + + data = new AnchorData(0, 0, QWIDGETSIZE_MAX); + addAnchor(layout, QGraphicsAnchorLayout::HCenter, + layout, QGraphicsAnchorLayout::Right, data); + data->skipInPreferred = 1; + c->variables.insert(data, -1.0); + + itemCenterConstraints[Horizontal].append(c); + + // Set the Layout Left edge as the root of the horizontal graph. + AnchorVertex *v; + v = m_vertexList.value(qMakePair(layout, QGraphicsAnchorLayout::Left)); + graph[Horizontal].setRootVertex(v); + + // Vertical + c = new QSimplexConstraint; + data = new AnchorData(0, 0, QWIDGETSIZE_MAX); + addAnchor(layout, QGraphicsAnchorLayout::Top, layout, + QGraphicsAnchorLayout::VCenter, data); + c->variables.insert(data, 1.0); + + data = new AnchorData(0, 0, QWIDGETSIZE_MAX); + addAnchor(layout, QGraphicsAnchorLayout::VCenter, + layout, QGraphicsAnchorLayout::Bottom, data); + c->variables.insert(data, -1.0); + + itemCenterConstraints[Vertical].append(c); + + // Set the Layout Top edge as the root of the vertical graph. + v = m_vertexList.value(qMakePair(layout, QGraphicsAnchorLayout::Top)); + graph[Vertical].setRootVertex(v); +} + +void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) +{ + items.append(item); + + // Horizontal + int minimumSize = item->minimumWidth() / 2; + int preferredSize = item->preferredWidth() / 2; + int maximumSize = item->maximumWidth() / 2; + + QSimplexConstraint *c = new QSimplexConstraint; + + AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); + addAnchor(item, QGraphicsAnchorLayout::Left, item, + QGraphicsAnchorLayout::HCenter, data); + c->variables.insert(data, 1.0); + + data = new AnchorData(minimumSize, preferredSize, maximumSize); + addAnchor(item, QGraphicsAnchorLayout::HCenter, + item, QGraphicsAnchorLayout::Right, data); + c->variables.insert(data, -1.0); + + itemCenterConstraints[Horizontal].append(c); + + // Vertical + minimumSize = item->minimumHeight() / 2; + preferredSize = item->preferredHeight() / 2; + maximumSize = item->maximumHeight() / 2; + + c = new QSimplexConstraint; + + data = new AnchorData(minimumSize, preferredSize, maximumSize); + addAnchor(item, QGraphicsAnchorLayout::Top, item, + QGraphicsAnchorLayout::VCenter, data); + c->variables.insert(data, 1.0); + + data = new AnchorData(minimumSize, preferredSize, maximumSize); + addAnchor(item, QGraphicsAnchorLayout::VCenter, + item, QGraphicsAnchorLayout::Bottom, data); + c->variables.insert(data, -1.0); + + itemCenterConstraints[Vertical].append(c); +} + +void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, + QGraphicsAnchorLayout::Edge firstEdge, + QGraphicsLayoutItem *secondItem, + QGraphicsAnchorLayout::Edge secondEdge, + AnchorData *data) +{ + AnchorVertex *v1; + AnchorVertex *v2; + + // Is the Vertex (firstItem, firstEdge) already represented in our + // internal structure? + v1 = m_vertexList.value(qMakePair(firstItem, firstEdge)); + if (!v1) { + v1 = new AnchorVertex(firstItem, firstEdge); + m_vertexList.insert(qMakePair(firstItem, firstEdge), v1); + } + + // The same for the second vertex + v2 = m_vertexList.value(qMakePair(secondItem, secondEdge)); + if (!v2) { + v2 = new AnchorVertex(secondItem, secondEdge); + m_vertexList.insert(qMakePair(secondItem, secondEdge), v2); + } + + // 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. + data->origin = v1; + data->name = QString("%1 --to--> %2").arg(v1->toString()).arg(v2->toString()); + + graph[edgeOrientation(firstEdge)].createEdge(v1, v2, data); +} + +void QGraphicsAnchorLayoutPrivate::removeAnchor(QGraphicsLayoutItem *firstItem, + QGraphicsAnchorLayout::Edge firstEdge, + QGraphicsLayoutItem *secondItem, + QGraphicsAnchorLayout::Edge secondEdge) +{ + AnchorVertex *v1 = 0; + AnchorVertex *v2 = 0; + + // Is there a representation for the Vertex (firstItem, firstEdge) + // in our internal structure? + if ((v1 = m_vertexList.value(qMakePair(firstItem,firstEdge)))) + m_vertexList.remove(qMakePair(firstItem,firstEdge)); + else + qWarning()<<"This item with this edge is not in the graph"; + + // The same for the second vertex + if ((v2 = m_vertexList.value(qMakePair(secondItem,secondEdge)))) + m_vertexList.remove(qMakePair(secondItem,secondEdge)); + else + qWarning()<<"This item with this edge is not in the graph"; + + if (v1 && v2) { + graph[edgeOrientation(firstEdge)].removeEdge(v1, v2); + } +} + +void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item) +{ + AnchorVertex *v1 = 0; + AnchorVertex *v2 = 0; + QList allVertex; + int edge; + + for (edge = QGraphicsAnchorLayout::Left; edge != QGraphicsAnchorLayout::Bottom; ++edge) { + // Remove all vertex for all edges + QGraphicsAnchorLayout::Edge e = static_cast(edge); + + if ((v1 = m_vertexList.value(qMakePair(item, e)))) { + m_vertexList.remove(qMakePair(item, e)); + + // Remove all edges + allVertex = graph[edgeOrientation(e)].adjacentVertices(v1); + + foreach (v2, allVertex) + graph[edgeOrientation(e)].removeEdge(v1, v2); + } + } +} + +/*! + \internal + + Use heuristics to determine the correct orientation of a given anchor. + + After API discussions, we decided we would like expressions like + anchor(A, Left, B, Right) to mean the same as anchor(B, Right, A, Left). + The problem with this is that anchors could become ambiguous, for + instance, what does the anchor A, B of size X mean? + + "pos(B) = pos(A) + X" or "pos(A) = pos(B) + X" ? + + To keep the API user friendly and at the same time, keep our algorithm + deterministic, we use an heuristic to determine a direction for each + added anchor and then keep it. The heuristic is based on the fact + that people usually avoid overlapping items, therefore: + + "A, RIGHT to B, LEFT" means that B is to the LEFT of A. + "B, LEFT to A, RIGHT" is corrected to the above anchor. + + Special correction is also applied when one of the items is the + layout. We handle Layout Left as if it was another items's Right + and Layout Right as another item's Left. +*/ +void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&firstItem, + QGraphicsAnchorLayout::Edge &firstEdge, + QGraphicsLayoutItem *&secondItem, + QGraphicsAnchorLayout::Edge &secondEdge) +{ + Q_Q(QGraphicsAnchorLayout); + + QGraphicsAnchorLayout::Edge effectiveFirst = firstEdge; + QGraphicsAnchorLayout::Edge effectiveSecond = secondEdge; + + if (firstItem == q) + effectiveFirst = QGraphicsAnchorLayoutPrivate::oppositeEdge(firstEdge); + if (secondItem == q) + effectiveSecond = QGraphicsAnchorLayoutPrivate::oppositeEdge(secondEdge); + + if (effectiveFirst < effectiveSecond) { + + // ### DEBUG + /* printf("Swapping Anchor from %s %d --to--> %s %d\n", + firstItem->isLayout() ? "" : + qPrintable(static_cast(firstItem)->data(0).toString()), + firstEdge, + secondItem->isLayout() ? "" : + qPrintable(static_cast(secondItem)->data(0).toString()), + secondEdge); + */ + qSwap(firstItem, secondItem); + qSwap(firstEdge, secondEdge); + } +} + +/*! + \internal + + XXX: REMOVE THIS ONCE WE INHERIT SOMEONE ELSE! +*/ +QGraphicsItem *QGraphicsAnchorLayoutPrivate::parentItem() const +{ + Q_Q(const QGraphicsLayoutItem); + + const QGraphicsLayoutItem *parent = q; + while (parent && parent->isLayout()) { + parent = parent->parentLayoutItem(); + } + return parent ? parent->graphicsItem() : 0; +} + +/*! + \internal +*/ +void QGraphicsAnchorLayoutPrivate::addChildItem(QGraphicsLayoutItem *child) +{ + // XXX: Re-implement this!! + if (child) { + Q_Q(QGraphicsAnchorLayout); + child->setParentLayoutItem(q); + + child->graphicsItem()->setParentItem(parentItem()); + } +} + +/*! + \internal + + Called on activation. Uses Linear Programming to define minimum, preferred + and maximum sizes for the layout. Also calculates the sizes that each item + should assume when the layout is in one of such situations. +*/ +void QGraphicsAnchorLayoutPrivate::calculateGraphs() +{ + if (!calculateGraphCacheDirty) + return; + + calculateGraphs(Horizontal); + calculateGraphs(Vertical); + + calculateGraphCacheDirty = 0; +} + +// ### remove me: +QList getVariables(QList constraints) +{ + QSet variableSet; + for (int i = 0; i < constraints.count(); ++i) { + const QSimplexConstraint *c = constraints[i]; + foreach (QSimplexVariable *var, c->variables.keys()) { + variableSet += static_cast(var); + } + } + return variableSet.toList(); +} + +void QGraphicsAnchorLayoutPrivate::calculateGraphs( + QGraphicsAnchorLayoutPrivate::Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + + // Reset the nominal sizes of each anchor based on the current item sizes + setAnchorSizeHintsFromItems(orientation); + + // Traverse all graph edges and store the possible paths to each vertex + findPaths(orientation); + + // From the paths calculated above, extract the constraints that the current + // anchor setup impose, to our Linear Programming problem. + constraintsFromPaths(orientation); + + // Split the constraints and anchors into groups that should be fed to the + // simplex solver independently. Currently we find two groups: + // + // 1) The "trunk", that is, the set of anchors (items) that are connected + // to the two opposite sides of our layout, and thus need to stretch in + // order to fit in the current layout size. + // + // 2) The floating or semi-floating anchors (items) that are those which + // are connected to only one (or none) of the layout sides, thus are not + // influenced by the layout size. + QList > parts; + parts = getGraphParts(orientation); + + // Now run the simplex solver to calculate Minimum, Preferred and Maximum sizes + // of the "trunk" set of constraints and variables. + // ### does trunk always exist? empty = trunk is the layout left->center->right + QList trunkConstraints = parts[0]; + + // For minimum and maximum, use the path between the two layout sides as the + // objective function. + + // Retrieve that path + QGraphicsAnchorLayout::Edge end; + if (orientation == Horizontal) { + end = QGraphicsAnchorLayout::Right; + } else { + end = QGraphicsAnchorLayout::Bottom; + } + AnchorVertex *v = + m_vertexList.value(qMakePair(static_cast(q), end)); + GraphPath trunkPath = graphPaths[orientation].value(v); + + // Solve min and max size hints for trunk + QPair minMax = solveMinMax(trunkConstraints, trunkPath); + sizeHints[orientation][Qt::MinimumSize] = minMax.first; + sizeHints[orientation][Qt::MaximumSize] = minMax.second; + + // Solve for preferred. The objective function is calculated from the constraints + // and variables internally. + solvePreferred(trunkConstraints); + + // Calculate and set the preferred size for the layout from the edge sizes that + // were calculated above. + qreal pref(0.0); + foreach (const AnchorData *ad, trunkPath.positives) { + pref += ad->sizeAtPreferred; + } + foreach (const AnchorData *ad, trunkPath.negatives) { + pref -= ad->sizeAtPreferred; + } + sizeHints[orientation][Qt::PreferredSize] = pref; + + // For the other parts that not the trunk, solve only for the preferred size + // that is the size they will remain at, since they are not stretched by the + // layout. + + // Solve the other only for preferred, skip trunk + for (int i = 1; i < parts.count(); ++i) { + QList partConstraints = parts[i]; + QList partVariables = getVariables(partConstraints); + + // ### + if (partVariables.isEmpty()) + continue; + + solvePreferred(partConstraints); + + // Propagate size at preferred to other sizes. Semi-floats + // always will be in their sizeAtPreferred. + for (int j = 0; j < partVariables.count(); ++j) { + AnchorData *ad = static_cast(partVariables[j]); + ad->sizeAtMinimum = ad->sizeAtPreferred; + ad->sizeAtMaximum = ad->sizeAtPreferred; + } + } + + // Clean up our data structures. They are not needed anymore since + // distribution uses just interpolation. + qDeleteAll(constraints[orientation]); + constraints[orientation].clear(); + graphPaths[orientation].clear(); // ### +} + +void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orientation) +{ + QPair beginningKey; + QPair centerKey; + QPair endKey; + + if (orientation == Horizontal) { + beginningKey.second = QGraphicsAnchorLayout::Left; + centerKey.second = QGraphicsAnchorLayout::HCenter; + endKey.second = QGraphicsAnchorLayout::Right; + } else { + beginningKey.second = QGraphicsAnchorLayout::Top; + centerKey.second = QGraphicsAnchorLayout::VCenter; + endKey.second = QGraphicsAnchorLayout::Bottom; + } + + foreach (QGraphicsLayoutItem *item, items) { + AnchorVertex *beginning, *center, *end; + qreal min, pref, max; + + beginningKey.first = item; + centerKey.first = item; + endKey.first = item; + + beginning = m_vertexList.value(beginningKey); + center = m_vertexList.value(centerKey); + end = m_vertexList.value(endKey); + + if (orientation == Horizontal) { + min = item->minimumWidth(); + pref = item->preferredWidth(); + max = item->maximumWidth(); + } else { + min = item->minimumHeight(); + pref = item->preferredHeight(); + max = item->maximumHeight(); + } + + // To support items that are represented by a single anchor as well as + // those that have been divided into two halfs, we must do this check. + AnchorData *data; + if (center == 0) { + data = graph[orientation].edgeData(beginning, end); + // Set the anchor nominal sizes to those of the corresponding item + data->minSize = min; + data->prefSize = pref; + data->maxSize = max; + + // Set the anchor effective sizes to preferred. + // Note: The idea here is that all items should remain at + // their preferred size unless where that's impossible. + // In cases where the item is subject to restrictions + // (anchored to the layout edges, for instance), the + // simplex solver will be run to recalculate and override + // the values we set here. + data->sizeAtMinimum = pref; + data->sizeAtPreferred = pref; + data->sizeAtMaximum = pref; + } else { + min = min / 2; + pref = pref / 2; + max = max / 2; + + // Same as above, for each half + data = graph[orientation].edgeData(beginning, center); + data->minSize = min; + data->prefSize = pref; + data->maxSize = max; + data->sizeAtMinimum = pref; + data->sizeAtPreferred = pref; + data->sizeAtMaximum = pref; + + data = graph[orientation].edgeData(center, end); + data->minSize = min; + data->prefSize = pref; + data->maxSize = max; + data->sizeAtMinimum = pref; + data->sizeAtPreferred = pref; + data->sizeAtMaximum = pref; + } + } +} + +/*! + \internal + + This method walks the graph using a breadth-first search to find paths + between the root vertex and each vertex on the graph. The edges + directions in each path are considered and they are stored as a + positive edge (left-to-right) or negative edge (right-to-left). + + The list of paths is used later to generate a list of constraints. + */ +void QGraphicsAnchorLayoutPrivate::findPaths(Orientation orientation) +{ + QQueue > queue; + + QSet visited; + + bool ok; + AnchorVertex *root = graph[orientation].firstVertex(&ok); + + graphPaths[orientation].insert(root, GraphPath()); + + foreach (AnchorVertex *v, graph[orientation].adjacentVertices(root)) { + queue.enqueue(qMakePair(root, v)); + } + + while(!queue.isEmpty()) { + QPair pair = queue.dequeue(); + AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second); + + if (visited.contains(edge)) + continue; + + visited.insert(edge); + GraphPath current = graphPaths[orientation].value(pair.first); + + if (edge->origin == pair.first) + current.positives.insert(edge); + else + current.negatives.insert(edge); + + graphPaths[orientation].insert(pair.second, current); + + foreach (AnchorVertex *v, + graph[orientation].adjacentVertices(pair.second)) { + queue.enqueue(qMakePair(pair.second, v)); + } + } +} + +/*! + \internal + + Each vertex on the graph that has more than one path to it + represents a contra int to the sizes of the items in these paths. + + This method walks the list of paths to each vertex, generate + the constraints and store them in a list so they can be used later + by the Simplex solver. +*/ +void QGraphicsAnchorLayoutPrivate::constraintsFromPaths(Orientation orientation) +{ + foreach (AnchorVertex *vertex, graphPaths[orientation].uniqueKeys()) + { + int valueCount = graphPaths[orientation].count(vertex); + if (valueCount == 1) + continue; + + QList pathsToVertex = graphPaths[orientation].values(vertex); + for (int i = 1; i < valueCount; ++i) { + constraints[orientation] += \ + pathsToVertex[0].constraint(pathsToVertex[i]); + } + } +} + +/*! + \Internal +*/ +QList< QList > +QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + + // Find layout vertices and edges for the current orientation. + AnchorVertex *layoutFirstVertex = + m_vertexList.value(qMakePair(static_cast(q), + orientation == Horizontal ? + QGraphicsAnchorLayout::Left :QGraphicsAnchorLayout::Top)); + + AnchorVertex *layoutCentralVertex = + m_vertexList.value(qMakePair(static_cast(q), + orientation == Horizontal ? + QGraphicsAnchorLayout::HCenter : QGraphicsAnchorLayout::VCenter)); + + AnchorVertex *layoutLastVertex = + m_vertexList.value(qMakePair(static_cast(q), + orientation == Horizontal ? + QGraphicsAnchorLayout::Right : QGraphicsAnchorLayout::Bottom)); + + AnchorData *edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutCentralVertex); + AnchorData *edgeL2 = graph[orientation].edgeData(layoutCentralVertex, layoutLastVertex); + + QLinkedList remainingConstraints; + for (int i = 0; i < constraints[orientation].count(); ++i) { + remainingConstraints += constraints[orientation][i]; + } + for (int i = 0; i < itemCenterConstraints[orientation].count(); ++i) { + remainingConstraints += itemCenterConstraints[orientation][i]; + } + + QList trunkConstraints; + QSet trunkVariables; + + trunkVariables += edgeL1; + trunkVariables += edgeL2; + + bool dirty; + do { + dirty = false; + + QLinkedList::iterator it = remainingConstraints.begin(); + while (it != remainingConstraints.end()) { + QSimplexConstraint *c = *it; + bool match = false; + + // Check if this constraint have some overlap with current + // trunk variables... + foreach (QSimplexVariable *ad, trunkVariables) { + if (c->variables.contains(ad)) { + match = true; + break; + } + } + + // If so, we add it to trunk, and erase it from the + // remaining constraints. + if (match) { + trunkConstraints += c; + trunkVariables += QSet::fromList(c->variables.keys()); + it = remainingConstraints.erase(it); + dirty = true; + } else { + ++it; + } + } + } while (dirty); + + QList< QList > result; + result += trunkConstraints; + + if (!remainingConstraints.isEmpty()) { + QList nonTrunkConstraints; + QLinkedList::iterator it = remainingConstraints.begin(); + while (it != remainingConstraints.end()) { + nonTrunkConstraints += *it; + ++it; + } + result += nonTrunkConstraints; + } + + return result; +} + +/*! + \internal + + Use the current vertices distance to calculate and set the geometry of + each item. +*/ +void QGraphicsAnchorLayoutPrivate::setItemsGeometries() +{ + AnchorVertex *firstH, *secondH, *firstV, *secondV; + + foreach (QGraphicsLayoutItem *item, items) { + firstH = + m_vertexList.value(qMakePair(item, QGraphicsAnchorLayout::Left)); + secondH = + m_vertexList.value(qMakePair(item, QGraphicsAnchorLayout::Right)); + firstV = + m_vertexList.value(qMakePair(item, QGraphicsAnchorLayout::Top)); + secondV = + m_vertexList.value(qMakePair(item, QGraphicsAnchorLayout::Bottom)); + + QPointF topLeft(firstH->distance, firstV->distance); + QPointF bottomRight(secondH->distance, secondV->distance); + + item->setGeometry(QRectF(topLeft, bottomRight)); + } +} + +/*! + \internal + + Calculate the position of each vertex based on the paths to each of + them as well as the current edges sizes. +*/ +void QGraphicsAnchorLayoutPrivate::calculateVertexPositions( + QGraphicsAnchorLayoutPrivate::Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + QQueue > queue; + QSet visited; + + // Get root vertex + bool ok; + AnchorVertex *root = graph[orientation].firstVertex(&ok); + + qreal widgetMargin; + qreal layoutMargin; + + // Initialize the first vertex + if (orientation == Horizontal) { + widgetMargin = q->geometry().x(); + q->getContentsMargins(&layoutMargin, 0, 0, 0); + } else { + // Root position is equal to the top margin + widgetMargin = q->geometry().y(); + q->getContentsMargins(0, &layoutMargin, 0, 0); + } + root->distance = widgetMargin + layoutMargin; + visited.insert(root); + + // Add initial edges to the queue + foreach (AnchorVertex *v, graph[orientation].adjacentVertices(root)) { + queue.enqueue(qMakePair(root, v)); + } + + // Do initial calculation required by "interpolateEdge()" + setupEdgesInterpolation(orientation); + + // Traverse the graph and calculate vertex positions. + while (!queue.isEmpty()) { + QPair pair = queue.dequeue(); + + if (visited.contains(pair.second)) + continue; + visited.insert(pair.second); + + // The distance to the next vertex is equal the distance to the + // previous one plus (or less) the size of the edge between them. + qreal distance; + AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second); + + if (edge->origin == pair.first) { + distance = pair.first->distance + interpolateEdge(edge); + } else { + distance = pair.first->distance - interpolateEdge(edge); + } + pair.second->distance = distance; + + foreach (AnchorVertex *v, + graph[orientation].adjacentVertices(pair.second)) { + queue.enqueue(qMakePair(pair.second, v)); + } + } +} + +/*! + \internal + + Calculate interpolation parameters based on current Layout Size. + Must once before calling "interpolateEdgeSize()" for each edge. +*/ +void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( + Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + qreal lower, upper, current; + + if (orientation == Horizontal) { + current = q->contentsRect().width(); + } else { + current = q->contentsRect().height(); + } + + if (current < sizeHints[orientation][Qt::PreferredSize]) { + interpolationInterval[orientation] = MinToPreferred; + lower = sizeHints[orientation][Qt::MinimumSize]; + upper = sizeHints[orientation][Qt::PreferredSize]; + } else { + interpolationInterval[orientation] = PreferredToMax; + lower = sizeHints[orientation][Qt::PreferredSize]; + upper = sizeHints[orientation][Qt::MaximumSize]; + } + + if (upper == lower) { + interpolationProgress[orientation] = 0; + } else { + interpolationProgress[orientation] = (current - lower) / (upper - lower); + } +} + +/*! + \internal + + Calculate the current Edge size based on the current Layout size and the + size the edge is supposed to have when: + + - the layout is at its minimum size. + - the layout is at its preferred size. + - the layout is at its maximum size. + + These three key values are calculated in advance using linear programming + (more expensive), then subsequential resizes of the parent layout require + a simple interpolation. +*/ +qreal QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorData *edge) +{ + qreal lower, upper; + + Orientation orientation = edgeOrientation(edge->origin->m_edge); + + if (interpolationInterval[orientation] == MinToPreferred) { + lower = edge->sizeAtMinimum; + upper = edge->sizeAtPreferred; + } else { + lower = edge->sizeAtPreferred; + upper = edge->sizeAtMaximum; + } + + return (interpolationProgress[orientation] * (upper - lower)) + lower; +} + + +QPair +QGraphicsAnchorLayoutPrivate::solveMinMax(QList constraints, + GraphPath path) +{ + QList variables = getVariables(constraints); + QList itemConstraints; + + for (int i = 0; i < variables.size(); ++i) { + QSimplexConstraint *c = new QSimplexConstraint; + c->variables.insert(variables[i], 1.0); + c->constant = variables[i]->minSize; + c->ratio = QSimplexConstraint::MoreOrEqual; + itemConstraints += c; + + c = new QSimplexConstraint; + c->variables.insert(variables[i], 1.0); + c->constant = variables[i]->maxSize; + c->ratio = QSimplexConstraint::LessOrEqual; + itemConstraints += c; + } + + QSimplex simplex; + simplex.setConstraints(constraints + itemConstraints); + + // Obtain the objective constraint + QSimplexConstraint objective; + QSet::const_iterator iter; + for (iter = path.positives.constBegin(); iter != path.positives.constEnd(); ++iter) + objective.variables.insert(*iter, 1.0); + + for (iter = path.negatives.constBegin(); iter != path.negatives.constEnd(); ++iter) + objective.variables.insert(*iter, -1.0); + + simplex.setObjective(&objective); + + // Calculate minimum values + qreal min = simplex.solveMin(); + + // Save sizeAtMinimum results + for (int i = 0; i < variables.size(); ++i) { + AnchorData *ad = static_cast(variables[i]); + ad->sizeAtMinimum = ad->result; + } + + // Calculate maximum values + qreal max = simplex.solveMax(); + + // Save sizeAtMaximum results + for (int i = 0; i < variables.size(); ++i) { + AnchorData *ad = static_cast(variables[i]); + ad->sizeAtMaximum = ad->result; + } + + qDeleteAll(itemConstraints); + + return qMakePair(min, max); +} + +void QGraphicsAnchorLayoutPrivate::solvePreferred(QList constraints) +{ + QList variables = getVariables(constraints); + + // ### + QList itemConstraints; + + for (int i = 0; i < variables.size(); ++i) { + QSimplexConstraint *c = new QSimplexConstraint; + c->variables.insert(variables[i], 1.0); + c->constant = variables[i]->minSize; + c->ratio = QSimplexConstraint::MoreOrEqual; + itemConstraints += c; + + c = new QSimplexConstraint; + c->variables.insert(variables[i], 1.0); + c->constant = variables[i]->maxSize; + c->ratio = QSimplexConstraint::LessOrEqual; + itemConstraints += c; + } + + QList preferredConstraints; + QList preferredVariables; + QSimplexConstraint objective; + + // Fill the objective coefficients for this variable. In the + // end the objective function will be + // + // z = n * (A_shrink + B_shrink + ...) + (A_grower + B_grower + ...) + // + // where n is the number of variables that have + // slacks. Note that here we use the number of variables + // as coefficient, this is to mark the "shrinker slack + // variable" less likely to get value than the "grower + // slack variable". + + // This will fill the values for the structural constraints + // and we now fill the values for the slack constraints (one per variable), + // which have this form (the constant A_pref was set when creating the slacks): + // + // A + A_shrinker - A_grower = A_pref + // + for (int i = 0; i < variables.size(); ++i) { + QSimplexVariable *grower = new QSimplexVariable; + QSimplexVariable *shrinker = new QSimplexVariable; + QSimplexConstraint *c = new QSimplexConstraint; + c->variables.insert(variables[i], 1.0); + c->variables.insert(shrinker, 1.0); + c->variables.insert(grower, -1.0); + c->constant = variables[i]->prefSize; + + preferredConstraints += c; + preferredVariables += grower; + preferredVariables += shrinker; + + objective.variables.insert(grower, 1.0); + objective.variables.insert(shrinker, variables.size()); + } + + + QSimplex simplex; + simplex.setConstraints(constraints + itemConstraints + preferredConstraints); + + simplex.setObjective(&objective); + + // Calculate minimum values + qreal min = simplex.solveMin(); + + // Save sizeAtMinimum results + for (int i = 0; i < variables.size(); ++i) { + AnchorData *ad = static_cast(variables[i]); + ad->sizeAtPreferred = ad->result; + } + + qDeleteAll(itemConstraints); + qDeleteAll(preferredConstraints); + qDeleteAll(preferredVariables); + +} diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h new file mode 100644 index 0000000..e8f5783 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -0,0 +1,301 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qgraphicsanchorlayout.h" +#include "qgraph_p.h" +#include "qsimplex_p.h" + +/* + The public QGraphicsAnchorLayout interface represents an anchorage point + as a pair of a and a . + + Internally though, it has a graph of anchorage points (vertices) and + anchors (edges), represented by the AnchorVertex and AnchorData structs + respectively. +*/ + +/*! + \internal + + Represents a vertex (anchorage point) in the internal graph +*/ +struct AnchorVertex { + AnchorVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge) + : m_item(item), m_edge(edge) {} + + AnchorVertex() + : m_item(0), m_edge(QGraphicsAnchorLayout::Edge(0)) {} + + inline QString toString() const; + + QGraphicsLayoutItem *m_item; + QGraphicsAnchorLayout::Edge m_edge; + + // Current distance from this vertex to the layout edge (Left or Top) + // Value is calculated from the current anchors sizes. + qreal distance; +}; + +inline QString AnchorVertex::toString() const +{ + if (!this || !m_item) { + return QLatin1String("NULL"); + } + QString edge; + switch (m_edge) { + case QGraphicsAnchorLayout::Left: + edge = QLatin1String("Left"); + break; + case QGraphicsAnchorLayout::HCenter: + edge = QLatin1String("HorizontalCenter"); + break; + case QGraphicsAnchorLayout::Right: + edge = QLatin1String("Right"); + break; + case QGraphicsAnchorLayout::Top: + edge = QLatin1String("Top"); + break; + case QGraphicsAnchorLayout::VCenter: + edge = QLatin1String("VerticalCenter"); + break; + case QGraphicsAnchorLayout::Bottom: + edge = QLatin1String("Bottom"); + break; + default: + edge = QLatin1String("None"); + break; + } + QString item; + if (m_item->isLayout()) { + item = QLatin1String("layout"); + } else { + QGraphicsWidget *w = static_cast(m_item); + item = w->data(0).toString(); + } + edge.insert(0, QLatin1String("%1_")); + return edge.arg(item); +} + + +/*! + \internal + + Represents an edge (anchor) in the internal graph. +*/ +struct AnchorData : public QSimplexVariable { + AnchorData(qreal minimumSize, qreal preferredSize, qreal maximumSize) + : QSimplexVariable(), minSize(minimumSize), prefSize(preferredSize), + maxSize(maximumSize), sizeAtMinimum(preferredSize), + sizeAtPreferred(preferredSize), sizeAtMaximum(preferredSize), + skipInPreferred(0) {} + + AnchorData(qreal size = 0) + : QSimplexVariable(), minSize(size), prefSize(size), maxSize(size), + sizeAtMinimum(size), sizeAtPreferred(size), sizeAtMaximum(size), + skipInPreferred(0) {} + + inline QString toString() const; + QString name; + + // Anchor is semantically directed + AnchorVertex *origin; + + // Size restrictions of this edge. For anchors internal to items, these + // values are derived from the respective item size hints. For anchors + // that were added by users, these values are equal to the specified anchor + // size. + qreal minSize; + qreal prefSize; + qreal maxSize; + + // These attributes define which sizes should that anchor be in when the + // layout is at its minimum, preferred or maximum sizes. Values are + // calculated by the Simplex solver based on the current layout setup. + qreal sizeAtMinimum; + qreal sizeAtPreferred; + qreal sizeAtMaximum; + + uint skipInPreferred : 1; +}; + +inline QString AnchorData::toString() const +{ + return QString("Anchor(%1)").arg(name); + //return QString().sprintf("Anchor %%1 ", + // minSize, prefSize, maxSize).arg(name); +} + + +/*! + \internal + + Representation of a valid path for a given vertex in the graph. + In this struct, "positives" is the set of anchors that have been + traversed in the forward direction, while "negatives" is the set + with the ones walked backwards. + + This paths are compared against each other to produce LP Constraints, + the exact order in which the anchors were traversed is not relevant. +*/ +class GraphPath +{ +public: + GraphPath() {}; + + QSimplexConstraint *constraint(const GraphPath &path) const; + + QString toString() const; + + QSet positives; + QSet negatives; +}; + + +/*! + \internal + + QGraphicsAnchorLayout private methods and attributes. +*/ +class QGraphicsAnchorLayoutPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsAnchorLayout) + QGraphicsAnchorLayout *q_ptr; + +public: + // When the layout geometry is different from its Minimum, Preferred + // or Maximum values, interpolation is used to calculate the geometries + // of the items. + // + // Interval represents which interpolation interval are we operating in. + enum Interval { + MinToPreferred = 0, + PreferredToMax + }; + + // Several structures internal to the layout are duplicated to handle + // both Horizontal and Vertical restrictions. + // + // Orientation is used to reference the right structure in each context + enum Orientation { + Horizontal = 0, + Vertical + }; + + QGraphicsAnchorLayoutPrivate(); + + static QGraphicsAnchorLayout::Edge oppositeEdge( + QGraphicsAnchorLayout::Edge edge); + + static Orientation edgeOrientation(QGraphicsAnchorLayout::Edge edge); + + // Init methods + void createLayoutEdges(); + void createItemEdges(QGraphicsLayoutItem *item); + + // Anchor Manipulation methods + void addAnchor(QGraphicsLayoutItem *firstItem, + QGraphicsAnchorLayout::Edge firstEdge, + QGraphicsLayoutItem *secondItem, + QGraphicsAnchorLayout::Edge secondEdge, + AnchorData *data); + + void removeAnchor(QGraphicsLayoutItem *firstItem, + QGraphicsAnchorLayout::Edge firstEdge, + QGraphicsLayoutItem *secondItem, + QGraphicsAnchorLayout::Edge secondEdge); + + void removeAnchors(QGraphicsLayoutItem *item); + + void correctEdgeDirection(QGraphicsLayoutItem *&firstItem, + QGraphicsAnchorLayout::Edge &firstEdge, + QGraphicsLayoutItem *&secondItem, + QGraphicsAnchorLayout::Edge &secondEdge); + + // Child manipulation methods + QGraphicsItem *parentItem() const; + void addChildItem(QGraphicsLayoutItem *child); + + // Activation methods + void calculateGraphs(); + void calculateGraphs(Orientation orientation); + void setAnchorSizeHintsFromItems(Orientation orientation); + void findPaths(Orientation orientation); + void constraintsFromPaths(Orientation orientation); + QList > getGraphParts(Orientation orientation); + + // Geometry interpolation methods + void setItemsGeometries(); + void calculateVertexPositions(Orientation orientation); + void setupEdgesInterpolation(Orientation orientation); + qreal interpolateEdge(AnchorData *edge); + + // Linear Programming solver methods + QPair solveMinMax(QList constraints, + GraphPath path); + void solvePreferred(QList constraints); + + // Size hints from simplex engine + qreal sizeHints[2][3]; + + // Items + QVector items; + + // Mapping between high level anchorage points (Item, Edge) to low level + // ones (Graph Vertices) + QHash, AnchorVertex *> m_vertexList; + + // Internal graph of anchorage points and anchors, for both orientations + Graph graph[2]; + + // Graph paths and constraints, for both orientations + QMultiHash graphPaths[2]; + QList constraints[2]; + QList itemCenterConstraints[2]; + + // The interpolation interval and progress based on the current size + // as well as the key values (minimum, preferred and maximum) + Interval interpolationInterval[2]; + qreal interpolationProgress[2]; + + uint calculateGraphCacheDirty : 1; +}; -- cgit v0.12 From 0b0fd15c04486ad098d93e2cbedc23497f3ec110 Mon Sep 17 00:00:00 2001 From: Jesus Sanchez-Palencia Date: Thu, 28 May 2009 15:55:05 -0300 Subject: QGraphicsAnchorLayout: Adding auto-tests Signed-off-by: Anselmo Lacerda S. de Melo Signed-off-by: Caio Marcelo de Oliveira Filho Signed-off-by: Eduardo M. Fleury Signed-off-by: Jesus Sanchez-Palencia --- .../qgraphicsanchorlayout.pro | 3 + .../tst_qgraphicsanchorlayout.cpp | 351 +++++++++++++++++++++ tests/auto/tests.xml | 2 + 3 files changed, 356 insertions(+) create mode 100644 tests/auto/qgraphicsanchorlayout/qgraphicsanchorlayout.pro create mode 100644 tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp diff --git a/tests/auto/qgraphicsanchorlayout/qgraphicsanchorlayout.pro b/tests/auto/qgraphicsanchorlayout/qgraphicsanchorlayout.pro new file mode 100644 index 0000000..4c065f4 --- /dev/null +++ b/tests/auto/qgraphicsanchorlayout/qgraphicsanchorlayout.pro @@ -0,0 +1,3 @@ +load(qttest_p4) +SOURCES += tst_qgraphicsanchorlayout.cpp + diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp new file mode 100644 index 0000000..de6caac --- /dev/null +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -0,0 +1,351 @@ +#include +#include +#include +#include + +class tst_QGraphicsAnchorLayout : public QObject { + Q_OBJECT; + +private slots: + void simple(); + void diagonal(); + void parallel(); + void snake(); + void fairDistribution(); +}; + +static QGraphicsWidget *createItem(const QSizeF &minimum = QSizeF(100.0, 100.0), + const QSizeF &preferred = QSize(150.0, 100.0), + const QSizeF &maximum = QSizeF(200.0, 100.0)) +{ + QGraphicsWidget *w = new QGraphicsWidget; + w->setMinimumSize(minimum); + w->setPreferredSize(preferred); + w->setMaximumSize(maximum); + return w; +} + +void tst_QGraphicsAnchorLayout::simple() +{ + QGraphicsWidget *w1 = createItem(); + QGraphicsWidget *w2 = createItem(); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + l->anchor(w1, QGraphicsAnchorLayout::Right, w2, QGraphicsAnchorLayout::Left); + + QGraphicsWidget p; + p.setLayout(l); + + QCOMPARE(l->count(), 2); +} + +void tst_QGraphicsAnchorLayout::diagonal() +{ + QSizeF min(10, 100); + QSizeF pref(70, 100); + QSizeF max(100, 100); + + QGraphicsWidget *a = createItem(min, pref, max); + QGraphicsWidget *b = createItem(min, pref, max); + QGraphicsWidget *c = createItem(min, pref, max); + QGraphicsWidget *d = createItem(min, pref, max); + QGraphicsWidget *e = createItem(min, pref, max); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + // vertical + l->anchor(a, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top); + l->anchor(b, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top); + + l->anchor(c, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Bottom); + l->anchor(c, QGraphicsAnchorLayout::Top, b, QGraphicsAnchorLayout::Bottom); + l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top); + l->anchor(c, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top); + + l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + l->anchor(e, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + + // horizontal + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left); + l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left); + + l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left); + l->anchor(c, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left); + + l->anchor(b, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); + l->anchor(e, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); + l->anchor(d, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left); + + QCOMPARE(l->count(), 5); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(30.0, 300.0)); + QCOMPARE(layoutPreferredSize, QSizeF(170.0, 300.0)); + QCOMPARE(layoutMaximumSize, QSizeF(190.0, 300.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 10.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(10.0, 0.0, 20.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(10.0, 100.0, 10.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 200.0, 20.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(20.0, 200.0, 10.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 70.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(70.0, 0.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(70.0, 100.0, 30.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 200.0, 100.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 70.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 90.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(90.0, 0.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(90.0, 100.0, 10.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 200.0, 100.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 90.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); + + QSizeF testA(175.0, 300.0); + p.resize(testA); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 75.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(75.0, 0.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(75.0, 100.0, 25.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 200.0, 100.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(100.0, 200.0, 75.0, 100.0)); + QCOMPARE(p.size(), testA); +} + +void tst_QGraphicsAnchorLayout::parallel() +{ + QGraphicsWidget *a = createItem(QSizeF(100.0, 100.0), + QSizeF(150.0, 100.0), + QSizeF(200.0, 100.0)); + + QGraphicsWidget *b = createItem(QSizeF(100.0, 100.0), + QSizeF(150.0, 100.0), + QSizeF(200.0, 100.0)); + + QGraphicsWidget *c = createItem(QSizeF(100.0, 100.0), + QSizeF(200.0, 100.0), + QSizeF(300.0, 100.0)); + + QGraphicsWidget *d = createItem(QSizeF(100.0, 100.0), + QSizeF(170.0, 100.0), + QSizeF(200.0, 100.0)); + + QGraphicsWidget *e = createItem(QSizeF(150.0, 100.0), + QSizeF(150.0, 100.0), + QSizeF(200.0, 100.0)); + + QGraphicsWidget *f = createItem(QSizeF(100.0, 100.0), + QSizeF(150.0, 100.0), + QSizeF(200.0, 100.0)); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top); + l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top); + l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top); + l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top); + l->anchor(d, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top); + l->anchor(e, QGraphicsAnchorLayout::Bottom, f, QGraphicsAnchorLayout::Top); + l->anchor(f, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left); + l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left); + l->anchor(b, QGraphicsAnchorLayout::Right, d, QGraphicsAnchorLayout::Left); + l->anchor(b, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left); + l->anchor(c, QGraphicsAnchorLayout::Right, f, QGraphicsAnchorLayout::Left); + l->anchor(d, QGraphicsAnchorLayout::Right, f, QGraphicsAnchorLayout::Left); + l->anchor(e, QGraphicsAnchorLayout::Right, f, QGraphicsAnchorLayout::Left); + l->anchor(f, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); + + QCOMPARE(l->count(), 6); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + + QCOMPARE(layoutMinimumSize, QSizeF(450.0, 600.0)); + QCOMPARE(layoutPreferredSize, QSizeF(600.0, 600.0)); + QCOMPARE(layoutMaximumSize, QSizeF(700.0, 600.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 100.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(100.0, 100.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(100.0, 200.0, 250.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(200.0, 300.0, 150.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(200.0, 400.0, 150.0, 100.0)); + QCOMPARE(f->geometry(), QRectF(350.0, 500.0, 100.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 150.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(150.0, 100.0, 150.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(150.0, 200.0, 300.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(300.0, 300.0, 150.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(300.0, 400.0, 150.0, 100.0)); + QCOMPARE(f->geometry(), QRectF(450.0, 500.0, 150.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + // Maximum size depends on simplification / fair distribution + // Without that, test may or may not pass, depending on the + // solution found by the solver at runtime. + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 200.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(200.0, 100.0, 150.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(200.0, 200.0, 300.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(350.0, 300.0, 150.0, 100.0)); + QCOMPARE(e->geometry(), QRectF(350.0, 400.0, 150.0, 100.0)); + QCOMPARE(f->geometry(), QRectF(500.0, 500.0, 200.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); +} + +void tst_QGraphicsAnchorLayout::snake() +{ + QGraphicsWidget *a = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0)); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(20.0, 100.0), + QSizeF(40.0, 100.0)); + + QGraphicsWidget *c = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0)); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top); + l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top); + l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top); + l->anchor(c, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Right); + l->anchor(b, QGraphicsAnchorLayout::Left, c, QGraphicsAnchorLayout::Left); + l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); + + QCOMPARE(l->count(), 3); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(60.0, 300.0)); + QCOMPARE(layoutPreferredSize, QSizeF(120.0, 300.0)); + QCOMPARE(layoutMaximumSize, QSizeF(190.0, 300.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 50.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(10.0, 100.0, 40.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(10.0, 200.0, 50.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 70.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(50.0, 100.0, 20.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(50.0, 200.0, 70.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 100.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(90.0, 100.0, 10.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(90.0, 200.0, 100.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); +} + +void tst_QGraphicsAnchorLayout::fairDistribution() +{ + QGraphicsWidget *a = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0)); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0)); + + QGraphicsWidget *c = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0)); + + QGraphicsWidget *d = createItem(QSizeF(60.0, 100.0), + QSizeF(165.0, 100.0), + QSizeF(600.0, 100.0)); + + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top); + l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top); + l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top); + l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top); + l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left); + l->anchor(b, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left); + l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); + l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left); + l->anchor(d, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); + + QCOMPARE(l->count(), 4); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(60.0, 400.0)); + QCOMPARE(layoutPreferredSize, QSizeF(165.0, 400.0)); + QCOMPARE(layoutMaximumSize, QSizeF(300.0, 400.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 20.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(20.0, 100.0, 20.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(40.0, 200.0, 20.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 300.0, 60.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 55.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(55.0, 100.0, 55.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(110.0, 200.0, 55.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 300.0, 165.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 100.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(100.0, 100.0, 100.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(200.0, 200.0, 100.0, 100.0)); + QCOMPARE(d->geometry(), QRectF(0.0, 300.0, 300.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); +} + + +QTEST_MAIN(tst_QGraphicsAnchorLayout) +#include "tst_qgraphicsanchorlayout.moc" diff --git a/tests/auto/tests.xml b/tests/auto/tests.xml index a5386b2..29cdf32 100644 --- a/tests/auto/tests.xml +++ b/tests/auto/tests.xml @@ -128,6 +128,7 @@ + @@ -534,6 +535,7 @@ + -- cgit v0.12 From 2f002ef720412dc834daac46843d1439dc49680f Mon Sep 17 00:00:00 2001 From: Jesus Sanchez-Palencia Date: Thu, 28 May 2009 17:20:36 -0300 Subject: QGraphicsAnchorLayout: Making private class inherit from QGraphicsLayoutPrivate Signed-off-by: Jesus Sanchez-Palencia --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 8 +++----- src/gui/graphicsview/qgraphicsanchorlayout.h | 1 - src/gui/graphicsview/qgraphicsanchorlayout_p.h | 4 ++-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index d737e36..d91b4ab 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -42,12 +42,10 @@ #include "qgraphicsanchorlayout_p.h" QGraphicsAnchorLayout::QGraphicsAnchorLayout(QGraphicsLayoutItem *parent) - : QGraphicsLayout(parent), d_ptr(new QGraphicsAnchorLayoutPrivate()) + : QGraphicsLayout(*new QGraphicsAnchorLayoutPrivate(), parent) { - // ### REMOVE THAT - d_ptr->q_ptr = this; - - d_ptr->createLayoutEdges(); + Q_D(QGraphicsAnchorLayout); + d->createLayoutEdges(); } QGraphicsAnchorLayout::~QGraphicsAnchorLayout() diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h index 288aec1..2093b15 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -89,7 +89,6 @@ public: ///////// DEBUG ///////// void dumpGraph(); - QGraphicsAnchorLayoutPrivate *d_ptr; //### TO REMOVE protected: private: diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index e8f5783..65315fb 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -41,6 +41,7 @@ #include +#include "qgraphicslayout_p.h" #include "qgraphicsanchorlayout.h" #include "qgraph_p.h" #include "qsimplex_p.h" @@ -196,10 +197,9 @@ public: QGraphicsAnchorLayout private methods and attributes. */ -class QGraphicsAnchorLayoutPrivate +class QGraphicsAnchorLayoutPrivate : public QGraphicsLayoutPrivate { Q_DECLARE_PUBLIC(QGraphicsAnchorLayout) - QGraphicsAnchorLayout *q_ptr; public: // When the layout geometry is different from its Minimum, Preferred -- cgit v0.12 From d61d477d2487bdc5c1fc854106a82450b613673f Mon Sep 17 00:00:00 2001 From: Jesus Sanchez-Palencia Date: Thu, 28 May 2009 18:22:27 -0300 Subject: QGraphicsAnchorLayout: Adding an usage example Signed-off-by: Jesus Sanchez-Palencia --- .../graphicsview/anchorlayout/anchorlayout.pro | 14 ++ examples/graphicsview/anchorlayout/main.cpp | 144 +++++++++++++++++++++ examples/graphicsview/anchorlayout/noGUI.cpp | 83 ++++++++++++ examples/graphicsview/graphicsview.pro | 3 +- 4 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 examples/graphicsview/anchorlayout/anchorlayout.pro create mode 100644 examples/graphicsview/anchorlayout/main.cpp create mode 100644 examples/graphicsview/anchorlayout/noGUI.cpp diff --git a/examples/graphicsview/anchorlayout/anchorlayout.pro b/examples/graphicsview/anchorlayout/anchorlayout.pro new file mode 100644 index 0000000..c969c8b --- /dev/null +++ b/examples/graphicsview/anchorlayout/anchorlayout.pro @@ -0,0 +1,14 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Tue May 12 15:22:25 2009 +###################################################################### + +# Input +SOURCES += main.cpp + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/graphicsview/anchorlayout +sources.files = $$SOURCES $$HEADERS $$RESOURCES anchorlayout.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/graphicsview/anchorlayout +INSTALLS += target sources + +TARGET = anchorlayout_example diff --git a/examples/graphicsview/anchorlayout/main.cpp b/examples/graphicsview/anchorlayout/main.cpp new file mode 100644 index 0000000..d7563be --- /dev/null +++ b/examples/graphicsview/anchorlayout/main.cpp @@ -0,0 +1,144 @@ +#include +#include +#include +#include + +//#define BENCHMARK_RUN 1 +#define CALLGRIND_DEBUG 1 + +#if defined(BENCHMARK_RUN) + +// Callgrind command line: +// valgrind --tool=callgrind --instr-atstart=no ./anchorlayout >/dev/null 2>&1 + +#if defined(CALLGRIND_DEBUG) +#include +#else +#define CALLGRIND_START_INSTRUMENTATION do { } while (0) +#define CALLGRIND_STOP_INSTRUMENTATION do { } while (0) +#endif + +#endif + + +static QGraphicsProxyWidget *createItem(const QSizeF &minimum = QSizeF(100.0, 100.0), + const QSizeF &preferred = QSize(150.0, 100.0), + const QSizeF &maximum = QSizeF(200.0, 100.0), + const QString &name = "0") +{ + QGraphicsProxyWidget *w = new QGraphicsProxyWidget; + w->setWidget(new QPushButton(name)); + w->setData(0, name); + w->setMinimumSize(minimum); + w->setPreferredSize(preferred); + w->setMaximumSize(maximum); + + return w; +} + +class Test : public QObject +{ + Q_OBJECT; +public: + Test(QGraphicsWidget *wi, QGraphicsLayout* la) + : QObject(), w(wi), l(la), first(true) {} + + QGraphicsWidget *w; + QGraphicsLayout *l; + bool first; + +public slots: + void onTimer() { + if (first) { + first = false; + w->setContentsMargins(10, 10, 10, 10); + } else { + l->setContentsMargins(10, 10, 10, 10); + } + } +}; + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QGraphicsScene *scene = new QGraphicsScene(); + scene->setSceneRect(0, 0, 800, 480); + + QSizeF min(30, 100); + QSizeF pref(210, 100); + QSizeF max(300, 100); + + QGraphicsProxyWidget *a = createItem(min, pref, max, "A"); + QGraphicsProxyWidget *b = createItem(min, pref, max, "B"); + QGraphicsProxyWidget *c = createItem(min, pref, max, "C"); + QGraphicsProxyWidget *d = createItem(min, pref, max, "D"); + QGraphicsProxyWidget *e = createItem(min, pref, max, "E"); + QGraphicsProxyWidget *f = createItem(QSizeF(30, 50), QSizeF(150, 50), max, "F"); + QGraphicsProxyWidget *g = createItem(QSizeF(30, 50), QSizeF(30, 100), max, "G"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + + QGraphicsWidget *w = new QGraphicsWidget(0, Qt::Window); + w->setPos(20, 20); + w->setLayout(l); + + // vertical + l->anchor(a, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top); + l->anchor(b, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top); + + l->anchor(c, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Bottom); + l->anchor(c, QGraphicsAnchorLayout::Top, b, QGraphicsAnchorLayout::Bottom); + l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top); + l->anchor(c, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top); + + l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + l->anchor(e, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + + l->anchor(c, QGraphicsAnchorLayout::Top, f, QGraphicsAnchorLayout::Top); + l->anchor(c, QGraphicsAnchorLayout::VCenter, f, QGraphicsAnchorLayout::Bottom); + l->anchor(f, QGraphicsAnchorLayout::Bottom, g, QGraphicsAnchorLayout::Top); + l->anchor(c, QGraphicsAnchorLayout::Bottom, g, QGraphicsAnchorLayout::Bottom); + + // horizontal + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left); + l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left); + + l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left); + l->anchor(c, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left); + + l->anchor(b, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); + l->anchor(e, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); + l->anchor(d, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left); + + l->anchor(l, QGraphicsAnchorLayout::Left, f, QGraphicsAnchorLayout::Left); + l->anchor(l, QGraphicsAnchorLayout::Left, g, QGraphicsAnchorLayout::Left); + l->anchor(f, QGraphicsAnchorLayout::Right, g, QGraphicsAnchorLayout::Right); + +#ifdef BENCHMARK_RUN + CALLGRIND_START_INSTRUMENTATION; + for (int i = 0; i < 100; i++) { + l->invalidate(); + l->d_ptr->calculateGraphs(); + } + CALLGRIND_STOP_INSTRUMENTATION; + return 0; +#endif + + l->dumpGraph(); + + scene->addItem(w); + scene->setBackgroundBrush(Qt::darkGreen); + QGraphicsView *view = new QGraphicsView(scene); + view->show(); + + Test test(w, l); + QTimer timer; + test.connect(&timer, SIGNAL(timeout()), SLOT(onTimer())); + timer.start(5000); + + return app.exec(); +} + +#include "main.moc" diff --git a/examples/graphicsview/anchorlayout/noGUI.cpp b/examples/graphicsview/anchorlayout/noGUI.cpp new file mode 100644 index 0000000..3f04f9a --- /dev/null +++ b/examples/graphicsview/anchorlayout/noGUI.cpp @@ -0,0 +1,83 @@ +#include +#include "qgraphicsanchorlayout.h" + +// DEBUG +#include "qgraphicsanchorlayout_p.h" + +static QGraphicsWidget *createItem(const QSizeF &minimum = QSizeF(100.0, 100.0), + const QSizeF &preferred = QSize(150.0, 100.0), + const QSizeF &maximum = QSizeF(200.0, 100.0)) +{ + QGraphicsWidget *w = new QGraphicsWidget; + w->setMinimumSize(minimum); + w->setPreferredSize(preferred); + w->setMaximumSize(maximum); + return w; +} + + +int main(int argc, char **argv) +{ + Q_UNUSED(argc); + Q_UNUSED(argv); + + + QGraphicsWidget *a = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 200.0)); + a->setData(0, "A"); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(20.0, 100.0), + QSizeF(40.0, 200.0)); + b->setData(0, "B"); + + QGraphicsWidget *c = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 200.0)); + c->setData(0, "C"); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + + l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top); + l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top); + l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top); + l->anchor(c, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Right); + l->anchor(b, QGraphicsAnchorLayout::Left, c, QGraphicsAnchorLayout::Left); + l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); + + l->dumpGraph(); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + + qWarning() << "Layout Min/Pref/Max: " << layoutMinimumSize + << " " << layoutPreferredSize + << " " << layoutMaximumSize; + + + qWarning() << "\nSetting minimum size"; + l->setGeometry(QRectF(QPointF(0,0), layoutMinimumSize)); + qWarning() << "A: " << a->geometry(); + qWarning() << "B: " << b->geometry(); + qWarning() << "C: " << c->geometry(); + + qWarning() << "\nSetting maximum size"; + l->setGeometry(QRectF(QPointF(0,0), layoutMaximumSize)); + qWarning() << "A: " << a->geometry(); + qWarning() << "B: " << b->geometry(); + qWarning() << "C: " << c->geometry(); + + qWarning() << "\nSetting size 85.0"; + l->setGeometry(QRectF(QPointF(0,0), QSizeF(85.0, 200.0))); + qWarning() << "A: " << a->geometry(); + qWarning() << "B: " << b->geometry(); + qWarning() << "C: " << c->geometry(); + + return 0; +} diff --git a/examples/graphicsview/graphicsview.pro b/examples/graphicsview/graphicsview.pro index 66eb0b4..7238995 100644 --- a/examples/graphicsview/graphicsview.pro +++ b/examples/graphicsview/graphicsview.pro @@ -6,7 +6,8 @@ SUBDIRS = \ diagramscene \ dragdroprobot \ padnavigator \ - basicgraphicslayouts + basicgraphicslayouts \ + anchorlayout contains(QT_CONFIG, qt3support):SUBDIRS += portedcanvas portedasteroids contains(DEFINES, QT_NO_CURSOR): SUBDIRS -= dragdroprobot -- cgit v0.12 From 7aa43c713b99905036c5118e2031de1017663741 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 28 May 2009 18:36:11 -0300 Subject: QGraphicsAnchorLayout: Bugfix, set skipInPreferred flag for vertical We don't want the layout internal anchors to be used in the preferred size calculation, therefore we must set this flag. It was already being set for horizontal edges but for the vertical ones this was missing. Signed-off-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 84d1c31..c95baa3 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -157,11 +157,13 @@ void QGraphicsAnchorLayoutPrivate::createLayoutEdges() data = new AnchorData(0, 0, QWIDGETSIZE_MAX); addAnchor(layout, QGraphicsAnchorLayout::Top, layout, QGraphicsAnchorLayout::VCenter, data); + data->skipInPreferred = 1; c->variables.insert(data, 1.0); data = new AnchorData(0, 0, QWIDGETSIZE_MAX); addAnchor(layout, QGraphicsAnchorLayout::VCenter, layout, QGraphicsAnchorLayout::Bottom, data); + data->skipInPreferred = 1; c->variables.insert(data, -1.0); itemCenterConstraints[Vertical].append(c); -- cgit v0.12 From a4f2082d34f5ef337ec5f280bd2a531eefb32e7f Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 28 May 2009 12:37:00 -0300 Subject: QGraphicsAnchorLayout: Bugfix in preferred size calculation Observe the "skipInPreferred" flag in AnchorEdges to avoid adding the layout internal anchors to the preferred size calculation, what was leading to non-optimal results. Signed-off-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index c95baa3..d3d0072 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1020,13 +1020,17 @@ void QGraphicsAnchorLayoutPrivate::solvePreferred(QList co // A + A_shrinker - A_grower = A_pref // for (int i = 0; i < variables.size(); ++i) { + AnchorData *ad = static_cast(variables[i]); + if (ad->skipInPreferred) + continue; + QSimplexVariable *grower = new QSimplexVariable; QSimplexVariable *shrinker = new QSimplexVariable; QSimplexConstraint *c = new QSimplexConstraint; - c->variables.insert(variables[i], 1.0); + c->variables.insert(ad, 1.0); c->variables.insert(shrinker, 1.0); c->variables.insert(grower, -1.0); - c->constant = variables[i]->prefSize; + c->constant = ad->prefSize; preferredConstraints += c; preferredVariables += grower; @@ -1045,7 +1049,7 @@ void QGraphicsAnchorLayoutPrivate::solvePreferred(QList co // Calculate minimum values qreal min = simplex.solveMin(); - // Save sizeAtMinimum results + // Save sizeAtPreferred results for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = static_cast(variables[i]); ad->sizeAtPreferred = ad->result; -- cgit v0.12 From 4bd165d6849311b948ed87b9a51c7318ae6bd748 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 28 May 2009 15:47:30 -0300 Subject: QGraphicsAnchorLayoutExample: Delete objects to avoid memory leaks Delete objects from heap. --- examples/graphicsview/anchorlayout/main.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/graphicsview/anchorlayout/main.cpp b/examples/graphicsview/anchorlayout/main.cpp index d7563be..1436aca 100644 --- a/examples/graphicsview/anchorlayout/main.cpp +++ b/examples/graphicsview/anchorlayout/main.cpp @@ -138,7 +138,12 @@ int main(int argc, char **argv) test.connect(&timer, SIGNAL(timeout()), SLOT(onTimer())); timer.start(5000); - return app.exec(); + int rc = app.exec(); + + delete scene; + delete view; + + return rc; } #include "main.moc" -- cgit v0.12 From 12913735db966558ac255780aaee0ec0d1096a8c Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 28 May 2009 17:26:21 -0300 Subject: QGraphicsAnchorLayout: Delete internal layout edges in destructor The internal layout anchors were leaking, now we delete them when destroying the layout. Signed-off-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 2 ++ src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 10 ++++++++++ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 1 + 3 files changed, 13 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index d91b4ab..b3dcdd0 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -60,6 +60,8 @@ QGraphicsAnchorLayout::~QGraphicsAnchorLayout() delete item; } } + + d->deleteLayoutEdges(); } void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index d3d0072..6bcc1f4 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -173,6 +173,16 @@ void QGraphicsAnchorLayoutPrivate::createLayoutEdges() graph[Vertical].setRootVertex(v); } +void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges() +{ + Q_Q(QGraphicsAnchorLayout); + + removeAnchor(q, QGraphicsAnchorLayout::Left, q, QGraphicsAnchorLayout::HCenter); + removeAnchor(q, QGraphicsAnchorLayout::HCenter, q, QGraphicsAnchorLayout::Right); + removeAnchor(q, QGraphicsAnchorLayout::Top, q, QGraphicsAnchorLayout::VCenter); + removeAnchor(q, QGraphicsAnchorLayout::VCenter, q, QGraphicsAnchorLayout::Bottom); +} + void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) { items.append(item); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 65315fb..89d3bfc 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -230,6 +230,7 @@ public: // Init methods void createLayoutEdges(); + void deleteLayoutEdges(); void createItemEdges(QGraphicsLayoutItem *item); // Anchor Manipulation methods -- cgit v0.12 From 4cb58c203242e6e24d1628673b145a038c93531f Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 28 May 2009 17:27:44 -0300 Subject: QGraphicsAnchorLayout: Temporary destructor to avoid memory leaks Currently the itemCenterConstraints that are created when an item is added to the layout are never being deleted. Ideally this should be done by the time the item is removed from the layout, this requires some changes in the data structures used though. As a temporary solution we keep all of them until the layout is destroyed. Signed-off-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index b3dcdd0..260c823 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -62,6 +62,18 @@ QGraphicsAnchorLayout::~QGraphicsAnchorLayout() } d->deleteLayoutEdges(); + + // ### make something better here + qDeleteAll(d->itemCenterConstraints[0]); + d->itemCenterConstraints[0].clear(); + qDeleteAll(d->itemCenterConstraints[1]); + d->itemCenterConstraints[1].clear(); + + Q_ASSERT(d->items.isEmpty()); + Q_ASSERT(d->m_vertexList.isEmpty()); + + // ### Remove when integrated into Qt + delete d_ptr; } void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, -- cgit v0.12 From 35f33618ec766e4922977c31a5f522c261b19c61 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 28 May 2009 14:41:37 -0300 Subject: QGraphicsAnchorLayout: Apply Jan-Arve's patch Applying the patch sent by email. Signed-off-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraph_p.h | 27 +++-- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 16 +++ src/gui/graphicsview/qgraphicsanchorlayout.h | 9 +- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 142 ++++++++++++----------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 22 +++- 5 files changed, 136 insertions(+), 80 deletions(-) diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h index 4a6bc8f..e487eca 100644 --- a/src/gui/graphicsview/qgraph_p.h +++ b/src/gui/graphicsview/qgraph_p.h @@ -68,7 +68,8 @@ public: return iterator(this,false); } - EdgeData *edgeData(Vertex *first, Vertex *second) { + EdgeData *edgeData(Vertex* first, Vertex* second) { + Q_ASSERT(m_graph.value(first)); return m_graph.value(first)->value(second); } @@ -81,11 +82,20 @@ public: void removeEdge(Vertex *first, Vertex *second) { - // Creates a bidirectional edge + // Removes a bidirectional edge EdgeData *data = edgeData(first, second); + removeDirectedEdge(first, second); + removeDirectedEdge(second, first); if (data) delete data; + } + + EdgeData *takeEdge(Vertex* first, Vertex* second) + { + // Removes a bidirectional edge + EdgeData *data = edgeData(first, second); removeDirectedEdge(first, second); removeDirectedEdge(second, first); + return data; } QList adjacentVertices(Vertex *vertex) const @@ -163,12 +173,13 @@ protected: void removeDirectedEdge(Vertex *from, Vertex *to) { QHash *adjacentToFirst = m_graph.value(from); - adjacentToFirst->remove(to); - if (adjacentToFirst->isEmpty()) { - //nobody point to 'from' so we can remove it from the graph - QHash *adjacentToFirst = m_graph.take(from); - delete adjacentToFirst; - delete from; + if (adjacentToFirst) { + adjacentToFirst->remove(to); + if (adjacentToFirst->isEmpty()) { + //nobody point to 'from' so we can remove it from the graph + m_graph.remove(from); + delete adjacentToFirst; + } } } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 260c823..631ce0c 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -160,6 +160,21 @@ void QGraphicsAnchorLayout::removeAnchor(QGraphicsLayoutItem *firstItem, Edge fi invalidate(); } +void QGraphicsAnchorLayout::setSpacing(qreal spacing, Qt::Orientations orientations /*= Qt::Horizontal|Qt::Vertical*/) +{ + Q_D(QGraphicsAnchorLayout); + if (orientations & Qt::Horizontal) + d->spacing[0] = spacing; + if (orientations & Qt::Vertical) + d->spacing[1] = spacing; +} + +qreal QGraphicsAnchorLayout::spacing(Qt::Orientation orientation) const +{ + Q_D(const QGraphicsAnchorLayout); + return d->spacing[orientation & Qt::Vertical]; +} + void QGraphicsAnchorLayout::setGeometry(const QRectF &geom) { Q_D(QGraphicsAnchorLayout); @@ -179,6 +194,7 @@ void QGraphicsAnchorLayout::removeAt(int index) d->removeAnchors(item); item->setParentLayoutItem(0); } + invalidate(); } int QGraphicsAnchorLayout::count() const diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h index 2093b15..fb6c396 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -45,7 +45,7 @@ #include #include -/* + QT_BEGIN_HEADER QT_BEGIN_NAMESPACE @@ -53,7 +53,6 @@ QT_BEGIN_NAMESPACE QT_MODULE(Gui) #if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW -*/ class QGraphicsAnchorLayoutPrivate; @@ -79,6 +78,9 @@ public: void removeAnchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, QGraphicsLayoutItem *secondItem, Edge secondEdge); + void setSpacing(qreal spacing, Qt::Orientations orientations = Qt::Horizontal|Qt::Vertical); + qreal spacing(Qt::Orientation) const; + void removeAt(int index); void setGeometry(const QRectF &rect); int count() const; @@ -97,12 +99,11 @@ private: Q_DECLARE_PRIVATE(QGraphicsAnchorLayout) }; -/* + #endif QT_END_NAMESPACE QT_END_HEADER -*/ #endif diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 6bcc1f4..1f6813f 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -73,12 +73,12 @@ QSimplexConstraint *GraphPath::constraint(const GraphPath &path) const QString GraphPath::toString() const { - QString string("Path: "); + QString string(QLatin1String("Path: ")); foreach(AnchorData *edge, positives) - string += QString(" (+++) %1").arg(edge->toString()); + string += QString::fromAscii(" (+++) %1").arg(edge->toString()); foreach(AnchorData *edge, negatives) - string += QString(" (---) %1").arg(edge->toString()); + string += QString::fromAscii(" (---) %1").arg(edge->toString()); return string; } @@ -148,8 +148,7 @@ void QGraphicsAnchorLayoutPrivate::createLayoutEdges() itemCenterConstraints[Horizontal].append(c); // Set the Layout Left edge as the root of the horizontal graph. - AnchorVertex *v; - v = m_vertexList.value(qMakePair(layout, QGraphicsAnchorLayout::Left)); + AnchorVertex *v = internalVertex(layout, QGraphicsAnchorLayout::Left); graph[Horizontal].setRootVertex(v); // Vertical @@ -169,7 +168,7 @@ void QGraphicsAnchorLayoutPrivate::createLayoutEdges() itemCenterConstraints[Vertical].append(c); // Set the Layout Top edge as the root of the vertical graph. - v = m_vertexList.value(qMakePair(layout, QGraphicsAnchorLayout::Top)); + v = internalVertex(layout, QGraphicsAnchorLayout::Top); graph[Vertical].setRootVertex(v); } @@ -232,53 +231,65 @@ void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, QGraphicsAnchorLayout::Edge secondEdge, AnchorData *data) { - AnchorVertex *v1; - AnchorVertex *v2; - // Is the Vertex (firstItem, firstEdge) already represented in our // internal structure? - v1 = m_vertexList.value(qMakePair(firstItem, firstEdge)); - if (!v1) { - v1 = new AnchorVertex(firstItem, firstEdge); - m_vertexList.insert(qMakePair(firstItem, firstEdge), v1); - } - - // The same for the second vertex - v2 = m_vertexList.value(qMakePair(secondItem, secondEdge)); - if (!v2) { - v2 = new AnchorVertex(secondItem, secondEdge); - m_vertexList.insert(qMakePair(secondItem, secondEdge), v2); - } + AnchorVertex *v1 = addInternalVertex(firstItem, firstEdge); + AnchorVertex *v2 = addInternalVertex(secondItem, secondEdge); // 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. data->origin = v1; - data->name = QString("%1 --to--> %2").arg(v1->toString()).arg(v2->toString()); + data->name = QString::fromAscii("%1 --to--> %2").arg(v1->toString()).arg(v2->toString()); graph[edgeOrientation(firstEdge)].createEdge(v1, v2, data); } +AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutItem *item, + QGraphicsAnchorLayout::Edge edge) +{ + QPair pair(item, edge); + QPair v = m_vertexList.value(pair); + if (!v.first) { + Q_ASSERT(v.second == 0); + v.first = new AnchorVertex(item, edge); + m_vertexList.insert(pair, v); + } + v.second++; + return v.first; +} + +/** + * \internal + * + * returns the AnchorVertex that was dereferenced, also when it was removed. + * returns 0 if it did not exist. + */ +AnchorVertex *QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *item, + QGraphicsAnchorLayout::Edge edge) +{ + QPair pair(item, edge); + QPair v = m_vertexList.value(pair); + if (v.first) { + v.second--; + if (v.second == 0) { + m_vertexList.remove(pair); + } + } else { + qWarning("This item with this edge is not in the graph"); + } + return v.first; +} + void QGraphicsAnchorLayoutPrivate::removeAnchor(QGraphicsLayoutItem *firstItem, QGraphicsAnchorLayout::Edge firstEdge, QGraphicsLayoutItem *secondItem, QGraphicsAnchorLayout::Edge secondEdge) { - AnchorVertex *v1 = 0; - AnchorVertex *v2 = 0; - // Is there a representation for the Vertex (firstItem, firstEdge) // in our internal structure? - if ((v1 = m_vertexList.value(qMakePair(firstItem,firstEdge)))) - m_vertexList.remove(qMakePair(firstItem,firstEdge)); - else - qWarning()<<"This item with this edge is not in the graph"; - - // The same for the second vertex - if ((v2 = m_vertexList.value(qMakePair(secondItem,secondEdge)))) - m_vertexList.remove(qMakePair(secondItem,secondEdge)); - else - qWarning()<<"This item with this edge is not in the graph"; + AnchorVertex *v1 = removeInternalVertex(firstItem, firstEdge); + AnchorVertex *v2 = removeInternalVertex(secondItem, secondEdge); if (v1 && v2) { graph[edgeOrientation(firstEdge)].removeEdge(v1, v2); @@ -292,18 +303,26 @@ void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item) QList allVertex; int edge; - for (edge = QGraphicsAnchorLayout::Left; edge != QGraphicsAnchorLayout::Bottom; ++edge) { + for (edge = QGraphicsAnchorLayout::Left; edge <= QGraphicsAnchorLayout::Bottom; ++edge) { // Remove all vertex for all edges QGraphicsAnchorLayout::Edge e = static_cast(edge); - if ((v1 = m_vertexList.value(qMakePair(item, e)))) { - m_vertexList.remove(qMakePair(item, e)); - + if ((v1 = removeInternalVertex(item, e))) { // Remove all edges allVertex = graph[edgeOrientation(e)].adjacentVertices(v1); - foreach (v2, allVertex) - graph[edgeOrientation(e)].removeEdge(v1, v2); + QList constraints = itemCenterConstraints[edgeOrientation(e)]; + foreach (v2, allVertex) { + AnchorData *data = graph[edgeOrientation(e)].takeEdge(v1, v2); + Q_ASSERT(data); + for (int i = 0; i < constraints.count(); ++i) { + QSimplexConstraint *c = constraints.at(i); + c->variables.remove(data); + } + delete data; + } + qDebug("removing anchor: %s", qPrintable(v1->toString())); + delete v1; } } } @@ -467,8 +486,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( } else { end = QGraphicsAnchorLayout::Bottom; } - AnchorVertex *v = - m_vertexList.value(qMakePair(static_cast(q), end)); + AnchorVertex *v = internalVertex(q, end); GraphPath trunkPath = graphPaths[orientation].value(v); // Solve min and max size hints for trunk @@ -509,7 +527,8 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // Propagate size at preferred to other sizes. Semi-floats // always will be in their sizeAtPreferred. for (int j = 0; j < partVariables.count(); ++j) { - AnchorData *ad = static_cast(partVariables[j]); + AnchorData *ad = partVariables[j]; + Q_ASSERT(ad); ad->sizeAtMinimum = ad->sizeAtPreferred; ad->sizeAtMaximum = ad->sizeAtPreferred; } @@ -546,9 +565,9 @@ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orien centerKey.first = item; endKey.first = item; - beginning = m_vertexList.value(beginningKey); - center = m_vertexList.value(centerKey); - end = m_vertexList.value(endKey); + beginning = internalVertex(beginningKey); + center = internalVertex(centerKey); + end = internalVertex(endKey); if (orientation == Horizontal) { min = item->minimumWidth(); @@ -690,19 +709,16 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) // Find layout vertices and edges for the current orientation. AnchorVertex *layoutFirstVertex = - m_vertexList.value(qMakePair(static_cast(q), - orientation == Horizontal ? - QGraphicsAnchorLayout::Left :QGraphicsAnchorLayout::Top)); + internalVertex(q, orientation == Horizontal ? + QGraphicsAnchorLayout::Left : QGraphicsAnchorLayout::Top); AnchorVertex *layoutCentralVertex = - m_vertexList.value(qMakePair(static_cast(q), - orientation == Horizontal ? - QGraphicsAnchorLayout::HCenter : QGraphicsAnchorLayout::VCenter)); + internalVertex(q, orientation == Horizontal ? + QGraphicsAnchorLayout::HCenter : QGraphicsAnchorLayout::VCenter); AnchorVertex *layoutLastVertex = - m_vertexList.value(qMakePair(static_cast(q), - orientation == Horizontal ? - QGraphicsAnchorLayout::Right : QGraphicsAnchorLayout::Bottom)); + internalVertex(q, orientation == Horizontal ? + QGraphicsAnchorLayout::Right : QGraphicsAnchorLayout::Bottom); AnchorData *edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutCentralVertex); AnchorData *edgeL2 = graph[orientation].edgeData(layoutCentralVertex, layoutLastVertex); @@ -779,14 +795,10 @@ void QGraphicsAnchorLayoutPrivate::setItemsGeometries() AnchorVertex *firstH, *secondH, *firstV, *secondV; foreach (QGraphicsLayoutItem *item, items) { - firstH = - m_vertexList.value(qMakePair(item, QGraphicsAnchorLayout::Left)); - secondH = - m_vertexList.value(qMakePair(item, QGraphicsAnchorLayout::Right)); - firstV = - m_vertexList.value(qMakePair(item, QGraphicsAnchorLayout::Top)); - secondV = - m_vertexList.value(qMakePair(item, QGraphicsAnchorLayout::Bottom)); + firstH = internalVertex(item, QGraphicsAnchorLayout::Left); + secondH = internalVertex(item, QGraphicsAnchorLayout::Right); + firstV = internalVertex(item, QGraphicsAnchorLayout::Top); + secondV = internalVertex(item, QGraphicsAnchorLayout::Bottom); QPointF topLeft(firstH->distance, firstV->distance); QPointF bottomRight(secondH->distance, secondV->distance); @@ -1057,7 +1069,7 @@ void QGraphicsAnchorLayoutPrivate::solvePreferred(QList co simplex.setObjective(&objective); // Calculate minimum values - qreal min = simplex.solveMin(); + simplex.solveMin(); // Save sizeAtPreferred results for (int i = 0; i < variables.size(); ++i) { diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 89d3bfc..ee1a181 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -161,7 +161,7 @@ struct AnchorData : public QSimplexVariable { inline QString AnchorData::toString() const { - return QString("Anchor(%1)").arg(name); + return QString::fromAscii("Anchor(%1)").arg(name); //return QString().sprintf("Anchor %%1 ", // minSize, prefSize, maxSize).arg(name); } @@ -218,7 +218,8 @@ public: // Orientation is used to reference the right structure in each context enum Orientation { Horizontal = 0, - Vertical + Vertical, + NOrientations }; QGraphicsAnchorLayoutPrivate(); @@ -264,6 +265,19 @@ public: void constraintsFromPaths(Orientation orientation); QList > getGraphParts(Orientation orientation); + inline AnchorVertex *internalVertex(const QPair &itemEdge) + { + return m_vertexList.value(itemEdge).first; + } + + inline AnchorVertex *internalVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge) + { + return internalVertex(qMakePair(item, edge)); + } + + AnchorVertex *addInternalVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge); + AnchorVertex *removeInternalVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge); + // Geometry interpolation methods void setItemsGeometries(); void calculateVertexPositions(Orientation orientation); @@ -275,6 +289,7 @@ public: GraphPath path); void solvePreferred(QList constraints); + qreal spacing[NOrientations]; // Size hints from simplex engine qreal sizeHints[2][3]; @@ -283,7 +298,8 @@ public: // Mapping between high level anchorage points (Item, Edge) to low level // ones (Graph Vertices) - QHash, AnchorVertex *> m_vertexList; + + QHash, QPair > m_vertexList; // Internal graph of anchorage points and anchors, for both orientations Graph graph[2]; -- cgit v0.12 From a5e99a6812e8a721dac29554ebcaa92c6d3fdd3a Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 28 May 2009 15:48:44 -0300 Subject: QGraphicsAnchorLayout: Bugfix in spacing() method In the public Qt::Orientation enum, Horizontal and Vertical mean 0x1 and 0x2 respectivelly, this was causing the internal data structure to be accessed in positions 0 and 2 instead of 0 and 1. Signed-off-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 631ce0c..166e6f2 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -172,7 +172,8 @@ void QGraphicsAnchorLayout::setSpacing(qreal spacing, Qt::Orientations orientati qreal QGraphicsAnchorLayout::spacing(Qt::Orientation orientation) const { Q_D(const QGraphicsAnchorLayout); - return d->spacing[orientation & Qt::Vertical]; + // Qt::Horizontal == 0x1, Qt::Vertical = 0x2 + return d->spacing[orientation - 1]; } void QGraphicsAnchorLayout::setGeometry(const QRectF &geom) -- cgit v0.12 From 1373081ce19698e50fdde95ab18012950f729d62 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 28 May 2009 17:19:17 -0300 Subject: QGraphicsAnchorLayout: Update m_vertexList reference count system The reference count system used to delete AnchorVertex objects stored in m_vertexList had an issue that prevented the count values from being stored properly. This is a tentative fix for the memory leaks in the layout, changes were made to the m_vertexList access methods as well as those that use them. 1) addInternalVertex() After incrementing the ref count, store it in m_vertexList. 2) removeInternalVertex() To make it symmetrical to addInternalVertex() we delete the anchor vertex if its reach count reaches zero. From now on, the only methods that create or delete vertices are these two. All other methods should rely on add/removeInternalVertex. Also made the method 'void'. This method is called to release the associated object and may trigger a deletion of it, thus any use of the object reference after that is unsafe. Other methods can get a reference with internalVertex(). 3) removeAnchor() Follow above instructions. No longer relies on the reference returned by removeInternalVertex(), instead, use the reference first then release. Note: In this particular case, we know that "removeEdge(v1, v2)" will only access the contents of the pointers, not the referenced object. Yet, the change probably pays itself for the sake of clarity and safety. 4) removeAnchors() Each tiime an anchor is created, the reference count of its both vertices is incremented, thus we should decrement that count every time we remove an anchor. (Instead of removing v1 only once). Also, leave the task of deleting v1 to removeInternalVertex() as explained above. Removing the constraints handling code. I understand that we cannot leave constraints with dangling pointers inside of them, however that raises an even worse problem. Every time an item is added, we create an item center constraint, thus we must be sure to delete those when the item is removed. Once we do that, we won't have bad constraints laying around. This issue remains open in this commit but will be solved soon. Signed-off-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 54 +++++++++++++----------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 2 +- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 1f6813f..b0ffea4 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -250,12 +250,13 @@ AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutIte { QPair pair(item, edge); QPair v = m_vertexList.value(pair); + if (!v.first) { Q_ASSERT(v.second == 0); v.first = new AnchorVertex(item, edge); - m_vertexList.insert(pair, v); } v.second++; + m_vertexList.insert(pair, v); return v.first; } @@ -265,20 +266,26 @@ AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutIte * returns the AnchorVertex that was dereferenced, also when it was removed. * returns 0 if it did not exist. */ -AnchorVertex *QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *item, - QGraphicsAnchorLayout::Edge edge) +void QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *item, + QGraphicsAnchorLayout::Edge edge) { QPair pair(item, edge); QPair v = m_vertexList.value(pair); - if (v.first) { - v.second--; - if (v.second == 0) { - m_vertexList.remove(pair); - } - } else { + + if (!v.first) { qWarning("This item with this edge is not in the graph"); + return; + } + + v.second--; + if (v.second == 0) { + // Remove reference and delete vertex + m_vertexList.remove(pair); + delete v.first; + } else { + // Update reference count + m_vertexList.insert(pair, v); } - return v.first; } void QGraphicsAnchorLayoutPrivate::removeAnchor(QGraphicsLayoutItem *firstItem, @@ -286,14 +293,18 @@ void QGraphicsAnchorLayoutPrivate::removeAnchor(QGraphicsLayoutItem *firstItem, QGraphicsLayoutItem *secondItem, QGraphicsAnchorLayout::Edge secondEdge) { - // Is there a representation for the Vertex (firstItem, firstEdge) - // in our internal structure? - AnchorVertex *v1 = removeInternalVertex(firstItem, firstEdge); - AnchorVertex *v2 = removeInternalVertex(secondItem, secondEdge); + // Look for both vertices + AnchorVertex *v1 = internalVertex(firstItem, firstEdge); + AnchorVertex *v2 = internalVertex(secondItem, secondEdge); + // Remove edge from graph if (v1 && v2) { graph[edgeOrientation(firstEdge)].removeEdge(v1, v2); } + + // Decrease vertices reference count (may trigger a deletion) + removeInternalVertex(firstItem, firstEdge); + removeInternalVertex(secondItem, secondEdge); } void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item) @@ -307,22 +318,15 @@ void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item) // Remove all vertex for all edges QGraphicsAnchorLayout::Edge e = static_cast(edge); - if ((v1 = removeInternalVertex(item, e))) { + if ((v1 = internalVertex(item, e))) { // Remove all edges allVertex = graph[edgeOrientation(e)].adjacentVertices(v1); - QList constraints = itemCenterConstraints[edgeOrientation(e)]; foreach (v2, allVertex) { - AnchorData *data = graph[edgeOrientation(e)].takeEdge(v1, v2); - Q_ASSERT(data); - for (int i = 0; i < constraints.count(); ++i) { - QSimplexConstraint *c = constraints.at(i); - c->variables.remove(data); - } - delete data; + graph[edgeOrientation(e)].removeEdge(v1, v2); + removeInternalVertex(item, e); + removeInternalVertex(v2->m_item, v2->m_edge); } - qDebug("removing anchor: %s", qPrintable(v1->toString())); - delete v1; } } } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index ee1a181..27855e6 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -276,7 +276,7 @@ public: } AnchorVertex *addInternalVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge); - AnchorVertex *removeInternalVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge); + void removeInternalVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge); // Geometry interpolation methods void setItemsGeometries(); -- cgit v0.12 From bf3de451f1532e10aef53b185120ae8d452c55aa Mon Sep 17 00:00:00 2001 From: "Anselmo Lacerda S. de Melo" Date: Thu, 28 May 2009 19:08:52 -0300 Subject: QGraphicsAnchorLayout: Removing delete d_ptr Removed an obsolete delete d_ptr, since it is not needed anymore. Signed-off-by: Anselmo Lacerda S. de Melo --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 166e6f2..7cde4ec 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -71,9 +71,6 @@ QGraphicsAnchorLayout::~QGraphicsAnchorLayout() Q_ASSERT(d->items.isEmpty()); Q_ASSERT(d->m_vertexList.isEmpty()); - - // ### Remove when integrated into Qt - delete d_ptr; } void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, -- cgit v0.12 From 4cecce2273d45e42136f70d3ae6453aa37e1d934 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 28 May 2009 20:27:11 -0300 Subject: QGraphicsAnchorLayout: Delete center constraints on item removal Each time an item is added to the layout, center constraits are created to ensure its internal anchors keep their size equal. To avoid memory leaks, we must delete them when the item is removed. Signed-off-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 24 +++++++++++---------- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 27 ++++++++++++++++++++++++ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 1 + 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 7cde4ec..ba3ba2a 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -61,14 +61,12 @@ QGraphicsAnchorLayout::~QGraphicsAnchorLayout() } } + d->removeCenterConstraints(this, QGraphicsAnchorLayoutPrivate::Horizontal); + d->removeCenterConstraints(this, QGraphicsAnchorLayoutPrivate::Vertical); d->deleteLayoutEdges(); - // ### make something better here - qDeleteAll(d->itemCenterConstraints[0]); - d->itemCenterConstraints[0].clear(); - qDeleteAll(d->itemCenterConstraints[1]); - d->itemCenterConstraints[1].clear(); - + Q_ASSERT(d->itemCenterConstraints[0].isEmpty()); + Q_ASSERT(d->itemCenterConstraints[1].isEmpty()); Q_ASSERT(d->items.isEmpty()); Q_ASSERT(d->m_vertexList.isEmpty()); } @@ -187,11 +185,15 @@ void QGraphicsAnchorLayout::removeAt(int index) Q_D(QGraphicsAnchorLayout); QGraphicsLayoutItem *item = d->items.value(index); - if (item) { - d->items.remove(index); - d->removeAnchors(item); - item->setParentLayoutItem(0); - } + if (!item) + return; + + d->removeCenterConstraints(item, QGraphicsAnchorLayoutPrivate::Horizontal); + d->removeCenterConstraints(item, QGraphicsAnchorLayoutPrivate::Vertical); + d->removeAnchors(item); + d->items.remove(index); + + item->setParentLayoutItem(0); invalidate(); } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index b0ffea4..8158f48 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -225,6 +225,33 @@ void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) itemCenterConstraints[Vertical].append(c); } +void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem *item, + Orientation orientation) +{ + // Remove the item center constraints associated to this item + // ### This is a temporary solution. We should probably use a better + // data structure to hold items and/or their associated constraints + // so that we can remove those easily + + AnchorVertex *first = internalVertex(item, orientation == Horizontal ? + QGraphicsAnchorLayout::Left : + QGraphicsAnchorLayout::Top); + AnchorVertex *second = internalVertex(item, orientation == Horizontal ? + QGraphicsAnchorLayout::HCenter : + QGraphicsAnchorLayout::VCenter); + + Q_ASSERT(first && second); + AnchorData *internalAnchor = graph[orientation].edgeData(first, second); + + // Look for our anchor in all item center constraints, then remove it + for (int i = 0; i < itemCenterConstraints[orientation].size(); ++i) { + if (itemCenterConstraints[orientation][i]->variables.contains(internalAnchor)) { + delete itemCenterConstraints[orientation].takeAt(i); + break; + } + } +} + void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, QGraphicsAnchorLayout::Edge firstEdge, QGraphicsLayoutItem *secondItem, diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 27855e6..6370f4a 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -233,6 +233,7 @@ public: void createLayoutEdges(); void deleteLayoutEdges(); void createItemEdges(QGraphicsLayoutItem *item); + void removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation); // Anchor Manipulation methods void addAnchor(QGraphicsLayoutItem *firstItem, -- cgit v0.12 From efb7495717b7fd84996a59ee99c9632aa4f43467 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Fri, 29 May 2009 13:44:24 -0300 Subject: QGraphicsAnchorLayout: Fix memory management issue in QSimplex solver Both QGraphicsAnchorLayoutPrivate::solveMinMax() and solvePreferred() were deleting the linear programming contraints while QSimplex still had them. For solveMinMax(), move the creation of constraints out of that method. For solvePreferred(), create the simplex solver in the heap so it can be deleted before the constraints are. Signed-off-by: Eduardo M. Fleury Reviewed-by: Anselmo Lacerda S. de Melo --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 101 ++++++++++++----------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 1 + src/gui/graphicsview/qsimplex_p.cpp | 2 +- 3 files changed, 54 insertions(+), 50 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 8158f48..644ed6b 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -506,6 +506,9 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // of the "trunk" set of constraints and variables. // ### does trunk always exist? empty = trunk is the layout left->center->right QList trunkConstraints = parts[0]; + QList sizeHintConstraints; + sizeHintConstraints = constraintsFromSizeHints(getVariables(trunkConstraints)); + trunkConstraints += sizeHintConstraints; // For minimum and maximum, use the path between the two layout sides as the // objective function. @@ -540,6 +543,10 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( } sizeHints[orientation][Qt::PreferredSize] = pref; + // Delete the constraints, we won't use them anymore. + qDeleteAll(sizeHintConstraints); + sizeHintConstraints.clear(); + // For the other parts that not the trunk, solve only for the preferred size // that is the size they will remain at, since they are not stretched by the // layout. @@ -548,11 +555,10 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( for (int i = 1; i < parts.count(); ++i) { QList partConstraints = parts[i]; QList partVariables = getVariables(partConstraints); + Q_ASSERT(!partVariables.isEmpty()); - // ### - if (partVariables.isEmpty()) - continue; - + sizeHintConstraints = constraintsFromSizeHints(partVariables); + partConstraints += sizeHintConstraints; solvePreferred(partConstraints); // Propagate size at preferred to other sizes. Semi-floats @@ -563,6 +569,10 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( ad->sizeAtMinimum = ad->sizeAtPreferred; ad->sizeAtMaximum = ad->sizeAtPreferred; } + + // Delete the constraints, we won't use them anymore. + qDeleteAll(sizeHintConstraints); + sizeHintConstraints.clear(); } // Clean up our data structures. They are not needed anymore since @@ -731,6 +741,33 @@ void QGraphicsAnchorLayoutPrivate::constraintsFromPaths(Orientation orientation) } /*! + \internal + + Create LP constraints for each anchor based on its minimum and maximum + sizes, as specified in its size hints +*/ +QList QGraphicsAnchorLayoutPrivate::constraintsFromSizeHints( + const QList &anchors) +{ + QList 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; + } + + return anchorConstraints; +} + +/*! \Internal */ QList< QList > @@ -976,25 +1013,8 @@ QPair QGraphicsAnchorLayoutPrivate::solveMinMax(QList constraints, GraphPath path) { - QList variables = getVariables(constraints); - QList itemConstraints; - - for (int i = 0; i < variables.size(); ++i) { - QSimplexConstraint *c = new QSimplexConstraint; - c->variables.insert(variables[i], 1.0); - c->constant = variables[i]->minSize; - c->ratio = QSimplexConstraint::MoreOrEqual; - itemConstraints += c; - - c = new QSimplexConstraint; - c->variables.insert(variables[i], 1.0); - c->constant = variables[i]->maxSize; - c->ratio = QSimplexConstraint::LessOrEqual; - itemConstraints += c; - } - QSimplex simplex; - simplex.setConstraints(constraints + itemConstraints); + simplex.setConstraints(constraints); // Obtain the objective constraint QSimplexConstraint objective; @@ -1011,6 +1031,7 @@ QGraphicsAnchorLayoutPrivate::solveMinMax(QList constraint qreal min = simplex.solveMin(); // Save sizeAtMinimum results + QList variables = simplex.constraintsVariables(); for (int i = 0; i < variables.size(); ++i) { AnchorData *ad = static_cast(variables[i]); ad->sizeAtMinimum = ad->result; @@ -1025,32 +1046,12 @@ QGraphicsAnchorLayoutPrivate::solveMinMax(QList constraint ad->sizeAtMaximum = ad->result; } - qDeleteAll(itemConstraints); - return qMakePair(min, max); } void QGraphicsAnchorLayoutPrivate::solvePreferred(QList constraints) { QList variables = getVariables(constraints); - - // ### - QList itemConstraints; - - for (int i = 0; i < variables.size(); ++i) { - QSimplexConstraint *c = new QSimplexConstraint; - c->variables.insert(variables[i], 1.0); - c->constant = variables[i]->minSize; - c->ratio = QSimplexConstraint::MoreOrEqual; - itemConstraints += c; - - c = new QSimplexConstraint; - c->variables.insert(variables[i], 1.0); - c->constant = variables[i]->maxSize; - c->ratio = QSimplexConstraint::LessOrEqual; - itemConstraints += c; - } - QList preferredConstraints; QList preferredVariables; QSimplexConstraint objective; @@ -1094,13 +1095,12 @@ void QGraphicsAnchorLayoutPrivate::solvePreferred(QList co } - QSimplex simplex; - simplex.setConstraints(constraints + itemConstraints + preferredConstraints); - - simplex.setObjective(&objective); + QSimplex *simplex = new QSimplex; + simplex->setConstraints(constraints + preferredConstraints); + simplex->setObjective(&objective); // Calculate minimum values - simplex.solveMin(); + simplex->solveMin(); // Save sizeAtPreferred results for (int i = 0; i < variables.size(); ++i) { @@ -1108,8 +1108,11 @@ void QGraphicsAnchorLayoutPrivate::solvePreferred(QList co ad->sizeAtPreferred = ad->result; } - qDeleteAll(itemConstraints); + // Make sure we delete the simplex solver -before- we delete the + // constraints used by it. + delete simplex; + + // Delete constraints and variables we created. qDeleteAll(preferredConstraints); qDeleteAll(preferredVariables); - } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 6370f4a..f2dd796 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -264,6 +264,7 @@ public: void setAnchorSizeHintsFromItems(Orientation orientation); void findPaths(Orientation orientation); void constraintsFromPaths(Orientation orientation); + QList constraintsFromSizeHints(const QList &anchors); QList > getGraphParts(Orientation orientation); inline AnchorVertex *internalVertex(const QPair &itemEdge) diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp index 4ed11c4..774af8c 100644 --- a/src/gui/graphicsview/qsimplex_p.cpp +++ b/src/gui/graphicsview/qsimplex_p.cpp @@ -5,7 +5,7 @@ #include -QSimplex::QSimplex() : rows(0), columns(0), firstArtificial(0), matrix(0) +QSimplex::QSimplex() : objective(0), rows(0), columns(0), firstArtificial(0), matrix(0) { } -- cgit v0.12 From b58d43e407029f19d313cdc2314cf114c572f692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 3 Jun 2009 10:16:35 +0200 Subject: Add an example Should hopefully be easier to test layouts now... --- examples/layouts/anchorlayout/anchorlayout.pro | 15 + examples/layouts/anchorlayout/anchorlayout.ui | 433 +++++++++++++++++ examples/layouts/anchorlayout/layoutitem.cpp | 93 ++++ examples/layouts/anchorlayout/layoutitem.h | 62 +++ examples/layouts/anchorlayout/main.cpp | 10 + examples/layouts/anchorlayout/scene.cpp | 57 +++ examples/layouts/anchorlayout/scene.h | 64 +++ .../layouts/anchorlayout/widgetchooserdelegate.cpp | 134 ++++++ .../layouts/anchorlayout/widgetchooserdelegate.h | 75 +++ examples/layouts/anchorlayout/window.cpp | 517 +++++++++++++++++++++ examples/layouts/anchorlayout/window.h | 60 +++ examples/layouts/anchorlayout/xml/linear.xml | 46 ++ examples/layouts/anchorlayout/xml/snake.xml | 79 ++++ 13 files changed, 1645 insertions(+) create mode 100644 examples/layouts/anchorlayout/anchorlayout.pro create mode 100644 examples/layouts/anchorlayout/anchorlayout.ui create mode 100644 examples/layouts/anchorlayout/layoutitem.cpp create mode 100644 examples/layouts/anchorlayout/layoutitem.h create mode 100644 examples/layouts/anchorlayout/main.cpp create mode 100644 examples/layouts/anchorlayout/scene.cpp create mode 100644 examples/layouts/anchorlayout/scene.h create mode 100644 examples/layouts/anchorlayout/widgetchooserdelegate.cpp create mode 100644 examples/layouts/anchorlayout/widgetchooserdelegate.h create mode 100644 examples/layouts/anchorlayout/window.cpp create mode 100644 examples/layouts/anchorlayout/window.h create mode 100644 examples/layouts/anchorlayout/xml/linear.xml create mode 100644 examples/layouts/anchorlayout/xml/snake.xml diff --git a/examples/layouts/anchorlayout/anchorlayout.pro b/examples/layouts/anchorlayout/anchorlayout.pro new file mode 100644 index 0000000..8c19404 --- /dev/null +++ b/examples/layouts/anchorlayout/anchorlayout.pro @@ -0,0 +1,15 @@ +###################################################################### +# Automatically generated by qmake (2.01a) fr 15. mai 08:51:37 2009 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += layoutitem.h widgetchooserdelegate.h window.h scene.h +FORMS += anchorlayout.ui +SOURCES += layoutitem.cpp main.cpp widgetchooserdelegate.cpp window.cpp scene.cpp + +DEFINES += PRO_FILE_PWD=$$_PRO_FILE_PWD_ \ No newline at end of file diff --git a/examples/layouts/anchorlayout/anchorlayout.ui b/examples/layouts/anchorlayout/anchorlayout.ui new file mode 100644 index 0000000..f4c061a --- /dev/null +++ b/examples/layouts/anchorlayout/anchorlayout.ui @@ -0,0 +1,433 @@ + + + MainWindow + + + + 0 + 0 + 794 + 590 + + + + Anchor layout example + + + + + 0 + + + + + + + 0 + 128 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + + + + + 0 + 0 + 794 + 21 + + + + + &File + + + + + &View + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + 0 + 0 + + + + Qt::BottomDockWidgetArea|Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea + + + Anchors + + + 1 + + + + + 0 + + + + + + 0 + 0 + + + + + 330 + 0 + + + + 50 + + + + Begin + + + + + Edge + + + + + End + + + + + Edge + + + + + Min + + + + + Max + + + + + + + + Add anchor ^^ + + + Ctrl+A + + + false + + + + + + + + + + 0 + 0 + + + + Item properties + + + 1 + + + + + 0 + + + + + + 0 + 0 + + + + Item properties + + + + + + TextLabel + + + + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + + + + + + + TextLabel + + + + + + + + + + + + + TextLabel + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Layout presets + + + 1 + + + + + 0 + + + + + + + + + + Add item + + + Ctrl+N + + + + + true + + + true + + + &Anchors + + + + + true + + + true + + + Layout &presets + + + + + true + + + true + + + &Item properties + + + + + + + actionLayout_presets + triggered(bool) + dwPresets + setVisible(bool) + + + -1 + -1 + + + 173 + 345 + + + + + action_Anchors + triggered(bool) + dwAnchors + setVisible(bool) + + + -1 + -1 + + + 173 + 169 + + + + + action_Item_properties + triggered(bool) + dwProperties + setVisible(bool) + + + 173 + 483 + + + 173 + 483 + + + + + dwAnchors + visibilityChanged(bool) + action_Anchors + setChecked(bool) + + + 173 + 169 + + + -1 + -1 + + + + + dwPresets + visibilityChanged(bool) + actionLayout_presets + setChecked(bool) + + + 173 + 345 + + + -1 + -1 + + + + + dwProperties + visibilityChanged(bool) + action_Item_properties + setChecked(bool) + + + 173 + 483 + + + -1 + -1 + + + + + diff --git a/examples/layouts/anchorlayout/layoutitem.cpp b/examples/layouts/anchorlayout/layoutitem.cpp new file mode 100644 index 0000000..b85a589 --- /dev/null +++ b/examples/layouts/anchorlayout/layoutitem.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "layoutitem.h" +#include "scene.h" + +LayoutItem::LayoutItem(const QString &name, QGraphicsItem *parent/* = 0*/) + : QGraphicsWidget(parent) +{ + setData(0, name); + setGraphicsItem(this); + setFocusPolicy(Qt::ClickFocus); +} + +LayoutItem::~LayoutItem() +{ +} + +void LayoutItem::paint(QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget /*= 0*/) +{ + Q_UNUSED(widget); + Q_UNUSED(option); + + // text + QString name = graphicsItem()->data(0).toString(); + painter->drawText(rect(), Qt::AlignCenter, name); + + // rect + if (Scene *scn = static_cast(scene())) { + if (scn->lastFocusItem() == this) { + QPen pen(Qt::blue, 2.0); + painter->setPen(pen); + } + } + painter->drawRoundedRect(rect(), 5, 5); +} + +void LayoutItem::focusInEvent(QFocusEvent *event) +{ + if (Scene *scn = static_cast(scene())) { + scn->emitFocusItemChanged(this); + } + QGraphicsWidget::focusInEvent(event); +} + +void LayoutItem::focusOutEvent(QFocusEvent *event) +{ + if (Scene *scn = static_cast(scene())) { + scn->emitFocusItemChanged(0); + } + QGraphicsWidget::focusOutEvent(event); +} + + diff --git a/examples/layouts/anchorlayout/layoutitem.h b/examples/layouts/anchorlayout/layoutitem.h new file mode 100644 index 0000000..86c0b06 --- /dev/null +++ b/examples/layouts/anchorlayout/layoutitem.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LAYOUTITEM_H +#define LAYOUTITEM_H + +#include + +class LayoutItem : public QGraphicsWidget +{ + Q_OBJECT +public: + LayoutItem(const QString &name, QGraphicsItem *parent = 0); + ~LayoutItem(); + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget = 0); + void focusInEvent(QFocusEvent *event); + void focusOutEvent(QFocusEvent *event); + +}; + + +#endif diff --git a/examples/layouts/anchorlayout/main.cpp b/examples/layouts/anchorlayout/main.cpp new file mode 100644 index 0000000..d0e6ee7 --- /dev/null +++ b/examples/layouts/anchorlayout/main.cpp @@ -0,0 +1,10 @@ +#include "window.h" +#include + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + Window *w = new Window; + w->show(); + return app.exec(); +} \ No newline at end of file diff --git a/examples/layouts/anchorlayout/scene.cpp b/examples/layouts/anchorlayout/scene.cpp new file mode 100644 index 0000000..68b4002 --- /dev/null +++ b/examples/layouts/anchorlayout/scene.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scene.h" + +Scene::Scene() : QGraphicsScene() +{ + +} + +void Scene::emitFocusItemChanged(QGraphicsItem *item) +{ + if (hasFocus()) { // when leaving the view, we want to remember the last focused item + emit focusItemChanged(item); + m_lastFocusItem = item; + } +} + + diff --git a/examples/layouts/anchorlayout/scene.h b/examples/layouts/anchorlayout/scene.h new file mode 100644 index 0000000..ba7f5d8 --- /dev/null +++ b/examples/layouts/anchorlayout/scene.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SCENE_H +#define SCENE_H + +#include + +class Scene : public QGraphicsScene +{ + Q_OBJECT +public: + Scene(); + + void emitFocusItemChanged(QGraphicsItem *item); + QGraphicsItem *lastFocusItem() { return m_lastFocusItem; } + +Q_SIGNALS: + void focusItemChanged(QGraphicsItem *item); + +private: + QGraphicsItem *m_lastFocusItem; + +}; + +#endif // SCENE_H diff --git a/examples/layouts/anchorlayout/widgetchooserdelegate.cpp b/examples/layouts/anchorlayout/widgetchooserdelegate.cpp new file mode 100644 index 0000000..0f988c1 --- /dev/null +++ b/examples/layouts/anchorlayout/widgetchooserdelegate.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "widgetchooserdelegate.h" + + +WidgetChooserDelegate::WidgetChooserDelegate(QVector *layoutItems, + QGraphicsLayout *layout, + QWidget *parent /*= 0*/) +: QStyledItemDelegate(parent), m_layoutItems(layoutItems), m_layout(layout) +{ +} + +static const char *edges[] = { + "Left", + "HCenter", + "Right", + "Top", + "VCenter", + "Bottom" + }; + +QWidget *WidgetChooserDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const + +{ + int column = index.column(); + if (column == 0 || column == 2) { + // first and second anchor items + QComboBox *chooser = new QComboBox(parent); + chooser->addItem(QLatin1String("layout"), + qVariantFromValue(reinterpret_cast(m_layout))); + for (int i = 0; i < m_layoutItems->count(); ++i) { + QGraphicsLayoutItem *item = m_layoutItems->at(i); + chooser->addItem(item->graphicsItem()->data(0).toString(), + qVariantFromValue(reinterpret_cast(item))); + } + connect(chooser, SIGNAL(currentIndexChanged(int)), + this, SLOT(commitAndCloseEditor())); + return chooser; + } else if (column == 1 || column == 3) { + // first and second anchor edges + QComboBox *chooser = new QComboBox(parent); + for (int i = 0; i < sizeof(edges)/sizeof(char*); ++i) { + chooser->addItem(QLatin1String(edges[i]), i); + } + connect(chooser, SIGNAL(currentIndexChanged(int)), + this, SLOT(commitAndCloseEditor())); + return chooser; + } else { + return QStyledItemDelegate::createEditor(parent, option, index); + } +} + +void WidgetChooserDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + if (QComboBox *chooser = qobject_cast(editor)) { + int column = index.column(); + if (column >= 0 && column <= 3) { + QVariant data = index.data(Qt::UserRole); + int index = chooser->findData(data); + if (index >= 0) { + chooser->setCurrentIndex(index); + } + } + } else { + QStyledItemDelegate::setEditorData(editor, index); + } +} + +void WidgetChooserDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + if (QComboBox *chooser = qobject_cast(editor)) { + int column = index.column(); + if (column >= 0 && column <= 3) { + int currentIndex = chooser->currentIndex(); + model->setData(index, chooser->itemText(currentIndex)); + model->setData(index, chooser->itemData(currentIndex), Qt::UserRole); + } + } else { + QStyledItemDelegate::setModelData(editor, model, index); + } +} + +void WidgetChooserDelegate::commitAndCloseEditor() +{ + QComboBox *editor = qobject_cast(sender()); + emit commitData(editor); + emit closeEditor(editor); +} + diff --git a/examples/layouts/anchorlayout/widgetchooserdelegate.h b/examples/layouts/anchorlayout/widgetchooserdelegate.h new file mode 100644 index 0000000..50b9e48 --- /dev/null +++ b/examples/layouts/anchorlayout/widgetchooserdelegate.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef STARDELEGATE_H +#define STARDELEGATE_H + +#include + +#include + +class QGraphicsLayoutItem; +class QGraphicsLayout; + +class WidgetChooserDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + WidgetChooserDelegate(QVector *layoutItems, + QGraphicsLayout *layout, + QWidget *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + +private slots: + void commitAndCloseEditor(); + +private: + QVector *m_layoutItems; + QGraphicsLayout *m_layout; +}; + +#endif diff --git a/examples/layouts/anchorlayout/window.cpp b/examples/layouts/anchorlayout/window.cpp new file mode 100644 index 0000000..50a1bae --- /dev/null +++ b/examples/layouts/anchorlayout/window.cpp @@ -0,0 +1,517 @@ +#include "window.h" +#include "layoutitem.h" +#include "scene.h" +#include "widgetchooserdelegate.h" + +#include +#include + +static QGraphicsLayoutItem *layoutItem(const QVariant &data) +{ + return reinterpret_cast(qVariantValue(data)); +} + +static QString nodeName(QGraphicsLayoutItem *item) +{ + QString str = item->isLayout() ? QLatin1String("layout") : item->graphicsItem()->data(0).toString(); + str.replace(QLatin1Char(' '), QLatin1Char('_')); + return str; +} + +#define _QUOTEMACRO(x) #x +#define QUOTEMACRO(x) _QUOTEMACRO(x) + +Window::Window(QWidget *parent) + : QMainWindow(parent), m_inAddAnchor(false) +{ + m_ui.setupUi(this); + m_scene = new Scene; + + m_ui.graphicsView->setScene(m_scene); + connect(m_scene, SIGNAL(focusItemChanged(QGraphicsItem*)), this, SLOT(scene_focusItemChanged(QGraphicsItem*))); + + m_window = new QGraphicsWidget(0, Qt::Window); + m_scene->addItem(m_window); + m_layout = new QGraphicsAnchorLayout(m_window); + m_window->setLayout(m_layout); + m_window->resize(400, 300); + + findLayoutFiles(); + + WidgetChooserDelegate *delegate = new WidgetChooserDelegate(&m_layoutItems, m_layout, m_ui.anchors); + m_ui.anchors->setItemDelegate(delegate); + +} + +void Window::findLayoutFiles() +{ + m_ignoreCurrentLayoutChange = true; + QLatin1String s(QUOTEMACRO(PRO_FILE_PWD)"/xml"); + QDirIterator it(s); + while (it.hasNext()) { + it.next(); + QFileInfo fi = it.fileInfo(); + QString baseName = fi.baseName(); + if (!baseName.isEmpty()) { // avoid "." and ".." + m_ui.layouts->addItem(baseName); + } + } + m_ignoreCurrentLayoutChange = false; +} + +void Window::on_layouts_currentRowChanged(int row) +{ + if (m_ignoreCurrentLayoutChange) + return; + + QListWidgetItem *item = m_ui.layouts->item(row); + if (item) { + QString fileName = QString::fromAscii("%1/xml/%2.xml").arg(QUOTEMACRO(PRO_FILE_PWD), item->text()); + if (!loadLayout(fileName, m_layout)) { + qWarning("could not find %s", qPrintable(fileName)); + } + } +} + +void Window::on_anchors_cellChanged(int row, int column) +{ + Q_UNUSED(row); + Q_UNUSED(column); + if (!m_inAddAnchor) + rebuildLayout(); +} + + +void Window::on_pbAddAnchor_clicked(bool) +{ + addAnchorRow(); +} + +void Window::on_actionAdd_item_triggered(bool checked) +{ + addItem(); +} + +void Window::on_itemName_textEdited(const QString & ) +{ + updateItem(); +} + +void Window::on_itemMinW_valueChanged(double ) +{ + updateItem(); +} + +void Window::on_itemMinH_valueChanged(double ) +{ + updateItem(); +} + +void Window::on_itemPrefW_valueChanged(double ) +{ + updateItem(); +} + +void Window::on_itemPrefH_valueChanged(double ) +{ + updateItem(); +} + +void Window::on_itemMaxW_valueChanged(double ) +{ + updateItem(); +} + +void Window::on_itemMaxH_valueChanged(double ) +{ + updateItem(); +} + +void Window::scene_focusItemChanged(QGraphicsItem *item) +{ + bool isItemValid = item && item->isWidget(); + m_ui.groupBox->setEnabled(isItemValid); + QGraphicsLayoutItem *newCurrentItem = m_currentItem; + m_currentItem = 0; + if (isItemValid) { + QGraphicsWidget *w = static_cast(item); + setItemData(item->data(0).toString(), w->minimumSize(), w->preferredSize(), w->maximumSize()); + newCurrentItem = w; + } else { + setItemData(QString(), QSizeF(), QSizeF(), QSizeF()); + } + m_currentItem = newCurrentItem; +} + +void Window::updateItem() +{ + if (!m_currentItem) + return; + if (QGraphicsItem *gi = m_currentItem->graphicsItem()) { + gi->setData(0, m_ui.itemName->text()); + gi->update(); + } + QSizeF min(m_ui.itemMinW->value(), m_ui.itemMinH->value()); + QSizeF pref(m_ui.itemPrefW->value(), m_ui.itemPrefH->value()); + QSizeF max(m_ui.itemMaxW->value(), m_ui.itemMaxH->value()); + + if (min.isValid()) + m_currentItem->setMinimumSize(min); + if (pref.isValid()) + m_currentItem->setPreferredSize(pref); + if (max.isValid()) + m_currentItem->setMaximumSize(max); +} + +void Window::rebuildLayout() +{ + int i; + for (i = m_layout->count(); i > 0; --i) { + m_layout->removeAt(0); + } + + int rc = m_ui.anchors->rowCount(); + for (i = 0; i < rc; ++i) { + bool ok; + + QGraphicsLayoutItem *startItem = layoutItemAt(m_ui.anchors->model(), i, 0); + if (!startItem) + continue; + QGraphicsAnchorLayout::Edge startEdge = (QGraphicsAnchorLayout::Edge)(m_ui.anchors->item(i, 1)->data(Qt::UserRole).toInt(&ok)); + if (!ok) + continue; + QGraphicsLayoutItem *endItem = layoutItemAt(m_ui.anchors->model(), i, 2); + if (!endItem) + continue; + QGraphicsAnchorLayout::Edge endEdge = (QGraphicsAnchorLayout::Edge)(m_ui.anchors->item(i, 3)->data(Qt::UserRole).toInt(&ok)); + if (!ok) + continue; + + QString strStart = nodeName(startItem); + QString strEnd = nodeName(endItem); + m_layout->anchor(startItem, startEdge, endItem, endEdge); + } +} + +void Window::setItemData(const QString &name, const QSizeF &min, const QSizeF &pref, const QSizeF &max) +{ + m_ui.itemName->setText(name); + m_ui.itemMinW->setValue(min.width()); + m_ui.itemMinH->setValue(min.height()); + m_ui.itemPrefW->setValue(pref.width()); + m_ui.itemPrefH->setValue(pref.height()); + m_ui.itemMaxW->setValue(max.width()); + m_ui.itemMaxH->setValue(max.height()); +} + +QGraphicsLayoutItem *Window::addItem(const QString &name) +{ + int rc = m_layoutItems.count(); + QString effectiveName = name.isEmpty() ? QString::fromAscii("item %1").arg(rc + 1) : name; + QGraphicsLayoutItem *layoutItem = new LayoutItem(effectiveName, m_window); + m_layoutItems.append(layoutItem); + m_scene->setFocusItem(layoutItem->graphicsItem()); + return layoutItem; +} + +static const char *strEdges[] = {"Left", + "HCenter", + "Right", + "Top", + "VCenter", + "Bottom"}; + +void Window::setAnchorData(QGraphicsLayoutItem *startItem, const QString &startName, QGraphicsAnchorLayout::Edge startEdge, + QGraphicsLayoutItem *endItem, const QString &endName, QGraphicsAnchorLayout::Edge endEdge, int row /*= -1*/) +{ + if (row == -1) { + row = m_ui.anchors->rowCount(); + m_ui.anchors->insertRow(row); + } + m_inAddAnchor = true; + + QTableWidgetItem *item = new QTableWidgetItem; + item->setData(Qt::UserRole, qVariantFromValue(reinterpret_cast(startItem))); + item->setData(Qt::DisplayRole, startName); + m_ui.anchors->setItem(row, 0, item); + + item = new QTableWidgetItem; + item->setData(Qt::UserRole, int(startEdge)); + item->setData(Qt::DisplayRole, QLatin1String(strEdges[startEdge])); + m_ui.anchors->setItem(row, 1, item); + + item = new QTableWidgetItem; + item->setData(Qt::UserRole, qVariantFromValue(reinterpret_cast(endItem))); + item->setData(Qt::DisplayRole, endName); + m_ui.anchors->setItem(row, 2, item); + + item = new QTableWidgetItem; + item->setData(Qt::UserRole, int(endEdge)); + item->setData(Qt::DisplayRole, QLatin1String(strEdges[endEdge])); + m_ui.anchors->setItem(row, 3, item); + + item = new QTableWidgetItem; + item->setData(Qt::DisplayRole, 6); + m_ui.anchors->setItem(row, 4, item); + + item = new QTableWidgetItem; + item->setData(Qt::DisplayRole, 6); + m_ui.anchors->setItem(row, 5, item); + m_inAddAnchor = false; + +} + +void Window::addAnchorRow() +{ + int rc = m_ui.anchors->rowCount(); + QGraphicsLayoutItem *defaultLayoutItem = m_layout; + if (m_layoutItems.count() > 0) { + defaultLayoutItem = m_layoutItems.at(0); + } + + m_ui.anchors->insertRow(rc); + + if (defaultLayoutItem) { + QString defaultName = defaultLayoutItem->isLayout() ? + QLatin1String("layout") : + defaultLayoutItem->graphicsItem()->data(0).toString(); + setAnchorData(defaultLayoutItem, defaultName, QGraphicsAnchorLayout::Right, defaultLayoutItem, defaultName, QGraphicsAnchorLayout::Left, rc); + rebuildLayout(); + } +} + +QGraphicsLayoutItem *Window::layoutItemAt(QAbstractItemModel *model, int row, int column /*= 0*/) +{ + int rc = model->rowCount(); + QGraphicsLayoutItem *item = 0; + if (row < rc) { + QVariant data = model->data(model->index(row, column), Qt::UserRole); + if (data.isValid()) { + item = layoutItem(data); + } + } + return item; +} + +bool Window::saveLayout(const QString& fileName) +{ + bool ok; +#if 0 + QFile out(fileName); + ok = out.open(QIODevice::WriteOnly); + + if (ok) { + int i; + out.write("graph anchorlayout {\nnode [shape=\"rect\"]\n\n"); + for (i = 0; i < m_ui.items->rowCount(); ++i) { + // name, min, pref, max + QTableWidgetItem *item = m_ui.items->item(i, 0); + QGraphicsLayoutItem *li = layoutItem(item->data(Qt::UserRole)); + if (li == m_layout) + continue; + out.write(qPrintable(QString::fromAscii("%1 [label=\"%1\"]\n").arg(nodeName(li)))); + } + out.write("\n\n"); + static const char *compass = "wnes"; + for (i = 0; i < m_ui.anchors->rowCount(); ++i) { + // name, min, pref, max + bool ok; + QGraphicsLayoutItem *startItem = layoutItem(m_ui.anchors->item(i, 0)->data(Qt::UserRole)); + + QGraphicsAnchorLayout::Edge startEdge = (QGraphicsAnchorLayout::Edge)(m_ui.anchors->item(i, 1)->data(Qt::UserRole).toInt(&ok)); + if (!ok) + continue; + + QGraphicsLayoutItem *endItem = layoutItem(m_ui.anchors->item(i, 2)->data(Qt::UserRole)); + + QGraphicsAnchorLayout::Edge endEdge = (QGraphicsAnchorLayout::Edge)(m_ui.anchors->item(i, 3)->data(Qt::UserRole).toInt(&ok)); + if (!ok) + continue; + + out.write(qPrintable(QString::fromAscii("%1:%2--%3:%4 [label=\"[%5,%6]\"]\n") + .arg(nodeName(startItem)) + .arg(QLatin1Char(compass[int(startEdge)])) + .arg(nodeName(endItem)) + .arg(QLatin1Char(compass[int(endEdge)])) + .arg(1) + .arg(1) + .arg(2))); + } + out.write("}"); + out.close(); + } +#endif + return ok; +} + +static bool parseProperty(QXmlStreamReader *xml, QString *name, QSizeF *size) +{ + QString propName = xml->attributes().value("name").toString(); + QSizeF sz; + while (!xml->atEnd()) { + xml->readNext(); + if (xml->isStartElement() && xml->name() == QLatin1String("size")) { + QXmlStreamAttributes attrs = xml->attributes(); + QString sw = attrs.value("width").toString(); + QString sh = attrs.value("height").toString(); + bool ok; + float w = sw.toFloat(&ok); + if (ok) { + sz.setWidth(w); + } + + float h = sw.toFloat(&ok); + if (ok) { + sz.setHeight(h); + } + + } + if (xml->isEndElement() && xml->name() == QLatin1String("property")) { + if (name && size && sz.isValid()) { + *name = propName; + *size = sz; + return true; + } + } + } + return false; +} + +static bool parseEdge(const QString &itemEdge, QByteArray *id, QGraphicsAnchorLayout::Edge *edge) +{ + QStringList item_edge = itemEdge.split(QLatin1Char('.')); + bool ok = item_edge.count() == 2; + if (ok) { + QByteArray strEdge = item_edge.at(1).toAscii().toLower(); + if (strEdge == "left") { + *edge = QGraphicsAnchorLayout::Left; + } else if (strEdge == "hcenter") { + *edge = QGraphicsAnchorLayout::HCenter; + } else if (strEdge == "right") { + *edge = QGraphicsAnchorLayout::Right; + } else if (strEdge == "top") { + *edge = QGraphicsAnchorLayout::Top; + } else if (strEdge == "vcenter") { + *edge = QGraphicsAnchorLayout::VCenter; + } else if (strEdge == "bottom") { + *edge = QGraphicsAnchorLayout::Bottom; + } else { + ok = false; + } + if (ok) + *id = item_edge.at(0).toAscii(); + } + return ok; +} + +bool Window::loadLayout(const QString& fileName, QGraphicsAnchorLayout *layout) +{ + QFile input(fileName); + bool ok = input.open(QIODevice::ReadOnly | QIODevice::Text); + if (ok) { + int i; + for (i = 0; i < m_layoutItems.count(); ++i) { + QGraphicsLayoutItem *item = m_layoutItems.at(i); + delete item; + } + + m_layoutItems.clear(); + m_ui.anchors->setRowCount(0); + + QLatin1String str_anchorlayout("anchorlayout"); + QLatin1String str_item("item"); + QLatin1String str_property("property"); + QXmlStreamReader xml(&input); + + int level = 0; + QHash hash; + QString item_id; + QSizeF min, pref, max; + + while (!xml.atEnd()) { + QXmlStreamReader::TokenType token = xml.readNext(); + switch (token) { + case QXmlStreamReader::StartDocument: + break; + case QXmlStreamReader::StartElement: + if (level == 0 && xml.name() == str_anchorlayout) { + + } else if (level == 1 && xml.name() == str_item) { + item_id = xml.attributes().value("id").toString(); + } else if (level == 1 && xml.name() == QLatin1String("anchor")) { + QXmlStreamAttributes attrs = xml.attributes(); + QString first = attrs.value("first").toString(); + QString second = attrs.value("second").toString(); + QByteArray startID; + QGraphicsAnchorLayout::Edge startEdge; + QGraphicsLayoutItem *startItem = 0; + if (parseEdge(first, &startID, &startEdge)) { + if (startID == "this") { + startID = "layout"; + startItem = layout; + } else { + startItem = hash.value(startID); + } + } else { + qWarning("%s is not a valid edge description", qPrintable(first)); + break; + } + + QByteArray endID; + QGraphicsAnchorLayout::Edge endEdge; + QGraphicsLayoutItem *endItem = 0; + if (parseEdge(second, &endID, &endEdge)) { + if (endID == "this") { + endID = "layout"; + endItem = layout; + } else { + endItem = hash.value(endID); + } + } else { + qWarning("%s is not a valid edge description", qPrintable(second)); + break; + } + setAnchorData( startItem, startID, startEdge, + endItem, endID, endEdge, -1); + } else if (level == 2 && xml.name() == str_property) { + QString name; + QSizeF size; + if (parseProperty(&xml, &name, &size)) { + if (name == QLatin1String("minimumSize")) { + min = size; + } else if (name == QLatin1String("preferredSize")) { + pref = size; + } else if (name == QLatin1String("maximumSize")) { + max = size; + } + break; + } + } else { + --level; + } + ++level; + break; + case QXmlStreamReader::EndElement: + if (xml.name() == str_anchorlayout) { + } + if (xml.name() == str_item) { + QGraphicsLayoutItem *item = addItem(item_id); + item->setMinimumSize(min); + item->setPreferredSize(pref); + item->setMaximumSize(max); + hash.insert(item_id.toAscii(), item); + } + --level; + break; + } + } + if (xml.hasError()) { + // do error handling + qWarning("%s", qPrintable(xml.errorString())); + } + input.close(); + } + if (ok) + rebuildLayout(); + return ok; +} diff --git a/examples/layouts/anchorlayout/window.h b/examples/layouts/anchorlayout/window.h new file mode 100644 index 0000000..34be649 --- /dev/null +++ b/examples/layouts/anchorlayout/window.h @@ -0,0 +1,60 @@ +#ifndef WINDOW_H +#define WINDOW_H + +#include "ui_anchorlayout.h" +#include + +class QGraphicsWidget; +class Scene; + +class Window : public QMainWindow +{ + Q_OBJECT +public: + Window(QWidget *parent = 0); + +private slots: + void on_anchors_cellChanged(int row, int column); + void on_pbAddAnchor_clicked(bool); + void on_actionAdd_item_triggered(bool checked); + void on_layouts_currentRowChanged(int row); + void on_itemName_textEdited(const QString &text); + void on_itemMinW_valueChanged(double d); + void on_itemMinH_valueChanged(double d); + void on_itemPrefW_valueChanged(double d); + void on_itemPrefH_valueChanged(double d); + void on_itemMaxW_valueChanged(double d); + void on_itemMaxH_valueChanged(double d); + + + + // not in ui file + void scene_focusItemChanged(QGraphicsItem *item); +private: + void updateItem(); + void setItemData(const QString &name, const QSizeF &min, const QSizeF &pref, const QSizeF &max); + QGraphicsLayoutItem *addItem(const QString &name = QString()); + void addAnchorRow(); + void setAnchorData(QGraphicsLayoutItem *startItem, const QString &startName, QGraphicsAnchorLayout::Edge startEdge, + QGraphicsLayoutItem *endItem, const QString &endName, QGraphicsAnchorLayout::Edge endEdge, int row = -1); + + bool saveLayout(const QString& fileName); + bool loadLayout(const QString& fileName, QGraphicsAnchorLayout *layout); + void findLayoutFiles(); + + void rebuildLayout(); + QGraphicsLayoutItem *layoutItemAt(QAbstractItemModel *mode, int row, int column = 0); + + Ui::MainWindow m_ui; + Scene *m_scene; + bool m_changingCell; + QGraphicsWidget *m_window; + QGraphicsAnchorLayout *m_layout; + QVector m_layoutItems; + QGraphicsLayoutItem *m_currentItem; + bool m_inAddAnchor; + bool m_ignoreCurrentLayoutChange; +}; + +#endif // WINDOW_H + diff --git a/examples/layouts/anchorlayout/xml/linear.xml b/examples/layouts/anchorlayout/xml/linear.xml new file mode 100644 index 0000000..723b72b --- /dev/null +++ b/examples/layouts/anchorlayout/xml/linear.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/layouts/anchorlayout/xml/snake.xml b/examples/layouts/anchorlayout/xml/snake.xml new file mode 100644 index 0000000..22b1a6d --- /dev/null +++ b/examples/layouts/anchorlayout/xml/snake.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file -- cgit v0.12 From 072a9595a308df9b8e7a547c370ce54ab5af6ed4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 3 Jun 2009 12:58:07 +0200 Subject: Some cleanup - improve dot dumper. The previous dumper relied on traversal of the graph, which meant that it would not output the nodes that were not connected to the "main" graph. Due to this, we cannot rely on traversal, so instead we must iterate through all the vertices of the graph. As an added bonus the code gets simpler :-) There was also a problem with the previous dumper, since it dumped two "digraph" elements into one file. Graphwiz (win32) did not handle that. Instead just dump all the nodes and all the connections, both horizontal and vertical ones. The horizontal and vertical connections are never connected anyway, so the result will be two separate graphs when it is rendered by graphwiz. Also renamed firstVertex to rootVertex and simplified it. --- src/gui/graphicsview/qgraph_p.h | 110 ++++++++++------------- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 6 +- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 6 +- 3 files changed, 52 insertions(+), 70 deletions(-) diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h index e487eca..6f4d7bd 100644 --- a/src/gui/graphicsview/qgraph_p.h +++ b/src/gui/graphicsview/qgraph_p.h @@ -11,18 +11,18 @@ class Graph public: Graph() {} - class iterator { + class const_iterator { public: - iterator(Graph *graph, bool begin) : g(graph){ + const_iterator(const Graph *graph, bool begin) : g(graph){ if (begin) { - row = g->m_graph.begin(); - //test if the graph is empty - if (row != g->m_graph.end()) - { - column = (*row)->begin(); - } + row = g->m_graph.constBegin(); + //test if the graph is empty + if (row != g->m_graph.constEnd()) + { + column = (*row)->constBegin(); + } } else { - row = g->m_graph.end(); + row = g->m_graph.constEnd(); } } @@ -30,24 +30,24 @@ public: return column.key(); } - inline bool operator==(const iterator &o) const { return !(*this != o); } - inline bool operator!=(const iterator &o) const { + inline bool operator==(const const_iterator &o) const { return !(*this != o); } + inline bool operator!=(const const_iterator &o) const { if (row == g->m_graph.end()) { - return row != o.row;} - else { + return row != o.row; + } else { return row != o.row || column != o.column; } } - inline iterator& operator=(const iterator &o) const { row = o.row; column = o.column; return *this;} + inline const_iterator& operator=(const const_iterator &o) const { row = o.row; column = o.column; return *this;} // prefix - iterator &operator++() { - if (row != g->m_graph.end()) { + const_iterator &operator++() { + if (row != g->m_graph.constEnd()) { ++column; - if (column == (*row)->end()) { + if (column == (*row)->constEnd()) { ++row; - if (row != g->m_graph.end()) { - column = (*row)->begin(); + if (row != g->m_graph.constEnd()) { + column = (*row)->constBegin(); } } } @@ -55,17 +55,17 @@ public: } private: - Graph *g; - Q_TYPENAME QHash * >::iterator row; - Q_TYPENAME QHash::iterator column; + const Graph *g; + Q_TYPENAME QHash * >::const_iterator row; + Q_TYPENAME QHash::const_iterator column; }; - iterator begin() { - return iterator(this,true); + const_iterator constBegin() const { + return const_iterator(this,true); } - iterator end() { - return iterator(this,false); + const_iterator constEnd() const { + return const_iterator(this,false); } EdgeData *edgeData(Vertex* first, Vertex* second) { @@ -112,51 +112,35 @@ public: userVertex = vertex; } + QSet vertices() const { + QSet setOfVertices; + for (const_iterator it = constBegin(); it != constEnd(); ++it) { + setOfVertices.insert(*it); + } + return setOfVertices; + } + QString serializeToDot() { // traversal - QString vertices; + QString strVertices; QString edges; - QQueue queue; - QSet visited; - bool ok; - Vertex *v = firstVertex(&ok); - if (ok) { - queue.enqueue(v); - } - while (!queue.isEmpty()) { - Vertex *v = queue.dequeue(); - vertices += QString::fromAscii("%1 [label=\"%2\"]\n").arg(v->toString()).arg(v->toString()); - visited.insert(v); - // visit it here - QList vertices = adjacentVertices(v); - for (int i = 0; i < vertices.count(); ++i) { - Vertex *v1 = vertices.at(i); + + QSet setOfVertices = vertices(); + for (Q_TYPENAME QSet::const_iterator it = setOfVertices.begin(); it != setOfVertices.end(); ++it) { + Vertex *v = *it; + QList adjacents = adjacentVertices(v); + for (int i = 0; i < adjacents.count(); ++i) { + Vertex *v1 = adjacents.at(i); EdgeData *data = edgeData(v, v1); - edges+=QString::fromAscii("%1->%2 [label=\"[%3,%4]\"]\n").arg(v->toString()).arg(v1->toString()).arg(data->minSize).arg(data->maxSize); - if (!queue.contains(v1) && !visited.contains(v1)) { - queue.enqueue(v1); - } else { - // a cycle.... - } + edges += QString::fromAscii("%1->%2 [label=\"[%3,%4]\"]\n").arg(v->toString()).arg(v1->toString()).arg(data->minSize).arg(data->maxSize); } + strVertices += QString::fromAscii("%1 [label=\"%2\"]\n").arg(v->toString()).arg(v->toString()); } - return QString::fromAscii("digraph anchorlayout {\nnode [shape=\"rect\"]\n%1%2}").arg(vertices).arg(edges); + return QString::fromAscii("%1\n%2\n").arg(strVertices).arg(edges); } - Vertex *firstVertex(bool *ok) + Vertex *rootVertex() const { - if (userVertex) { - *ok = true; - return userVertex; - } - - Vertex *v = 0; - *ok = false; - Q_TYPENAME Graph::iterator it = Graph::begin(); - if (it != Graph::end()) { - v = *it; - *ok = true; - } - return v; + return userVertex; } protected: diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index ba3ba2a..1a72ace 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -254,10 +254,10 @@ void QGraphicsAnchorLayout::dumpGraph() if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) qWarning("Could not write to %s", file.fileName().toLocal8Bit().constData()); + QString str = QString::fromAscii("digraph anchorlayout {\nnode [shape=\"rect\"]\n%1}"); QString dotContents = d->graph[0].serializeToDot(); - file.write(dotContents.toLocal8Bit()); - dotContents = d->graph[1].serializeToDot(); - file.write(dotContents.toLocal8Bit()); + dotContents += d->graph[1].serializeToDot(); + file.write(str.arg(dotContents).toLocal8Bit()); file.close(); } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 644ed6b..add2db3 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -681,8 +681,7 @@ void QGraphicsAnchorLayoutPrivate::findPaths(Orientation orientation) QSet visited; - bool ok; - AnchorVertex *root = graph[orientation].firstVertex(&ok); + AnchorVertex *root = graph[orientation].rootVertex(); graphPaths[orientation].insert(root, GraphPath()); @@ -889,8 +888,7 @@ void QGraphicsAnchorLayoutPrivate::calculateVertexPositions( QSet visited; // Get root vertex - bool ok; - AnchorVertex *root = graph[orientation].firstVertex(&ok); + AnchorVertex *root = graph[orientation].rootVertex(); qreal widgetMargin; qreal layoutMargin; -- cgit v0.12 From 9d725b4bd16921e6e7967beb3c29681c0c25fc1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 3 Jun 2009 14:18:55 +0200 Subject: Added the possibility to save a layout. Keep it simple... --- examples/layouts/anchorlayout/anchorlayout.ui | 24 ++++++- examples/layouts/anchorlayout/window.cpp | 94 ++++++++++++++++++--------- examples/layouts/anchorlayout/window.h | 1 + 3 files changed, 85 insertions(+), 34 deletions(-) diff --git a/examples/layouts/anchorlayout/anchorlayout.ui b/examples/layouts/anchorlayout/anchorlayout.ui index f4c061a..5f3718b 100644 --- a/examples/layouts/anchorlayout/anchorlayout.ui +++ b/examples/layouts/anchorlayout/anchorlayout.ui @@ -6,8 +6,8 @@ 0 0 - 794 - 590 + 795 + 765 @@ -47,7 +47,7 @@ 0 0 - 794 + 795 21 @@ -55,6 +55,8 @@ &File + + @@ -330,6 +332,22 @@ &Item properties + + + Save layout + + + Ctrl+S + + + + + &Quit + + + Ctrl+Q + + diff --git a/examples/layouts/anchorlayout/window.cpp b/examples/layouts/anchorlayout/window.cpp index 50a1bae..8658d06 100644 --- a/examples/layouts/anchorlayout/window.cpp +++ b/examples/layouts/anchorlayout/window.cpp @@ -87,11 +87,18 @@ void Window::on_pbAddAnchor_clicked(bool) addAnchorRow(); } -void Window::on_actionAdd_item_triggered(bool checked) +void Window::on_actionAdd_item_triggered(bool ) { addItem(); } +void Window::on_actionSave_layout_triggered(bool ) +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save layout"), QLatin1String(QUOTEMACRO(PRO_FILE_PWD)"/xml"), QLatin1String("*.xml")); + if (!fileName.isEmpty()) + saveLayout(fileName); +} + void Window::on_itemName_textEdited(const QString & ) { updateItem(); @@ -187,8 +194,6 @@ void Window::rebuildLayout() if (!ok) continue; - QString strStart = nodeName(startItem); - QString strEnd = nodeName(endItem); m_layout->anchor(startItem, startEdge, endItem, endEdge); } } @@ -296,51 +301,78 @@ QGraphicsLayoutItem *Window::layoutItemAt(QAbstractItemModel *model, int row, in bool Window::saveLayout(const QString& fileName) { bool ok; -#if 0 QFile out(fileName); ok = out.open(QIODevice::WriteOnly); - if (ok) { + QXmlStreamWriter xml(&out); + xml.writeStartDocument(); + xml.writeStartElement(QLatin1String("anchorlayout")); int i; - out.write("graph anchorlayout {\nnode [shape=\"rect\"]\n\n"); - for (i = 0; i < m_ui.items->rowCount(); ++i) { - // name, min, pref, max - QTableWidgetItem *item = m_ui.items->item(i, 0); - QGraphicsLayoutItem *li = layoutItem(item->data(Qt::UserRole)); - if (li == m_layout) - continue; - out.write(qPrintable(QString::fromAscii("%1 [label=\"%1\"]\n").arg(nodeName(li)))); + for (i = 0; i < m_layoutItems.count(); ++i) { + QGraphicsLayoutItem *item = m_layoutItems.at(i); + xml.writeStartElement(QLatin1String("item")); + QString name = nodeName(item); + if (name == QLatin1String("layout")) + name = QLatin1String("this"); + xml.writeAttribute(QLatin1String("id"), name); + for (int p = 0; p < 3; ++p) { + const char *propertyNames[] = {"minimumSize", "preferredSize", "maximumSize"}; + int b; + typedef QSizeF (QGraphicsLayoutItem::*QGLISizeGetter)(void) const; + QGLISizeGetter sizeGetters[] = { &QGraphicsLayoutItem::minimumSize, + &QGraphicsLayoutItem::preferredSize, + &QGraphicsLayoutItem::maximumSize}; + QSizeF size = ((*item).*(sizeGetters[p])) (); + xml.writeStartElement(QLatin1String("property")); + xml.writeAttribute(QLatin1String("name"), QLatin1String(propertyNames[p])); + xml.writeStartElement(QLatin1String("size")); + xml.writeAttribute(QLatin1String("width"), QString::number(size.width())); + xml.writeAttribute(QLatin1String("height"), QString::number(size.height())); + xml.writeEndElement(); + xml.writeEndElement(); + } + xml.writeEndElement(); + } + + QHash edgeString; + for (i = 0; i < sizeof(strEdges)/sizeof(char*); ++i) { + edgeString.insert(i, QLatin1String(strEdges[i])); } - out.write("\n\n"); - static const char *compass = "wnes"; - for (i = 0; i < m_ui.anchors->rowCount(); ++i) { - // name, min, pref, max + int rc = m_ui.anchors->rowCount(); + for (i = 0; i < rc; ++i) { + xml.writeStartElement(QLatin1String("anchor")); bool ok; - QGraphicsLayoutItem *startItem = layoutItem(m_ui.anchors->item(i, 0)->data(Qt::UserRole)); + QGraphicsLayoutItem *startItem = layoutItemAt(m_ui.anchors->model(), i, 0); + if (!startItem) + continue; QGraphicsAnchorLayout::Edge startEdge = (QGraphicsAnchorLayout::Edge)(m_ui.anchors->item(i, 1)->data(Qt::UserRole).toInt(&ok)); if (!ok) continue; - - QGraphicsLayoutItem *endItem = layoutItem(m_ui.anchors->item(i, 2)->data(Qt::UserRole)); - + QGraphicsLayoutItem *endItem = layoutItemAt(m_ui.anchors->model(), i, 2); + if (!endItem) + continue; QGraphicsAnchorLayout::Edge endEdge = (QGraphicsAnchorLayout::Edge)(m_ui.anchors->item(i, 3)->data(Qt::UserRole).toInt(&ok)); if (!ok) continue; + + QString strStart = nodeName(startItem); + if (strStart == QLatin1String("layout")) + strStart = QLatin1String("this"); + xml.writeAttribute(QLatin1String("first"), QString::fromAscii("%1.%2").arg(strStart, edgeString.value(startEdge))); - out.write(qPrintable(QString::fromAscii("%1:%2--%3:%4 [label=\"[%5,%6]\"]\n") - .arg(nodeName(startItem)) - .arg(QLatin1Char(compass[int(startEdge)])) - .arg(nodeName(endItem)) - .arg(QLatin1Char(compass[int(endEdge)])) - .arg(1) - .arg(1) - .arg(2))); + QString strEnd = nodeName(endItem); + if (strEnd == QLatin1String("layout")) + strEnd = QLatin1String("this"); + xml.writeAttribute(QLatin1String("second"), QString::fromAscii("%1.%2").arg(strEnd, edgeString.value(endEdge))); + + xml.writeEndElement(); } - out.write("}"); + + xml.writeEndElement(); + xml.writeEndDocument(); out.close(); } -#endif return ok; } diff --git a/examples/layouts/anchorlayout/window.h b/examples/layouts/anchorlayout/window.h index 34be649..911cc84 100644 --- a/examples/layouts/anchorlayout/window.h +++ b/examples/layouts/anchorlayout/window.h @@ -17,6 +17,7 @@ private slots: void on_anchors_cellChanged(int row, int column); void on_pbAddAnchor_clicked(bool); void on_actionAdd_item_triggered(bool checked); + void on_actionSave_layout_triggered(bool checked); void on_layouts_currentRowChanged(int row); void on_itemName_textEdited(const QString &text); void on_itemMinW_valueChanged(double d); -- cgit v0.12 From f5dc6a07fb9eb99211eb1fa3056a51b933ac3430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Tue, 9 Jun 2009 13:49:06 +0200 Subject: Simplify the graph by replacing a sequence of anchors with one anchor. We insert a special SequentialAnchorData. This anchor's min/pref/maxSize will be the sum of the sizes of all the anchors it is replacing. This should make the equation solver faster, and will enable us to do distribution properly just like a linear layout would do. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 135 +++++++++++++++++++++++ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 39 ++++++- 2 files changed, 172 insertions(+), 2 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index add2db3..62d2d9a 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -41,6 +41,7 @@ #include #include +#include #include "qgraphicsanchorlayout_p.h" @@ -111,6 +112,135 @@ QGraphicsAnchorLayout::Edge QGraphicsAnchorLayoutPrivate::oppositeEdge( return edge; } + +/*! + * \internal + * + * helper function in order to avoid overflowing anchor sizes + * the returned size will never be larger than FLT_MAX + * + */ +inline static qreal checkAdd(qreal a, qreal b) +{ + if (FLT_MAX - b < a) + return FLT_MAX; + return a + b; +} +/*! + * \internal + * + * The purpose of this function is to simplify the graph. The process of simplification can be + * broken down to two methods: + * Algorithm can be described as: + * + * 1. Simplify all sequence of anchors into one anchor. + * If not first iteration and no further simplification was done, go to (3) + * 2. Simplify two parallel anchors into one anchor. + * If any simplification was done, go to (1) + * 3. Done + * + * Notes: + * * The algorithm should not make a sequence of the layout edge anchors. + * => Make sure those edges are not traversed + * * A generic algorithm will make a sequential simplification node of a Left-HCenter-Right + * sequence. This is ok, but that sequence should not be affected by stretch factors. + * + */ +void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + Graph &g = graph[orientation]; + AnchorVertex *v = g.rootVertex(); + QGraphicsAnchorLayout::Edge layoutEdge = oppositeEdge(v->m_edge); + + if (!v) + return; + QSet visited; + QStack stack; + stack.push(v); + QVector candidates; + + // walk depth-first. + while (!stack.isEmpty()) { + v = stack.pop(); + QList vertices = g.adjacentVertices(v); + const int count = vertices.count(); + if (count == 2 && v->m_item != q) { + candidates.append(v); + } + if ((v->m_item == q && v->m_edge == layoutEdge) || (count != 2 && candidates.count() >= 1)) { + SequentialAnchorData *sequence = new SequentialAnchorData; + AnchorVertex * &sequenceLast = v; //alias + AnchorVertex *sequenceFirst = 0; + QList adjacentOfSecondVertex = g.adjacentVertices(candidates.first()); + Q_ASSERT(adjacentOfSecondVertex.count() == 2); + if (adjacentOfSecondVertex.first() == candidates.at(1)) + sequenceFirst = adjacentOfSecondVertex.last(); + else + sequenceFirst = adjacentOfSecondVertex.first(); + + // The complete path of the sequence to simplify is: sequenceFirst, , sequenceLast + qreal min = 0; + qreal pref = 0; + qreal max = 0; + +/* // ### DEBUG + QString strCandidates; + for (int i = 0; i < candidates.count(); ++i) + strCandidates += QString::fromAscii("%1--").arg(candidates.at(i)->toString()); + QString strPath = QString::fromAscii("%1--%2%3").arg(sequenceFirst->toString(), strCandidates, sequenceLast->toString()); + qDebug("simplifying [%s] to [%s--%s]", qPrintable(strPath), qPrintable(sequenceFirst->toString()), qPrintable(sequenceLast->toString())); +*/ + + AnchorData *data = g.edgeData(sequenceFirst, candidates.first()); + min += data->minSize; + pref += data->prefSize; + max = checkAdd(max, data->maxSize); + g.removeEdge(sequenceFirst, candidates.first()); + + for (int i = 0; i < candidates.count() - 1; ++i) { + AnchorVertex *v1 = candidates.at(i); + AnchorVertex *v2 = candidates.at(i + 1); + data = g.edgeData(v1, v2); + min += data->minSize; + pref += data->prefSize; + max = checkAdd(max, data->maxSize); + g.removeEdge(v1, v2); + } + + data = g.edgeData(candidates.last(), sequenceLast); + min += data->minSize; + pref += data->prefSize; + max = checkAdd(max, data->maxSize); + g.removeEdge(candidates.last(), sequenceLast); + + // insert new + sequence->minSize = min; + sequence->prefSize = pref; + sequence->maxSize = max; + sequence->m_children = candidates; + sequence->origin = sequenceFirst; + g.createEdge(sequenceFirst, sequenceLast, sequence); + + // start all over again + candidates.clear(); + } + if (count != 2) + candidates.clear(); + + QGraphicsAnchorLayout::Edge centerEdge = pickEdge(QGraphicsAnchorLayout::HCenter, orientation); + for (int i = 0; i < count; ++i) { + AnchorVertex *next = vertices.at(i); + if (next->m_item == q && next->m_edge == centerEdge) + continue; + if (visited.contains(next)) + continue; + stack.push(next); + } + visited.insert(v); + } +} + QGraphicsAnchorLayoutPrivate::Orientation QGraphicsAnchorLayoutPrivate::edgeOrientation(QGraphicsAnchorLayout::Edge edge) { @@ -482,6 +612,11 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // Reset the nominal sizes of each anchor based on the current item sizes setAnchorSizeHintsFromItems(orientation); +// ### currently crashes +// q->dumpGraph(); +// simplifyGraph(orientation); +// q->dumpGraph(); + // Traverse all graph edges and store the possible paths to each vertex findPaths(orientation); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index f2dd796..71c00a2 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -124,16 +124,21 @@ inline QString AnchorVertex::toString() const Represents an edge (anchor) in the internal graph. */ struct AnchorData : public QSimplexVariable { + enum Type { + Normal = 0, + Sequential, + Parallel + }; AnchorData(qreal minimumSize, qreal preferredSize, qreal maximumSize) : QSimplexVariable(), minSize(minimumSize), prefSize(preferredSize), maxSize(maximumSize), sizeAtMinimum(preferredSize), sizeAtPreferred(preferredSize), sizeAtMaximum(preferredSize), - skipInPreferred(0) {} + skipInPreferred(0), type(Normal) {} AnchorData(qreal size = 0) : QSimplexVariable(), minSize(size), prefSize(size), maxSize(size), sizeAtMinimum(size), sizeAtPreferred(size), sizeAtMaximum(size), - skipInPreferred(0) {} + skipInPreferred(0), type(Normal) {} inline QString toString() const; QString name; @@ -157,6 +162,13 @@ struct AnchorData : public QSimplexVariable { qreal sizeAtMaximum; uint skipInPreferred : 1; + uint type : 2; // either Normal, Sequential or Parallel +protected: + AnchorData(Type type, qreal size = 0) + : QSimplexVariable(), minSize(size), prefSize(size), + maxSize(size), sizeAtMinimum(size), + sizeAtPreferred(size), sizeAtMaximum(size), + skipInPreferred(0), type(type) {} }; inline QString AnchorData::toString() const @@ -167,6 +179,18 @@ inline QString AnchorData::toString() const } +struct SequentialAnchorData : public AnchorData +{ + SequentialAnchorData() : AnchorData(AnchorData::Sequential) {} + QVector m_children; // list of vertices in the sequence +}; + +struct ParallelAnchorData : public AnchorData +{ + ParallelAnchorData() : AnchorData(AnchorData::Parallel) {} + QVector children; // list of parallel edges +}; + /*! \internal @@ -229,6 +253,16 @@ public: static Orientation edgeOrientation(QGraphicsAnchorLayout::Edge edge); + static QGraphicsAnchorLayout::Edge pickEdge(QGraphicsAnchorLayout::Edge edge, Orientation orientation) + { + if (orientation == Vertical && int(edge) <= 2) + return (QGraphicsAnchorLayout::Edge)(edge + 3); + else if (orientation == Horizontal && int(edge) >= 3) { + return (QGraphicsAnchorLayout::Edge)(edge - 3); + } + return edge; + } + // Init methods void createLayoutEdges(); void deleteLayoutEdges(); @@ -259,6 +293,7 @@ public: void addChildItem(QGraphicsLayoutItem *child); // Activation methods + void simplifyGraph(Orientation orientation); void calculateGraphs(); void calculateGraphs(Orientation orientation); void setAnchorSizeHintsFromItems(Orientation orientation); -- cgit v0.12 From 07cac98dddb5bf3f4c6705d53ef240abcf6e8e90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Thu, 11 Jun 2009 13:37:45 +0200 Subject: Improve how graphviz (dotty) renders the serialized graph. The black arrow shows the principal direction based on the "origin" member of AnchorData. --- src/gui/graphicsview/qgraph_p.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h index 6f4d7bd..63ba53b 100644 --- a/src/gui/graphicsview/qgraph_p.h +++ b/src/gui/graphicsview/qgraph_p.h @@ -131,7 +131,16 @@ public: for (int i = 0; i < adjacents.count(); ++i) { Vertex *v1 = adjacents.at(i); EdgeData *data = edgeData(v, v1); - edges += QString::fromAscii("%1->%2 [label=\"[%3,%4]\"]\n").arg(v->toString()).arg(v1->toString()).arg(data->minSize).arg(data->maxSize); + bool forward = data->origin == v; + if (forward) { + edges += QString::fromAscii("%1->%2 [label=\"[%3,%4]\" dir=both color=\"#000000:#a0a0a0\"] \n") + .arg(v->toString()) + .arg(v1->toString()) + .arg(data->minSize) + .arg(data->maxSize) + ; + } + } strVertices += QString::fromAscii("%1 [label=\"%2\"]\n").arg(v->toString()).arg(v->toString()); } -- cgit v0.12 From a41ac791921329c33947a0748d76683868a9bbf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Thu, 11 Jun 2009 13:49:29 +0200 Subject: Improved sequential simplification. Added restoreSimplifiedGraph(). See comment in simplifyGraph on how the overall approach is. There are still outstanding issues: 1. Simplify parallel anchors 2. Use linear layout to distribute sequential anchors. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 251 ++++++++++++++++++----- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 2 + 2 files changed, 197 insertions(+), 56 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 62d2d9a..0339f41 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -126,21 +126,84 @@ inline static qreal checkAdd(qreal a, qreal b) return FLT_MAX; return a + 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 void simplifySequentialChunk(Graph *graph, + AnchorVertex *before, + const QVector &vertices, + AnchorVertex *after) +{ + int i; +#if 0 + QString strVertices; + for (i = 0; i < vertices.count(); ++i) + strVertices += QString::fromAscii("%1 - ").arg(vertices.at(i)->toString()); + QString strPath = QString::fromAscii("%1 - %2%3").arg(before->toString(), strVertices, after->toString()); + qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString())); +#endif + + if (graph->edgeData(before, after)) { + qDebug("### FIXME: Need to merge parallel anchors, ignoring this simplification for now"); + return; + } + + qreal min = 0; + qreal pref = 0; + qreal max = 0; + + SequentialAnchorData *sequence = new SequentialAnchorData; + AnchorVertex *prev = before; + AnchorData *data; + for (i = 0; i <= vertices.count(); ++i) { + AnchorVertex *next = (i < vertices.count()) ? vertices.at(i) : after; + data = graph->takeEdge(prev, next); + min += data->minSize; + pref += data->prefSize; + max = checkAdd(max, data->maxSize); + sequence->m_edges.append(data); + prev = next; + } + + // insert new + sequence->minSize = min; + sequence->prefSize = pref; + sequence->maxSize = max; + sequence->m_children = vertices; + sequence->origin = data->origin == vertices.last() ? before : after; + graph->createEdge(before, after, sequence); +} /*! * \internal * * The purpose of this function is to simplify the graph. The process of simplification can be - * broken down to two methods: - * Algorithm can be described as: + * described as: * - * 1. Simplify all sequence of anchors into one anchor. + * 1. Simplify all sequences of anchors into one anchor. * If not first iteration and no further simplification was done, go to (3) * 2. Simplify two parallel anchors into one anchor. * If any simplification was done, go to (1) * 3. Done * + * + * The algorithm walks the graph in depth-first order, and only collects vertices that has two + * edges connected to it. If the vertex does not have two edges or if it is a layout edge, + * it will take all the previously collected vertices and try to create a simplified sequential + * anchor representing all the previously collected vertices. + * Once the simplified anchor is inserted, the collected list is cleared in order to find the next + * sequence to simplify. + * Note that there are some catches to this that are not covered by the above explanation. + * + * * Notes: - * * The algorithm should not make a sequence of the layout edge anchors. + * * The algorithm should not make a sequence of the layout edge anchors. * => Make sure those edges are not traversed * * A generic algorithm will make a sequential simplification node of a Left-HCenter-Right * sequence. This is ok, but that sequence should not be affected by stretch factors. @@ -165,67 +228,98 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O v = stack.pop(); QList vertices = g.adjacentVertices(v); const int count = vertices.count(); + bool endOfSequence = (v->m_item == q && v->m_edge == layoutEdge) || count != 2; if (count == 2 && v->m_item != q) { candidates.append(v); + if (visited.contains(vertices.first()) && visited.contains(vertices.last())) { + // in case of a cycle + endOfSequence = true; + } } - if ((v->m_item == q && v->m_edge == layoutEdge) || (count != 2 && candidates.count() >= 1)) { - SequentialAnchorData *sequence = new SequentialAnchorData; - AnchorVertex * &sequenceLast = v; //alias - AnchorVertex *sequenceFirst = 0; + if (endOfSequence && candidates.count() >= 2) { + int i; + AnchorVertex *afterSequence= 0; + { + QList adjacentOfSecondLastVertex = g.adjacentVertices(candidates.last()); + Q_ASSERT(adjacentOfSecondLastVertex.count() == 2); + if (adjacentOfSecondLastVertex.first() == candidates.at(candidates.count() - 2)) + afterSequence = adjacentOfSecondLastVertex.last(); + else + afterSequence = adjacentOfSecondLastVertex.first(); + } + + AnchorVertex *beforeSequence = 0; + { QList adjacentOfSecondVertex = g.adjacentVertices(candidates.first()); Q_ASSERT(adjacentOfSecondVertex.count() == 2); if (adjacentOfSecondVertex.first() == candidates.at(1)) - sequenceFirst = adjacentOfSecondVertex.last(); + beforeSequence = adjacentOfSecondVertex.last(); else - sequenceFirst = adjacentOfSecondVertex.first(); - - // The complete path of the sequence to simplify is: sequenceFirst, , sequenceLast - qreal min = 0; - qreal pref = 0; - qreal max = 0; - -/* // ### DEBUG + beforeSequence = adjacentOfSecondVertex.first(); + } + // The complete path of the sequence to simplify is: beforeSequence, , afterSequence + // where beforeSequence and afterSequence are the endpoints where the anchor is inserted + // between. +#if 0 + // ### DEBUG QString strCandidates; - for (int i = 0; i < candidates.count(); ++i) - strCandidates += QString::fromAscii("%1--").arg(candidates.at(i)->toString()); - QString strPath = QString::fromAscii("%1--%2%3").arg(sequenceFirst->toString(), strCandidates, sequenceLast->toString()); - qDebug("simplifying [%s] to [%s--%s]", qPrintable(strPath), qPrintable(sequenceFirst->toString()), qPrintable(sequenceLast->toString())); -*/ + for (i = 0; i < candidates.count(); ++i) + strCandidates += QString::fromAscii("%1 - ").arg(candidates.at(i)->toString()); + QString strPath = QString::fromAscii("%1 - %2%3").arg(beforeSequence->toString(), strCandidates, afterSequence->toString()); + qDebug("candidate list for sequential simplification:\n[%s]", qPrintable(strPath), qPrintable(beforeSequence->toString()), qPrintable(afterSequence->toString())); +#endif + + bool forward; + AnchorVertex *prev = beforeSequence; + int intervalFrom = 0; + int intervalTo = candidates.count() + 1; + bool forwardSet = false; + + // Check for directionality (origin). We don't want to destroy that information, + // thus we only combine anchors with the same direction. + + // "i" is the index *including* the beforeSequence and afterSequence vertices. + for (i = 1; i <= candidates.count() + 1; ++i) { + AnchorVertex *v1 = (i <= candidates.count()) ? candidates.at(i - 1) : afterSequence; + AnchorData *data = g.edgeData(prev, v1); + bool shouldSimplify = false; + if (data) { + if (!forwardSet) { + forward = (prev == data->origin ? true : false); + forwardSet = true; + } else if ((forward != (prev == data->origin))) { + shouldSimplify = true; + } + } else { + shouldSimplify = true; + forwardSet = false; + } - AnchorData *data = g.edgeData(sequenceFirst, candidates.first()); - min += data->minSize; - pref += data->prefSize; - max = checkAdd(max, data->maxSize); - g.removeEdge(sequenceFirst, candidates.first()); - - for (int i = 0; i < candidates.count() - 1; ++i) { - AnchorVertex *v1 = candidates.at(i); - AnchorVertex *v2 = candidates.at(i + 1); - data = g.edgeData(v1, v2); - min += data->minSize; - pref += data->prefSize; - max = checkAdd(max, data->maxSize); - g.removeEdge(v1, v2); + if (shouldSimplify) { + intervalTo = i - 1; + if (intervalTo - intervalFrom >= 2) { + // simplify in the range [intervalFrom, intervalTo] + AnchorVertex *intervalVertexFrom = intervalFrom == 0 ? beforeSequence : candidates.at(intervalFrom - 1); + AnchorVertex *intervalVertexTo = intervalTo <= candidates.count() ? candidates.at(intervalTo - 1) : afterSequence; + QVector subCandidates = candidates.mid(intervalFrom, intervalTo - intervalFrom - 1); + simplifySequentialChunk(&g, intervalVertexFrom, subCandidates, intervalVertexTo); + // finished simplification of chunk with same direction + } + intervalFrom = intervalTo; + forward = !forward; + } + prev = v1; + } + if (intervalTo - intervalFrom >= 2) { + // simplify in the range [intervalFrom, intervalTo] + AnchorVertex *intervalVertexFrom = intervalFrom == 0 ? beforeSequence : candidates.at(intervalFrom - 1); + AnchorVertex *intervalVertexTo = intervalTo <= candidates.count() ? candidates.at(intervalTo - 1) : afterSequence; + QVector subCandidates = candidates.mid(intervalFrom, intervalTo - intervalFrom - 1); + simplifySequentialChunk(&g, intervalVertexFrom, subCandidates, intervalVertexTo); + // finished simplification of chunk with same direction } - - data = g.edgeData(candidates.last(), sequenceLast); - min += data->minSize; - pref += data->prefSize; - max = checkAdd(max, data->maxSize); - g.removeEdge(candidates.last(), sequenceLast); - - // insert new - sequence->minSize = min; - sequence->prefSize = pref; - sequence->maxSize = max; - sequence->m_children = candidates; - sequence->origin = sequenceFirst; - g.createEdge(sequenceFirst, sequenceLast, sequence); - - // start all over again - candidates.clear(); } - if (count != 2) + if (endOfSequence) candidates.clear(); QGraphicsAnchorLayout::Edge centerEdge = pickEdge(QGraphicsAnchorLayout::HCenter, orientation); @@ -241,6 +335,49 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O } } +void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientation) +{ + Q_Q(QGraphicsAnchorLayout); + Graph &g = graph[orientation]; + AnchorVertex *v = g.rootVertex(); + + if (!v) + return; + QSet visited; + QStack stack; + stack.push(v); + + QGraphicsAnchorLayout::Edge layoutCenterEdge = pickEdge(QGraphicsAnchorLayout::HCenter, orientation); + // walk depth-first. + while (!stack.isEmpty()) { + v = stack.pop(); + QList vertices = g.adjacentVertices(v); + const int count = vertices.count(); + for (int i = 0; i < count; ++i) { + AnchorVertex *next = vertices.at(i); + if (next->m_item == q && next->m_edge == layoutCenterEdge) + continue; + if (visited.contains(next)) + continue; + AnchorData *edge = g.edgeData(v, next); + if (edge->type == AnchorData::Sequential) { + SequentialAnchorData* seqEdge = static_cast(edge); + // restore the sequential anchor + AnchorVertex *prev = v; + for (int i = 0; i < seqEdge->m_edges.count(); ++i) { + AnchorVertex *v1 = (i < seqEdge->m_children.count()) ? seqEdge->m_children.at(i) : next; + AnchorData *data = seqEdge->m_edges.at(i); + g.createEdge(prev, v1, data); + prev = v1; + } + g.removeEdge(v, next); + } + stack.push(next); + } + visited.insert(v); + } +} + QGraphicsAnchorLayoutPrivate::Orientation QGraphicsAnchorLayoutPrivate::edgeOrientation(QGraphicsAnchorLayout::Edge edge) { @@ -613,9 +750,11 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( setAnchorSizeHintsFromItems(orientation); // ### currently crashes -// q->dumpGraph(); + //q->dumpGraph(); // simplifyGraph(orientation); -// q->dumpGraph(); + //q->dumpGraph(); +// restoreSimplifiedGraph(orientation); // should not be here + //q->dumpGraph(); // Traverse all graph edges and store the possible paths to each vertex findPaths(orientation); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 71c00a2..f945cf7 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -183,6 +183,7 @@ struct SequentialAnchorData : public AnchorData { SequentialAnchorData() : AnchorData(AnchorData::Sequential) {} QVector m_children; // list of vertices in the sequence + QVector m_edges; // keep the list of edges too. }; struct ParallelAnchorData : public AnchorData @@ -294,6 +295,7 @@ public: // Activation methods void simplifyGraph(Orientation orientation); + void restoreSimplifiedGraph(Orientation orientation); void calculateGraphs(); void calculateGraphs(Orientation orientation); void setAnchorSizeHintsFromItems(Orientation orientation); -- cgit v0.12 From b7150b21f02435abb8f7a4d94b929ecdad2ef79b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Thu, 11 Jun 2009 15:25:05 +0200 Subject: Simplify the code that finds the sequential chunks for simplification. If two vertices are adjacent in the candidate list they should also be adjacent in the graph. This means there should always be an edge between candidates.at(n) and candidates.at(n + 1). --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 0339f41..3745b3b 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -282,20 +282,10 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O for (i = 1; i <= candidates.count() + 1; ++i) { AnchorVertex *v1 = (i <= candidates.count()) ? candidates.at(i - 1) : afterSequence; AnchorData *data = g.edgeData(prev, v1); - bool shouldSimplify = false; - if (data) { - if (!forwardSet) { - forward = (prev == data->origin ? true : false); - forwardSet = true; - } else if ((forward != (prev == data->origin))) { - shouldSimplify = true; - } - } else { - shouldSimplify = true; - forwardSet = false; - } - - if (shouldSimplify) { + Q_ASSERT(data); + if (i == 1) { + forward = (prev == data->origin ? true : false); + } else if ((forward != (prev == data->origin))) { intervalTo = i - 1; if (intervalTo - intervalFrom >= 2) { // simplify in the range [intervalFrom, intervalTo] -- cgit v0.12 From 92bca69c15898dd504f96e18227a22566038154a Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Mon, 22 Jun 2009 12:27:31 -0300 Subject: QGraphicsAnchorLayout: Fix QGraphicsItem reparent code Using QGraphicsLayoutPrivate methods for proper reparenting of items. This makes it work in cases where nested layouts are used. Signed-off-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 4 ++-- src/gui/graphicsview/qgraphicsanchorlayout.h | 3 +-- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 30 ------------------------ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 4 ---- 4 files changed, 3 insertions(+), 38 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 1a72ace..a86626b 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -105,11 +105,11 @@ void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, // Ensure that the internal anchors have been created for both items. if (firstItem != this && !d->items.contains(firstItem)) { d->createItemEdges(firstItem); - d->addChildItem(firstItem); + d->addChildLayoutItem(firstItem); } if (secondItem != this && !d->items.contains(secondItem)) { d->createItemEdges(secondItem); - d->addChildItem(secondItem); + d->addChildLayoutItem(secondItem); } // Use heuristics to find out what the user meant with this anchor. diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h index fb6c396..cd1c080 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -94,8 +94,7 @@ public: protected: private: - -// Q_DISABLE_COPY(QGraphicsAnchorLayout) //### TO UNCOMMENT + Q_DISABLE_COPY(QGraphicsAnchorLayout) Q_DECLARE_PRIVATE(QGraphicsAnchorLayout) }; diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 3745b3b..3569b3d 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -673,36 +673,6 @@ void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&fi /*! \internal - XXX: REMOVE THIS ONCE WE INHERIT SOMEONE ELSE! -*/ -QGraphicsItem *QGraphicsAnchorLayoutPrivate::parentItem() const -{ - Q_Q(const QGraphicsLayoutItem); - - const QGraphicsLayoutItem *parent = q; - while (parent && parent->isLayout()) { - parent = parent->parentLayoutItem(); - } - return parent ? parent->graphicsItem() : 0; -} - -/*! - \internal -*/ -void QGraphicsAnchorLayoutPrivate::addChildItem(QGraphicsLayoutItem *child) -{ - // XXX: Re-implement this!! - if (child) { - Q_Q(QGraphicsAnchorLayout); - child->setParentLayoutItem(q); - - child->graphicsItem()->setParentItem(parentItem()); - } -} - -/*! - \internal - Called on activation. Uses Linear Programming to define minimum, preferred and maximum sizes for the layout. Also calculates the sizes that each item should assume when the layout is in one of such situations. diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index f945cf7..1e9af63 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -289,10 +289,6 @@ public: QGraphicsLayoutItem *&secondItem, QGraphicsAnchorLayout::Edge &secondEdge); - // Child manipulation methods - QGraphicsItem *parentItem() const; - void addChildItem(QGraphicsLayoutItem *child); - // Activation methods void simplifyGraph(Orientation orientation); void restoreSimplifiedGraph(Orientation orientation); -- cgit v0.12 From d3d455e6d019a381b338c24b3c2593b2e12215cb Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Tue, 23 Jun 2009 10:45:49 -0300 Subject: QGraphicsAnchorLayout: Add convencience "fill" methods Adding "fillWidth()", "fillHeight()" and "fill()". These convenience methods simplify the creation of anchor setups where some items are meant to use the full width and/or height of the layout. For instance, instead of creating four anchors (bottom, top, left, right) between an item and the layout, to make it use the full layout area, the user can call layout->fill(item). It is also possible to make an item assume the same width or height of another item, calling layout->fill(firstItem, secondItem). Signed-off-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout.h | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h index cd1c080..74075ff 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -78,6 +78,13 @@ public: void removeAnchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, QGraphicsLayoutItem *secondItem, Edge secondEdge); + inline void fillWidth(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo = 0); + inline void fillHeight(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo = 0); + inline void fill(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo = 0); + void setSpacing(qreal spacing, Qt::Orientations orientations = Qt::Horizontal|Qt::Vertical); qreal spacing(Qt::Orientation) const; @@ -98,6 +105,35 @@ private: Q_DECLARE_PRIVATE(QGraphicsAnchorLayout) }; +void QGraphicsAnchorLayout::fillWidth(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo) +{ + if (!relativeTo) + relativeTo = this; + + anchor(relativeTo, Left, item, Left); + anchor(item, Right, relativeTo, Right); +} + +void QGraphicsAnchorLayout::fillHeight(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo) +{ + if (!relativeTo) + relativeTo = this; + + anchor(relativeTo, Top, item, Top); + anchor(item, Bottom, relativeTo, Bottom); +} + +void QGraphicsAnchorLayout::fill(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo) +{ + if (!relativeTo) + relativeTo = this; + + fillWidth(item, relativeTo); + fillHeight(item, relativeTo); +} #endif -- cgit v0.12 From ca3ab7615d08742cb81dcc6fab723b89355ac82a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 24 Jun 2009 16:45:18 +0200 Subject: Implemented parallel simplification, some bugfixes of the previous code. Currently, the code is not in effect for the simplex solver kicks in (it crashes), but it is in effect for each layout to check that simplification and restoring the simplification back again works. This is currently done in calculateGraphs() Parts of the code does not read well, especially the detection of the sequential chunks. --- src/gui/graphicsview/qgraph_p.h | 18 ++-- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 104 +++++++++++++++-------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 26 +++++- 3 files changed, 104 insertions(+), 44 deletions(-) diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h index 63ba53b..9128df8 100644 --- a/src/gui/graphicsview/qgraph_p.h +++ b/src/gui/graphicsview/qgraph_p.h @@ -68,9 +68,16 @@ public: return const_iterator(this,false); } + /*! + * \internal + * + * If there is an edge between \a first and \a second, it will return a structure + * containing the data associated with the edge, otherwise it will return 0. + * + */ EdgeData *edgeData(Vertex* first, Vertex* second) { - Q_ASSERT(m_graph.value(first)); - return m_graph.value(first)->value(second); + QHash *row = m_graph.value(first); + return row ? row->value(second) : 0; } void createEdge(Vertex *first, Vertex *second, EdgeData *data) @@ -93,8 +100,10 @@ public: { // Removes a bidirectional edge EdgeData *data = edgeData(first, second); - removeDirectedEdge(first, second); - removeDirectedEdge(second, first); + if (data) { + removeDirectedEdge(first, second); + removeDirectedEdge(second, first); + } return data; } @@ -140,7 +149,6 @@ public: .arg(data->maxSize) ; } - } strVertices += QString::fromAscii("%1 [label=\"%2\"]\n").arg(v->toString()).arg(v->toString()); } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 3569b3d..502da5d 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -150,11 +150,6 @@ static void simplifySequentialChunk(Graph *graph, qDebug("simplifying [%s] to [%s - %s]", qPrintable(strPath), qPrintable(before->toString()), qPrintable(after->toString())); #endif - if (graph->edgeData(before, after)) { - qDebug("### FIXME: Need to merge parallel anchors, ignoring this simplification for now"); - return; - } - qreal min = 0; qreal pref = 0; qreal max = 0; @@ -176,10 +171,21 @@ static void simplifySequentialChunk(Graph *graph, sequence->minSize = min; sequence->prefSize = pref; sequence->maxSize = max; - sequence->m_children = vertices; + sequence->setVertices(vertices); sequence->origin = data->origin == vertices.last() ? before : after; - graph->createEdge(before, after, sequence); + AnchorData *newAnchor = sequence; + if (AnchorData *oldAnchor = graph->takeEdge(before, after)) { + newAnchor = new ParallelAnchorData(oldAnchor, sequence); + min = qMax(oldAnchor->minSize, sequence->minSize); + pref = qMax(oldAnchor->prefSize, sequence->prefSize); + max = qMin(oldAnchor->maxSize, sequence->maxSize); + newAnchor->minSize = min; + newAnchor->prefSize = pref; + newAnchor->maxSize = max; + } + graph->createEdge(before, after, newAnchor); } + /*! * \internal * @@ -239,24 +245,20 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O if (endOfSequence && candidates.count() >= 2) { int i; AnchorVertex *afterSequence= 0; - { QList adjacentOfSecondLastVertex = g.adjacentVertices(candidates.last()); Q_ASSERT(adjacentOfSecondLastVertex.count() == 2); if (adjacentOfSecondLastVertex.first() == candidates.at(candidates.count() - 2)) afterSequence = adjacentOfSecondLastVertex.last(); else afterSequence = adjacentOfSecondLastVertex.first(); - } AnchorVertex *beforeSequence = 0; - { QList adjacentOfSecondVertex = g.adjacentVertices(candidates.first()); Q_ASSERT(adjacentOfSecondVertex.count() == 2); if (adjacentOfSecondVertex.first() == candidates.at(1)) beforeSequence = adjacentOfSecondVertex.last(); else beforeSequence = adjacentOfSecondVertex.first(); - } // The complete path of the sequence to simplify is: beforeSequence, , afterSequence // where beforeSequence and afterSequence are the endpoints where the anchor is inserted // between. @@ -266,7 +268,7 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O for (i = 0; i < candidates.count(); ++i) strCandidates += QString::fromAscii("%1 - ").arg(candidates.at(i)->toString()); QString strPath = QString::fromAscii("%1 - %2%3").arg(beforeSequence->toString(), strCandidates, afterSequence->toString()); - qDebug("candidate list for sequential simplification:\n[%s]", qPrintable(strPath), qPrintable(beforeSequence->toString()), qPrintable(afterSequence->toString())); + qDebug("candidate list for sequential simplification:\n[%s]", qPrintable(strPath)); #endif bool forward; @@ -280,34 +282,45 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O // "i" is the index *including* the beforeSequence and afterSequence vertices. for (i = 1; i <= candidates.count() + 1; ++i) { - AnchorVertex *v1 = (i <= candidates.count()) ? candidates.at(i - 1) : afterSequence; + bool atVertexAfter = i > candidates.count(); + AnchorVertex *v1 = atVertexAfter ? afterSequence : candidates.at(i - 1); AnchorData *data = g.edgeData(prev, v1); Q_ASSERT(data); if (i == 1) { forward = (prev == data->origin ? true : false); - } else if ((forward != (prev == data->origin))) { - intervalTo = i - 1; + } else if (forward != (prev == data->origin) || atVertexAfter) { + int intervalTo = i; + if (forward != (prev == data->origin)) + --intervalTo; + + // intervalFrom and intervalTo should now be indices to the vertex before and + // after the sequential anchor. if (intervalTo - intervalFrom >= 2) { // simplify in the range [intervalFrom, intervalTo] AnchorVertex *intervalVertexFrom = intervalFrom == 0 ? beforeSequence : candidates.at(intervalFrom - 1); AnchorVertex *intervalVertexTo = intervalTo <= candidates.count() ? candidates.at(intervalTo - 1) : afterSequence; - QVector subCandidates = candidates.mid(intervalFrom, intervalTo - intervalFrom - 1); + QVector subCandidates; + if (forward) { + subCandidates = candidates.mid(intervalFrom, intervalTo - intervalFrom - 1); + } else { + // reverse the order of the candidates. + qSwap(intervalVertexFrom, intervalVertexTo); + do { + ++intervalFrom; + subCandidates.prepend(candidates.at(intervalFrom - 1)); + } while (intervalFrom < intervalTo - 1); + } simplifySequentialChunk(&g, intervalVertexFrom, subCandidates, intervalVertexTo); // finished simplification of chunk with same direction } + if (forward == (prev == data->origin)) + --intervalTo; intervalFrom = intervalTo; + forward = !forward; } prev = v1; } - if (intervalTo - intervalFrom >= 2) { - // simplify in the range [intervalFrom, intervalTo] - AnchorVertex *intervalVertexFrom = intervalFrom == 0 ? beforeSequence : candidates.at(intervalFrom - 1); - AnchorVertex *intervalVertexTo = intervalTo <= candidates.count() ? candidates.at(intervalTo - 1) : afterSequence; - QVector subCandidates = candidates.mid(intervalFrom, intervalTo - intervalFrom - 1); - simplifySequentialChunk(&g, intervalVertexFrom, subCandidates, intervalVertexTo); - // finished simplification of chunk with same direction - } } if (endOfSequence) candidates.clear(); @@ -349,18 +362,32 @@ void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientatio continue; if (visited.contains(next)) continue; - AnchorData *edge = g.edgeData(v, next); - if (edge->type == AnchorData::Sequential) { - SequentialAnchorData* seqEdge = static_cast(edge); - // restore the sequential anchor - AnchorVertex *prev = v; - for (int i = 0; i < seqEdge->m_edges.count(); ++i) { - AnchorVertex *v1 = (i < seqEdge->m_children.count()) ? seqEdge->m_children.at(i) : next; - AnchorData *data = seqEdge->m_edges.at(i); - g.createEdge(prev, v1, data); - prev = v1; + + QQueue queue; + queue.enqueue(g.edgeData(v, next)); + while (!queue.isEmpty()) { + AnchorData *edge = queue.dequeue(); + if (edge->type == AnchorData::Sequential) { + SequentialAnchorData* seqEdge = static_cast(edge); + // restore the sequential anchor + AnchorVertex *prev = v; + AnchorVertex *last = next; + if (edge->origin != prev) + qSwap(last, prev); + + for (int i = 0; i < seqEdge->m_edges.count(); ++i) { + AnchorVertex *v1 = (i < seqEdge->m_children.count()) ? seqEdge->m_children.at(i) : last; + AnchorData *data = seqEdge->m_edges.at(i); + g.createEdge(prev, v1, data); + prev = v1; + } + g.removeEdge(v, next); + } else if (edge->type == AnchorData::Parallel) { + ParallelAnchorData* parallelEdge = static_cast(edge); + queue.enqueue(parallelEdge->firstEdge); + queue.enqueue(parallelEdge->secondEdge); + g.removeEdge(v, next); } - g.removeEdge(v, next); } stack.push(next); } @@ -682,6 +709,13 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs() if (!calculateGraphCacheDirty) return; + simplifyGraph(Horizontal); + simplifyGraph(Vertical); + //q->dumpGraph(); + restoreSimplifiedGraph(Horizontal); // should not be here, but currently crashes if not + restoreSimplifiedGraph(Vertical); // should not be here, but currently crashes if not + + calculateGraphs(Horizontal); calculateGraphs(Vertical); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 1e9af63..605a8e2 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -181,15 +181,33 @@ inline QString AnchorData::toString() const struct SequentialAnchorData : public AnchorData { - SequentialAnchorData() : AnchorData(AnchorData::Sequential) {} + SequentialAnchorData() : AnchorData(AnchorData::Sequential) + { + name = QLatin1String("SequentialAnchorData"); + } + + void setVertices(const QVector &vertices) + { + m_children = vertices; + name = QString::fromAscii("%1 -- %2").arg(vertices.first()->toString(), vertices.last()->toString()); + } + QVector m_children; // list of vertices in the sequence QVector m_edges; // keep the list of edges too. }; struct ParallelAnchorData : public AnchorData { - ParallelAnchorData() : AnchorData(AnchorData::Parallel) {} - QVector children; // list of parallel edges + ParallelAnchorData(AnchorData *first, AnchorData *second) + : AnchorData(AnchorData::Parallel), + firstEdge(first), secondEdge(second) + { + Q_ASSERT(first->origin == second->origin); + origin = first->origin; + name = QString::fromAscii("%1 | %2").arg(first->toString(), second->toString()); + } + AnchorData* firstEdge; + AnchorData* secondEdge; }; /*! @@ -259,7 +277,7 @@ public: if (orientation == Vertical && int(edge) <= 2) return (QGraphicsAnchorLayout::Edge)(edge + 3); else if (orientation == Horizontal && int(edge) >= 3) { - return (QGraphicsAnchorLayout::Edge)(edge - 3); + return (QGraphicsAnchorLayout::Edge)(edge - 3); } return edge; } -- cgit v0.12 From 4424171fc41601831089b08d774813f3985ed5a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 24 Jun 2009 16:51:06 +0200 Subject: Use whitespaces and newlines when saving the xml file. Some whitespace cleanup. --- examples/layouts/anchorlayout/window.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/examples/layouts/anchorlayout/window.cpp b/examples/layouts/anchorlayout/window.cpp index 8658d06..32efa69 100644 --- a/examples/layouts/anchorlayout/window.cpp +++ b/examples/layouts/anchorlayout/window.cpp @@ -98,7 +98,7 @@ void Window::on_actionSave_layout_triggered(bool ) if (!fileName.isEmpty()) saveLayout(fileName); } - + void Window::on_itemName_textEdited(const QString & ) { updateItem(); @@ -193,7 +193,7 @@ void Window::rebuildLayout() QGraphicsAnchorLayout::Edge endEdge = (QGraphicsAnchorLayout::Edge)(m_ui.anchors->item(i, 3)->data(Qt::UserRole).toInt(&ok)); if (!ok) continue; - + m_layout->anchor(startItem, startEdge, endItem, endEdge); } } @@ -219,14 +219,14 @@ QGraphicsLayoutItem *Window::addItem(const QString &name) return layoutItem; } -static const char *strEdges[] = {"Left", - "HCenter", - "Right", - "Top", - "VCenter", +static const char *strEdges[] = {"Left", + "HCenter", + "Right", + "Top", + "VCenter", "Bottom"}; -void Window::setAnchorData(QGraphicsLayoutItem *startItem, const QString &startName, QGraphicsAnchorLayout::Edge startEdge, +void Window::setAnchorData(QGraphicsLayoutItem *startItem, const QString &startName, QGraphicsAnchorLayout::Edge startEdge, QGraphicsLayoutItem *endItem, const QString &endName, QGraphicsAnchorLayout::Edge endEdge, int row /*= -1*/) { if (row == -1) { @@ -305,6 +305,7 @@ bool Window::saveLayout(const QString& fileName) ok = out.open(QIODevice::WriteOnly); if (ok) { QXmlStreamWriter xml(&out); + xml.setAutoFormatting(true); xml.writeStartDocument(); xml.writeStartElement(QLatin1String("anchorlayout")); int i; @@ -319,8 +320,8 @@ bool Window::saveLayout(const QString& fileName) const char *propertyNames[] = {"minimumSize", "preferredSize", "maximumSize"}; int b; typedef QSizeF (QGraphicsLayoutItem::*QGLISizeGetter)(void) const; - QGLISizeGetter sizeGetters[] = { &QGraphicsLayoutItem::minimumSize, - &QGraphicsLayoutItem::preferredSize, + QGLISizeGetter sizeGetters[] = { &QGraphicsLayoutItem::minimumSize, + &QGraphicsLayoutItem::preferredSize, &QGraphicsLayoutItem::maximumSize}; QSizeF size = ((*item).*(sizeGetters[p])) (); xml.writeStartElement(QLatin1String("property")); @@ -355,7 +356,7 @@ bool Window::saveLayout(const QString& fileName) QGraphicsAnchorLayout::Edge endEdge = (QGraphicsAnchorLayout::Edge)(m_ui.anchors->item(i, 3)->data(Qt::UserRole).toInt(&ok)); if (!ok) continue; - + QString strStart = nodeName(startItem); if (strStart == QLatin1String("layout")) strStart = QLatin1String("this"); @@ -391,7 +392,7 @@ static bool parseProperty(QXmlStreamReader *xml, QString *name, QSizeF *size) if (ok) { sz.setWidth(w); } - + float h = sw.toFloat(&ok); if (ok) { sz.setHeight(h); @@ -404,7 +405,7 @@ static bool parseProperty(QXmlStreamReader *xml, QString *name, QSizeF *size) *size = sz; return true; } - } + } } return false; } @@ -467,7 +468,7 @@ bool Window::loadLayout(const QString& fileName, QGraphicsAnchorLayout *layout) break; case QXmlStreamReader::StartElement: if (level == 0 && xml.name() == str_anchorlayout) { - + } else if (level == 1 && xml.name() == str_item) { item_id = xml.attributes().value("id").toString(); } else if (level == 1 && xml.name() == QLatin1String("anchor")) { @@ -503,7 +504,7 @@ bool Window::loadLayout(const QString& fileName, QGraphicsAnchorLayout *layout) qWarning("%s is not a valid edge description", qPrintable(second)); break; } - setAnchorData( startItem, startID, startEdge, + setAnchorData( startItem, startID, startEdge, endItem, endID, endEdge, -1); } else if (level == 2 && xml.name() == str_property) { QString name; -- cgit v0.12 From a3d7de451c005d76e6680a785195fab0f986c06c Mon Sep 17 00:00:00 2001 From: "Anselmo Lacerda S. de Melo" Date: Fri, 29 May 2009 16:49:02 -0300 Subject: QSimplex: new method solver, avoinding code replication The methods solveMin() and solveMax() had similar implementation, except by a "-1" multiplier. This commit includes a new private method called solver that is called by both solveMin() and solveMax(). A new enum 'solverFactor' was added admiting 2 values - Maximum (+1) and Minimum (-1). Signed-off-by: Anselmo Lacerda S. de Melo --- src/gui/graphicsview/qsimplex_p.cpp | 34 ++++++++++++++++------------------ src/gui/graphicsview/qsimplex_p.h | 2 ++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp index 774af8c..b1cba45 100644 --- a/src/gui/graphicsview/qsimplex_p.cpp +++ b/src/gui/graphicsview/qsimplex_p.cpp @@ -306,7 +306,14 @@ bool QSimplex::iterate() return true; } -qreal QSimplex::solveMin() +/*! + \internal + + Both solveMin and solveMax are interfaces to this method. + + The enum solverFactor admits 2 values: Minimum (-1) and Maximum (+1). + */ +qreal QSimplex::solver(solverFactor factor) { // Remove old objective clearRow(0); @@ -316,32 +323,23 @@ qreal QSimplex::solveMin() for (iter = objective->variables.constBegin(); iter != objective->variables.constEnd(); ++iter) { - setValueAt(0, iter.key()->index, iter.value()); + setValueAt(0, iter.key()->index, -1 * factor * iter.value()); } solveMaxHelper(); collectResults(); - return -1 * valueAt(0, columns - 1); + return factor * valueAt(0, columns - 1); } -qreal QSimplex::solveMax() +qreal QSimplex::solveMin() { - // Remove old objective - clearRow(0); - - // Set new objective - QHash::const_iterator iter; - for (iter = objective->variables.constBegin(); - iter != objective->variables.constEnd(); - ++iter) { - setValueAt(0, iter.key()->index, -1 * iter.value()); - } - - solveMaxHelper(); - collectResults(); + return solver(Minimum); +} - return valueAt(0, columns - 1); +qreal QSimplex::solveMax() +{ + return solver(Maximum); } void QSimplex::collectResults() diff --git a/src/gui/graphicsview/qsimplex_p.h b/src/gui/graphicsview/qsimplex_p.h index 3881893..dad82ce 100644 --- a/src/gui/graphicsview/qsimplex_p.h +++ b/src/gui/graphicsview/qsimplex_p.h @@ -88,6 +88,8 @@ private: // Helpers void clearDataStructures(); void solveMaxHelper(); + enum solverFactor { Minimum = -1, Maximum = 1 }; + qreal solver(solverFactor factor); void collectResults(); QList constraints; -- cgit v0.12 From afaa14f2d84c0071b1ca8b75e35ba456382bb29f Mon Sep 17 00:00:00 2001 From: "Anselmo Lacerda S. de Melo" Date: Fri, 29 May 2009 20:59:28 -0300 Subject: QSimplex: Skip x = x + y*0.0 A small optimization. Added a verification in combineRows to skip calculating x = x + y*0.0 as it obviously won't change the value of x. Signed-off-by: Anselmo Lacerda S. de Melo --- src/gui/graphicsview/qsimplex_p.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp index b1cba45..b90d53f 100644 --- a/src/gui/graphicsview/qsimplex_p.cpp +++ b/src/gui/graphicsview/qsimplex_p.cpp @@ -220,7 +220,13 @@ void QSimplex::combineRows(int toIndex, int fromIndex, qreal factor) qreal *to = matrix + toIndex * columns; for (int j = 1; j < columns; ++j) { - to[j] += factor * from[j]; + qreal value = from[j]; + + // skip to[j] = to[j] + factor*0.0 + if (value == 0.0) + continue; + + to[j] += factor * value; // ### Avoid Numerical errors if (qAbs(to[j]) < 0.0000000001) -- cgit v0.12 From 4610f2b6e28c97b73b7231cac33b828f17b99ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 1 Jul 2009 11:50:45 +0200 Subject: Add QGraphicsLayout::anchor() overloads and query the default spacing. This commit fixes several related issues: 1. QGAL::anchor() had a spacing argument that defaulted to 0. That made it impossible to know if the user meant that the spacing should be 0 or if the spacing should be picked from the style. Instead we have to overload anchor, so now we have one function that does not take spacing as an argument. That one will create an anchor where the spacing is queried from the style. The other overload allows the user to explicitly set the spacing, thus the default spacing is ignored. 2. Make sure we pick up the spacing correctly from the style if needed. setAnchorSizeHintsFromDefaults() will set the correct spacing on anchors that was created without specifying a spacing value. 3. Add QGAL::anchor(Qt::Corner, ...) convenience function with an overload (for the same reason as explained in 1.) 4. Added QGraphicsAnchorLayoutPrivate::anchor() as a helper function that is called from all the 4 public API anchor() functions so that we don't need to have duplicate code for argument checking etc. 5. Fix autotests. They assumed that anchor() without a spacing argument created a spacing of 0. --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 138 ++++++++++++++------- src/gui/graphicsview/qgraphicsanchorlayout.h | 12 +- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 121 ++++++++++++++++++ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 25 +++- .../tst_qgraphicsanchorlayout.cpp | 124 ++++++++++-------- 5 files changed, 316 insertions(+), 104 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index a86626b..31cc911 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -71,59 +71,108 @@ QGraphicsAnchorLayout::~QGraphicsAnchorLayout() Q_ASSERT(d->m_vertexList.isEmpty()); } +/*! + * Creates an anchor between the edge \a firstEdge of item \a firstItem and the edge \a secondEdge + * of item \a secondItem. The magnitude of the anchor is picked up from the style. Anchors + * between a layout edge and an item edge will have a size of 0. + * 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. + */ +void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, + Edge firstEdge, + QGraphicsLayoutItem *secondItem, + Edge secondEdge) +{ + Q_D(QGraphicsAnchorLayout); + d->anchor(firstItem, firstEdge, secondItem, secondEdge); + invalidate(); +} + +/*! + * \overload + * + * By calling this function the caller can specify the magnitude of the anchor with \a spacing. + * + */ void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, QGraphicsLayoutItem *secondItem, Edge secondEdge, qreal spacing) { Q_D(QGraphicsAnchorLayout); - if ((firstItem == 0) || (secondItem == 0)) { - qWarning("QGraphicsAnchorLayout::anchor: " - "Cannot anchor NULL items"); - return; - } + d->anchor(firstItem, firstEdge, secondItem, secondEdge, &spacing); + invalidate(); +} - if (firstItem == secondItem) { - qWarning("QGraphicsAnchorLayout::anchor: " - "Cannot anchor the item to itself"); - return; - } +/*! + * 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. + * + * This is a convenience function, since anchoring corners can be expressed as anchoring two edges. + * For instance, + * \code + * layout->anchor(layout, QGraphicsAnchorLayout::Top, b, QGraphicsAnchorLayout::Top); + * layout->anchor(layout, QGraphicsAnchorLayout::Left, b, QGraphicsAnchorLayout::Left); + * \endcode + * + * has the same effect as + * + * \code + * layout->anchor(layout, Qt::TopLeft, b, Qt::TopLeft); + * \endcode + * + * 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. + */ +void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, + Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, + Qt::Corner secondCorner) +{ + Q_D(QGraphicsAnchorLayout); - if (d->edgeOrientation(secondEdge) != d->edgeOrientation(firstEdge)) { - qWarning("QGraphicsAnchorLayout::anchor: " - "Cannot anchor edges of different orientations"); - return; - } + // Horizontal anchor + QGraphicsAnchorLayout::Edge firstEdge = (firstCorner & 1 ? QGraphicsAnchorLayout::Right: QGraphicsAnchorLayout::Left); + QGraphicsAnchorLayout::Edge secondEdge = (secondCorner & 1 ? QGraphicsAnchorLayout::Right: QGraphicsAnchorLayout::Left); + d->anchor(firstItem, firstEdge, secondItem, secondEdge); - // In QGraphicsAnchorLayout, items are represented in its internal - // graph as four anchors that connect: - // - Left -> HCenter - // - HCenter-> Right - // - Top -> VCenter - // - VCenter -> Bottom - - // Ensure that the internal anchors have been created for both items. - if (firstItem != this && !d->items.contains(firstItem)) { - d->createItemEdges(firstItem); - d->addChildLayoutItem(firstItem); - } - if (secondItem != this && !d->items.contains(secondItem)) { - d->createItemEdges(secondItem); - d->addChildLayoutItem(secondItem); - } + // Vertical anchor + firstEdge = (firstCorner & 2 ? QGraphicsAnchorLayout::Bottom: QGraphicsAnchorLayout::Top); + secondEdge = (secondCorner & 2 ? QGraphicsAnchorLayout::Bottom: QGraphicsAnchorLayout::Top); + d->anchor(firstItem, firstEdge, secondItem, secondEdge); - // Use heuristics to find out what the user meant with this anchor. - d->correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge); - - // Create actual anchor between firstItem and secondItem. - AnchorData *data; - if (spacing >= 0) { - data = new AnchorData(spacing); - d->addAnchor(firstItem, firstEdge, secondItem, secondEdge, data); - } else { - data = new AnchorData(-spacing); - d->addAnchor(secondItem, secondEdge, firstItem, firstEdge, data); - } + invalidate(); +} + +/*! + * \overload + * + * By calling this function the caller can specify the magnitude of the anchor with \a spacing. + * + */ +void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, + Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, + Qt::Corner secondCorner, qreal spacing) +{ + Q_D(QGraphicsAnchorLayout); + + // Horizontal anchor + QGraphicsAnchorLayout::Edge firstEdge = (firstCorner & 1 ? QGraphicsAnchorLayout::Right: QGraphicsAnchorLayout::Left); + QGraphicsAnchorLayout::Edge secondEdge = (secondCorner & 1 ? QGraphicsAnchorLayout::Right: QGraphicsAnchorLayout::Left); + d->anchor(firstItem, firstEdge, secondItem, secondEdge, &spacing); + + // Vertical anchor + firstEdge = (firstCorner & 2 ? QGraphicsAnchorLayout::Bottom: QGraphicsAnchorLayout::Top); + secondEdge = (secondCorner & 2 ? QGraphicsAnchorLayout::Bottom: QGraphicsAnchorLayout::Top); + d->anchor(firstItem, firstEdge, secondItem, secondEdge, &spacing); invalidate(); } @@ -167,8 +216,7 @@ void QGraphicsAnchorLayout::setSpacing(qreal spacing, Qt::Orientations orientati qreal QGraphicsAnchorLayout::spacing(Qt::Orientation orientation) const { Q_D(const QGraphicsAnchorLayout); - // Qt::Horizontal == 0x1, Qt::Vertical = 0x2 - return d->spacing[orientation - 1]; + return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Orientation(orientation - 1)); } void QGraphicsAnchorLayout::setGeometry(const QRectF &geom) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h index 74075ff..1f622eb 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -72,8 +72,18 @@ public: virtual ~QGraphicsAnchorLayout(); void anchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, + QGraphicsLayoutItem *secondItem, Edge secondEdge); + + void anchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, QGraphicsLayoutItem *secondItem, Edge secondEdge, - qreal spacing = 0); + qreal spacing); + + void anchor(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner); + + void anchor(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner, + qreal spacing); void removeAnchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, QGraphicsLayoutItem *secondItem, Edge secondEdge); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 502da5d..8b661e3 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -536,6 +536,75 @@ void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem * } } +/*! + * \internal + * + * Helper function that is called from the anchor functions in the public API. + * If \a spacing is 0, it will pick up the spacing defined by the style. + */ +void QGraphicsAnchorLayoutPrivate::anchor(QGraphicsLayoutItem *firstItem, + QGraphicsAnchorLayout::Edge firstEdge, + QGraphicsLayoutItem *secondItem, + QGraphicsAnchorLayout::Edge secondEdge, + qreal *spacing) +{ + Q_Q(QGraphicsAnchorLayout); + if ((firstItem == 0) || (secondItem == 0)) { + qWarning("QGraphicsAnchorLayout::anchor(): " + "Cannot anchor NULL items"); + return; + } + + if (firstItem == secondItem) { + qWarning("QGraphicsAnchorLayout::anchor(): " + "Cannot anchor the item to itself"); + return; + } + + if (edgeOrientation(secondEdge) != edgeOrientation(firstEdge)) { + qWarning("QGraphicsAnchorLayout::anchor(): " + "Cannot anchor edges of different orientations"); + return; + } + + // In QGraphicsAnchorLayout, items are represented in its internal + // graph as four anchors that connect: + // - Left -> HCenter + // - HCenter-> Right + // - Top -> VCenter + // - VCenter -> Bottom + + // Ensure that the internal anchors have been created for both items. + if (firstItem != q && !items.contains(firstItem)) { + createItemEdges(firstItem); + addChildLayoutItem(firstItem); + } + if (secondItem != q && !items.contains(secondItem)) { + createItemEdges(secondItem); + addChildLayoutItem(secondItem); + } + + // Use heuristics to find out what the user meant with this anchor. + correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge); + + AnchorData *data; + if (!spacing) { + // If we anchor to the layout edges or if we anchor + // Right->Right or Left->Left, our default spacing will be 0 + if (firstItem == q || secondItem == q || firstEdge == secondEdge) + data = new AnchorData(0); + else + data = new AnchorData; // otherwise, ask the style later + addAnchor(firstItem, firstEdge, secondItem, secondEdge, data); + } else if (*spacing >= 0) { + data = new AnchorData(*spacing); + addAnchor(firstItem, firstEdge, secondItem, secondEdge, data); + } else { + data = new AnchorData(-*spacing); + addAnchor(secondItem, secondEdge, firstItem, firstEdge, data); + } +} + void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, QGraphicsAnchorLayout::Edge firstEdge, QGraphicsLayoutItem *secondItem, @@ -697,6 +766,28 @@ void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&fi } } +qreal QGraphicsAnchorLayoutPrivate::effectiveSpacing(Orientation orientation) const +{ + Q_Q(const QGraphicsAnchorLayout); + qreal s = spacing[orientation]; + if (s < 0) { + QGraphicsLayoutItem *parent = q->parentLayoutItem(); + while (parent && parent->isLayout()) { + parent = parent->parentLayoutItem(); + } + if (parent) { + QGraphicsItem *parentItem = parent->graphicsItem(); + if (parentItem && parentItem->isWidget()) { + QGraphicsWidget *w = static_cast(parentItem); + s = w->style()->pixelMetric(orientation == Horizontal + ? QStyle::PM_LayoutHorizontalSpacing + : QStyle::PM_LayoutVerticalSpacing); + } + } + } + return s; +} + /*! \internal @@ -750,6 +841,9 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // restoreSimplifiedGraph(orientation); // should not be here //q->dumpGraph(); + // Reset the nominal sizes of each anchor based on the current item sizes + setAnchorSizeHintsFromDefaults(orientation); + // Traverse all graph edges and store the possible paths to each vertex findPaths(orientation); @@ -850,6 +944,33 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( graphPaths[orientation].clear(); // ### } +void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromDefaults(Orientation orientation) +{ + Graph &g = graph[orientation]; + QSet setOfVertices = g.vertices(); + + for (QSet::const_iterator it = setOfVertices.begin(); it != setOfVertices.end(); ++it) { + AnchorVertex *v = *it; + QList adjacents = g.adjacentVertices(v); + for (int i = 0; i < adjacents.count(); ++i) { + AnchorVertex *v1 = adjacents.at(i); + AnchorData *data = g.edgeData(v, v1); + if (!data->hasSize) { + bool forward = data->origin == v; + if (forward) { + qreal s = effectiveSpacing(orientation); + data->minSize = s; + data->prefSize = s; + data->maxSize = s; + data->sizeAtMinimum = s; + data->sizeAtPreferred = s; + data->sizeAtMaximum = s; + } + } + } + } +} + void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orientation) { QPair beginningKey; diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 605a8e2..9e6c1bc 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -133,12 +133,17 @@ struct AnchorData : public QSimplexVariable { : QSimplexVariable(), minSize(minimumSize), prefSize(preferredSize), maxSize(maximumSize), sizeAtMinimum(preferredSize), sizeAtPreferred(preferredSize), sizeAtMaximum(preferredSize), - skipInPreferred(0), type(Normal) {} + skipInPreferred(0), type(Normal), hasSize(true) {} - AnchorData(qreal size = 0) + AnchorData(qreal size) : QSimplexVariable(), minSize(size), prefSize(size), maxSize(size), sizeAtMinimum(size), sizeAtPreferred(size), sizeAtMaximum(size), - skipInPreferred(0), type(Normal) {} + skipInPreferred(0), type(Normal), hasSize(true) {} + + AnchorData() + : QSimplexVariable(), minSize(0), prefSize(0), maxSize(0), + sizeAtMinimum(0), sizeAtPreferred(0), sizeAtMaximum(0), + skipInPreferred(0), type(Normal), hasSize(false) {} inline QString toString() const; QString name; @@ -163,12 +168,13 @@ struct AnchorData : public QSimplexVariable { uint skipInPreferred : 1; uint type : 2; // either Normal, Sequential or Parallel + uint hasSize : 1; // if false, get size from style. protected: AnchorData(Type type, qreal size = 0) : QSimplexVariable(), minSize(size), prefSize(size), maxSize(size), sizeAtMinimum(size), sizeAtPreferred(size), sizeAtMaximum(size), - skipInPreferred(0), type(type) {} + skipInPreferred(0), type(type), hasSize(true) {} }; inline QString AnchorData::toString() const @@ -288,6 +294,13 @@ public: void createItemEdges(QGraphicsLayoutItem *item); void removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation); + // helper function used by the 4 API functions + void anchor(QGraphicsLayoutItem *firstItem, + QGraphicsAnchorLayout::Edge firstEdge, + QGraphicsLayoutItem *secondItem, + QGraphicsAnchorLayout::Edge secondEdge, + qreal *spacing = 0); + // Anchor Manipulation methods void addAnchor(QGraphicsLayoutItem *firstItem, QGraphicsAnchorLayout::Edge firstEdge, @@ -306,6 +319,9 @@ public: QGraphicsAnchorLayout::Edge &firstEdge, QGraphicsLayoutItem *&secondItem, QGraphicsAnchorLayout::Edge &secondEdge); + // for getting the actual spacing (will query the style if the + // spacing is not explicitly set). + qreal effectiveSpacing(Orientation orientation) const; // Activation methods void simplifyGraph(Orientation orientation); @@ -313,6 +329,7 @@ public: void calculateGraphs(); void calculateGraphs(Orientation orientation); void setAnchorSizeHintsFromItems(Orientation orientation); + void setAnchorSizeHintsFromDefaults(Orientation orientation); void findPaths(Orientation orientation); void constraintsFromPaths(Orientation orientation); QList constraintsFromSizeHints(const QList &anchors); diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index de6caac..10b55d7 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -2,6 +2,7 @@ #include #include #include +#include class tst_QGraphicsAnchorLayout : public QObject { Q_OBJECT; @@ -14,11 +15,26 @@ private slots: void fairDistribution(); }; +class RectWidget : public QGraphicsWidget +{ +public: + RectWidget(QGraphicsItem *parent = 0) : QGraphicsWidget(parent){} + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) + { + Q_UNUSED(option); + Q_UNUSED(widget); + painter->drawRoundRect(rect()); + painter->drawLine(rect().topLeft(), rect().bottomRight()); + painter->drawLine(rect().bottomLeft(), rect().topRight()); + } +}; + static QGraphicsWidget *createItem(const QSizeF &minimum = QSizeF(100.0, 100.0), const QSizeF &preferred = QSize(150.0, 100.0), const QSizeF &maximum = QSizeF(200.0, 100.0)) { - QGraphicsWidget *w = new QGraphicsWidget; + QGraphicsWidget *w = new RectWidget; w->setMinimumSize(minimum); w->setPreferredSize(preferred); w->setMaximumSize(maximum); @@ -56,28 +72,28 @@ void tst_QGraphicsAnchorLayout::diagonal() l->setContentsMargins(0, 0, 0, 0); // vertical - l->anchor(a, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top); - l->anchor(b, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top); + l->anchor(a, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top, 0); + l->anchor(b, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Bottom); - l->anchor(c, QGraphicsAnchorLayout::Top, b, QGraphicsAnchorLayout::Bottom); - l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top); - l->anchor(c, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top); + l->anchor(c, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Bottom, 0); + l->anchor(c, QGraphicsAnchorLayout::Top, b, QGraphicsAnchorLayout::Bottom, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top, 0); - l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); - l->anchor(e, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + l->anchor(e, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); // horizontal - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left); - l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left); + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); + l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left, 0); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left, 0); - l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left); - l->anchor(c, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left); + l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left, 0); + l->anchor(c, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); - l->anchor(b, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); - l->anchor(e, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); - l->anchor(d, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left); + l->anchor(b, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->anchor(e, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->anchor(d, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); QCOMPARE(l->count(), 5); @@ -155,23 +171,23 @@ void tst_QGraphicsAnchorLayout::parallel() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); - l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top); - l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top); - l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top); - l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top); - l->anchor(d, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top); - l->anchor(e, QGraphicsAnchorLayout::Bottom, f, QGraphicsAnchorLayout::Top); - l->anchor(f, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); - - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left); - l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left); - l->anchor(b, QGraphicsAnchorLayout::Right, d, QGraphicsAnchorLayout::Left); - l->anchor(b, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left); - l->anchor(c, QGraphicsAnchorLayout::Right, f, QGraphicsAnchorLayout::Left); - l->anchor(d, QGraphicsAnchorLayout::Right, f, QGraphicsAnchorLayout::Left); - l->anchor(e, QGraphicsAnchorLayout::Right, f, QGraphicsAnchorLayout::Left); - l->anchor(f, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); + l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); + l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); + l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); + l->anchor(d, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top, 0); + l->anchor(e, QGraphicsAnchorLayout::Bottom, f, QGraphicsAnchorLayout::Top, 0); + l->anchor(f, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left, 0); + l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left, 0); + l->anchor(b, QGraphicsAnchorLayout::Right, d, QGraphicsAnchorLayout::Left, 0); + l->anchor(b, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); + l->anchor(c, QGraphicsAnchorLayout::Right, f, QGraphicsAnchorLayout::Left, 0); + l->anchor(d, QGraphicsAnchorLayout::Right, f, QGraphicsAnchorLayout::Left, 0); + l->anchor(e, QGraphicsAnchorLayout::Right, f, QGraphicsAnchorLayout::Left, 0); + l->anchor(f, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); QCOMPARE(l->count(), 6); @@ -234,15 +250,15 @@ void tst_QGraphicsAnchorLayout::snake() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); - l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top); - l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top); - l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top); - l->anchor(c, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); + l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); + l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Right); - l->anchor(b, QGraphicsAnchorLayout::Left, c, QGraphicsAnchorLayout::Left); - l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Right, 0); + l->anchor(b, QGraphicsAnchorLayout::Left, c, QGraphicsAnchorLayout::Left, 0); + l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); QCOMPARE(l->count(), 3); @@ -298,18 +314,18 @@ void tst_QGraphicsAnchorLayout::fairDistribution() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); - l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top); - l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top); - l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top); - l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top); - l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); - - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left); - l->anchor(b, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left); - l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); - l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left); - l->anchor(d, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); + l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); + l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); + l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); + l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left, 0); + l->anchor(b, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left, 0); + l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left, 0); + l->anchor(d, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); QCOMPARE(l->count(), 4); -- cgit v0.12 From 1ead82df833086eede2e2959705e1d9e8c15c389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 1 Jul 2009 14:43:26 +0200 Subject: Avoid some warnings (unused variables) --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 8b661e3..5963d16 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -274,8 +274,6 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O bool forward; AnchorVertex *prev = beforeSequence; int intervalFrom = 0; - int intervalTo = candidates.count() + 1; - bool forwardSet = false; // Check for directionality (origin). We don't want to destroy that information, // thus we only combine anchors with the same direction. -- cgit v0.12 From a2a3b7e1db732f24fba46f779f525d3624879676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 1 Jul 2009 14:47:02 +0200 Subject: The simplification makes the parallel autotest fail. Disable it for now. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 5963d16..0bf974f 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -798,11 +798,11 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs() if (!calculateGraphCacheDirty) return; - simplifyGraph(Horizontal); - simplifyGraph(Vertical); + //simplifyGraph(Horizontal); + //simplifyGraph(Vertical); //q->dumpGraph(); - restoreSimplifiedGraph(Horizontal); // should not be here, but currently crashes if not - restoreSimplifiedGraph(Vertical); // should not be here, but currently crashes if not + //restoreSimplifiedGraph(Horizontal); // should not be here, but currently crashes if not + //restoreSimplifiedGraph(Vertical); // should not be here, but currently crashes if not calculateGraphs(Horizontal); -- cgit v0.12 From 6f4317a9a5f304a07b522de302a6d293f60e6802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 1 Jul 2009 15:03:43 +0200 Subject: Fix typo: feaseable => feasible --- src/gui/graphicsview/qsimplex_p.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/graphicsview/qsimplex_p.cpp b/src/gui/graphicsview/qsimplex_p.cpp index b90d53f..1349ced 100644 --- a/src/gui/graphicsview/qsimplex_p.cpp +++ b/src/gui/graphicsview/qsimplex_p.cpp @@ -154,7 +154,7 @@ void QSimplex::setConstraints(const QList newConstraints) solveMaxHelper(); if (valueAt(0, columns - 1) != 0.0) { - qWarning() << "QSimplex: No feaseable solution!"; + qWarning() << "QSimplex: No feasible solution!"; clearDataStructures(); return; } -- cgit v0.12 From 448a082899857f515f4040fccebaf0918ed19f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 1 Jul 2009 16:26:28 +0200 Subject: Explicitly use a spacing of 0 in order to keep old behaviour. This broke after commit 0872f78d8924b9bea8b8da6618e24cd7d06c5d7d that fixed the default spacings. --- examples/graphicsview/anchorlayout/main.cpp | 46 ++++++++++++++--------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/examples/graphicsview/anchorlayout/main.cpp b/examples/graphicsview/anchorlayout/main.cpp index 1436aca..5e383fa 100644 --- a/examples/graphicsview/anchorlayout/main.cpp +++ b/examples/graphicsview/anchorlayout/main.cpp @@ -84,37 +84,37 @@ int main(int argc, char **argv) w->setLayout(l); // vertical - l->anchor(a, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top); - l->anchor(b, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top); + l->anchor(a, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top, 0); + l->anchor(b, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Bottom); - l->anchor(c, QGraphicsAnchorLayout::Top, b, QGraphicsAnchorLayout::Bottom); - l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top); - l->anchor(c, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top); + l->anchor(c, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Bottom, 0); + l->anchor(c, QGraphicsAnchorLayout::Top, b, QGraphicsAnchorLayout::Bottom, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top, 0); - l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); - l->anchor(e, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + l->anchor(e, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(c, QGraphicsAnchorLayout::Top, f, QGraphicsAnchorLayout::Top); - l->anchor(c, QGraphicsAnchorLayout::VCenter, f, QGraphicsAnchorLayout::Bottom); - l->anchor(f, QGraphicsAnchorLayout::Bottom, g, QGraphicsAnchorLayout::Top); - l->anchor(c, QGraphicsAnchorLayout::Bottom, g, QGraphicsAnchorLayout::Bottom); + l->anchor(c, QGraphicsAnchorLayout::Top, f, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::VCenter, f, QGraphicsAnchorLayout::Bottom, 0); + l->anchor(f, QGraphicsAnchorLayout::Bottom, g, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, g, QGraphicsAnchorLayout::Bottom, 0); // horizontal - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left); - l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left); + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); + l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left, 0); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left, 0); - l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left); - l->anchor(c, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left); + l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left, 0); + l->anchor(c, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); - l->anchor(b, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); - l->anchor(e, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); - l->anchor(d, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left); + l->anchor(b, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->anchor(e, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->anchor(d, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); - l->anchor(l, QGraphicsAnchorLayout::Left, f, QGraphicsAnchorLayout::Left); - l->anchor(l, QGraphicsAnchorLayout::Left, g, QGraphicsAnchorLayout::Left); - l->anchor(f, QGraphicsAnchorLayout::Right, g, QGraphicsAnchorLayout::Right); + l->anchor(l, QGraphicsAnchorLayout::Left, f, QGraphicsAnchorLayout::Left, 0); + l->anchor(l, QGraphicsAnchorLayout::Left, g, QGraphicsAnchorLayout::Left, 0); + l->anchor(f, QGraphicsAnchorLayout::Right, g, QGraphicsAnchorLayout::Right, 0); #ifdef BENCHMARK_RUN CALLGRIND_START_INSTRUMENTATION; -- cgit v0.12 From 4a03627703af9119e02024239cfdb464c5013396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 22 Jul 2009 09:26:28 +0200 Subject: Can now specify which layout to load on the command line. --- examples/layouts/anchorlayout/main.cpp | 4 ++-- examples/layouts/anchorlayout/window.cpp | 8 +++++++- examples/layouts/anchorlayout/window.h | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/layouts/anchorlayout/main.cpp b/examples/layouts/anchorlayout/main.cpp index d0e6ee7..716a563 100644 --- a/examples/layouts/anchorlayout/main.cpp +++ b/examples/layouts/anchorlayout/main.cpp @@ -4,7 +4,7 @@ int main(int argc, char **argv) { QApplication app(argc, argv); - Window *w = new Window; + Window *w = new Window(app.arguments()); w->show(); return app.exec(); -} \ No newline at end of file +} diff --git a/examples/layouts/anchorlayout/window.cpp b/examples/layouts/anchorlayout/window.cpp index 32efa69..d8d68e2 100644 --- a/examples/layouts/anchorlayout/window.cpp +++ b/examples/layouts/anchorlayout/window.cpp @@ -21,7 +21,7 @@ static QString nodeName(QGraphicsLayoutItem *item) #define _QUOTEMACRO(x) #x #define QUOTEMACRO(x) _QUOTEMACRO(x) -Window::Window(QWidget *parent) +Window::Window(const QStringList &arguments, QWidget *parent) : QMainWindow(parent), m_inAddAnchor(false) { m_ui.setupUi(this); @@ -41,6 +41,12 @@ Window::Window(QWidget *parent) WidgetChooserDelegate *delegate = new WidgetChooserDelegate(&m_layoutItems, m_layout, m_ui.anchors); m_ui.anchors->setItemDelegate(delegate); + if (arguments.count() >= 2) { + QString fileName = QString::fromAscii("%1/xml/%2.xml").arg(QUOTEMACRO(PRO_FILE_PWD), arguments.at(1)); + if (!loadLayout(fileName, m_layout)) { + QMessageBox::warning(this, tr("Not found"), tr("Could not find %1").arg(fileName)); + } + } } void Window::findLayoutFiles() diff --git a/examples/layouts/anchorlayout/window.h b/examples/layouts/anchorlayout/window.h index 911cc84..c81dbac 100644 --- a/examples/layouts/anchorlayout/window.h +++ b/examples/layouts/anchorlayout/window.h @@ -11,7 +11,7 @@ class Window : public QMainWindow { Q_OBJECT public: - Window(QWidget *parent = 0); + Window(const QStringList &arguments, QWidget *parent = 0); private slots: void on_anchors_cellChanged(int row, int column); -- cgit v0.12 From 8bb931a6bff959279189662299ad6f8516de1789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 22 Jul 2009 10:25:26 +0200 Subject: Make sure that internal center anchors are not simplified. --- examples/layouts/anchorlayout/xml/center.xml | 57 ++++++++++++++++++++++++ src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 21 ++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 examples/layouts/anchorlayout/xml/center.xml diff --git a/examples/layouts/anchorlayout/xml/center.xml b/examples/layouts/anchorlayout/xml/center.xml new file mode 100644 index 0000000..cf857fc --- /dev/null +++ b/examples/layouts/anchorlayout/xml/center.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 0bf974f..9023344 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -220,7 +220,6 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O Q_Q(QGraphicsAnchorLayout); Graph &g = graph[orientation]; AnchorVertex *v = g.rootVertex(); - QGraphicsAnchorLayout::Edge layoutEdge = oppositeEdge(v->m_edge); if (!v) return; @@ -229,6 +228,9 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O stack.push(v); QVector candidates; + const QGraphicsAnchorLayout::Edge centerEdge = pickEdge(QGraphicsAnchorLayout::HCenter, orientation); + const QGraphicsAnchorLayout::Edge layoutEdge = oppositeEdge(v->m_edge); + // walk depth-first. while (!stack.isEmpty()) { v = stack.pop(); @@ -295,8 +297,23 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O // after the sequential anchor. if (intervalTo - intervalFrom >= 2) { // simplify in the range [intervalFrom, intervalTo] + + // Trim off internal center anchors (Left-Center/Center-Right) from the + // start and the end of the sequence. We never want to simplify internal + // center anchors where there is an external anchor connected to the center. AnchorVertex *intervalVertexFrom = intervalFrom == 0 ? beforeSequence : candidates.at(intervalFrom - 1); + if (intervalVertexFrom->m_edge == centerEdge + && intervalVertexFrom->m_item == candidates.at(intervalFrom)->m_item) { + ++intervalFrom; + intervalVertexFrom = candidates.at(intervalFrom - 1); + } AnchorVertex *intervalVertexTo = intervalTo <= candidates.count() ? candidates.at(intervalTo - 1) : afterSequence; + if (intervalVertexTo->m_edge == centerEdge + && intervalVertexTo->m_item == candidates.at(intervalTo - 2)->m_item) { + --intervalTo; + intervalVertexTo = candidates.at(intervalTo - 1); + } + QVector subCandidates; if (forward) { subCandidates = candidates.mid(intervalFrom, intervalTo - intervalFrom - 1); @@ -323,7 +340,6 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O if (endOfSequence) candidates.clear(); - QGraphicsAnchorLayout::Edge centerEdge = pickEdge(QGraphicsAnchorLayout::HCenter, orientation); for (int i = 0; i < count; ++i) { AnchorVertex *next = vertices.at(i); if (next->m_item == q && next->m_edge == centerEdge) @@ -800,6 +816,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs() //simplifyGraph(Horizontal); //simplifyGraph(Vertical); + //Q_Q(QGraphicsAnchorLayout); //q->dumpGraph(); //restoreSimplifiedGraph(Horizontal); // should not be here, but currently crashes if not //restoreSimplifiedGraph(Vertical); // should not be here, but currently crashes if not -- cgit v0.12 From 4a5b176b5bd302903a7baf1517e7cb9dced70d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 22 Jul 2009 10:37:13 +0200 Subject: Anchors to center edges should also have a default spacing of 0. Since the spacing will be 0 in most cases, we therefore "invert" the if (..) to only check for the cases where the spacing will *not* be zero. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 9023344..e969e40 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -603,12 +603,22 @@ void QGraphicsAnchorLayoutPrivate::anchor(QGraphicsLayoutItem *firstItem, AnchorData *data; if (!spacing) { - // If we anchor to the layout edges or if we anchor - // Right->Right or Left->Left, our default spacing will be 0 - if (firstItem == q || secondItem == q || firstEdge == secondEdge) - data = new AnchorData(0); - else - data = new AnchorData; // otherwise, ask the style later + // If firstItem or secondItem is the layout itself, the spacing will default to 0. + // Otherwise, the following matrix is used (questionmark means that the spacing + // is queried from the style): + // from + // to Left HCenter Right + // Left 0 0 ? + // HCenter 0 0 0 + // Right ? 0 0 + if (firstItem != q + && secondItem != q + && pickEdge(firstEdge, Horizontal) != QGraphicsAnchorLayout::HCenter + && oppositeEdge(firstEdge) == secondEdge) { + data = new AnchorData; // ask the style later + } else { + data = new AnchorData(0); // spacing should be 0 + } addAnchor(firstItem, firstEdge, secondItem, secondEdge, data); } else if (*spacing >= 0) { data = new AnchorData(*spacing); -- cgit v0.12 From 9438460039f077f67afb398b0623992fe6a6f0e2 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Tue, 14 Jul 2009 17:53:52 -0300 Subject: QGraphicsAnchorLayout: Use Q_ASSERT instead of "if" tests Replacing two IF tests for Q_ASSERT statements instead. These tests are sanity checks that should never be false. Signed-off-by: Eduardo M. Fleury Reviewed-by: Anselmo Lacerda S. de Melo --- src/gui/graphicsview/qgraph_p.h | 14 +++++++------- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h index 9128df8..053114b 100644 --- a/src/gui/graphicsview/qgraph_p.h +++ b/src/gui/graphicsview/qgraph_p.h @@ -174,13 +174,13 @@ protected: void removeDirectedEdge(Vertex *from, Vertex *to) { QHash *adjacentToFirst = m_graph.value(from); - if (adjacentToFirst) { - adjacentToFirst->remove(to); - if (adjacentToFirst->isEmpty()) { - //nobody point to 'from' so we can remove it from the graph - m_graph.remove(from); - delete adjacentToFirst; - } + Q_ASSERT(adjacentToFirst); + + adjacentToFirst->remove(to); + if (adjacentToFirst->isEmpty()) { + //nobody point to 'from' so we can remove it from the graph + m_graph.remove(from); + delete adjacentToFirst; } } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index e969e40..c83f939 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -701,10 +701,10 @@ void QGraphicsAnchorLayoutPrivate::removeAnchor(QGraphicsLayoutItem *firstItem, AnchorVertex *v1 = internalVertex(firstItem, firstEdge); AnchorVertex *v2 = internalVertex(secondItem, secondEdge); + Q_ASSERT(v1 && v2); + // Remove edge from graph - if (v1 && v2) { - graph[edgeOrientation(firstEdge)].removeEdge(v1, v2); - } + graph[edgeOrientation(firstEdge)].removeEdge(v1, v2); // Decrease vertices reference count (may trigger a deletion) removeInternalVertex(firstItem, firstEdge); -- cgit v0.12 From ded06db0f1289ab0dbb2bd546cb05cc5ab354be8 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Tue, 14 Jul 2009 17:56:28 -0300 Subject: QGraphicsAnchorLayout: Initialize spacing variables To avoid non-deterministic behavior. Signed-off-by: Eduardo M. Fleury Reviewed-by: Anselmo Lacerda S. de Melo --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index c83f939..63179ef 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -88,6 +88,8 @@ QString GraphPath::toString() const QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate() : calculateGraphCacheDirty(1) { + for (int i = 0; i < NOrientations; ++i) + spacing[i] = -1; } QGraphicsAnchorLayout::Edge QGraphicsAnchorLayoutPrivate::oppositeEdge( -- cgit v0.12 From 4419654149be34e646fdff4c4f2897292a129d33 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Tue, 14 Jul 2009 17:58:09 -0300 Subject: QGraphicsAnchorLayout: Testing for existing anchors before adding new ones The layout was not aware of the fact an anchor could already exist when it was added. This commit ensures that only one anchor exists between two vertices to avoid unwanted behavior and memory leaks (AnchorData's and Vertices would leak otherwise). Signed-off-by: Eduardo M. Fleury Reviewed-by: Anselmo Lacerda S. de Melo --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 63179ef..9d145a2 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -642,6 +642,11 @@ void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, AnchorVertex *v1 = addInternalVertex(firstItem, firstEdge); AnchorVertex *v2 = addInternalVertex(secondItem, secondEdge); + // Remove previous anchor + // ### Could we update the existing edgeData rather than creating a new one? + if (graph[edgeOrientation(firstEdge)].edgeData(v1, v2)) + removeAnchor(firstItem, firstEdge, secondItem, secondEdge); + // 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. -- cgit v0.12 From 22a6ce9b329a081d07296533980d374b0c7ad88b Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 9 Jul 2009 15:12:29 -0300 Subject: QGraphicsAnchorLayout: Do not create center anchors by default Previously we would create two half anchors internally to each item added to the layout. These anchors are meant to enforce the central anchorage point remains on its position. It is not effecient however, to create these anchors for all items if not all of them will have anchors linked to their center. This commit removes the creation of center anchors, the idea is to postpone this to the moment their are really needed, therefore reducing the number of variables and constraints sent to the simplex solver on the average case. Signed-off-by: Eduardo M. Fleury Reviewed-by: Anselmo Lacerda S. de Melo --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 36 ++++++------------------ 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 9d145a2..1564163 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -487,42 +487,22 @@ void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) items.append(item); // Horizontal - int minimumSize = item->minimumWidth() / 2; - int preferredSize = item->preferredWidth() / 2; - int maximumSize = item->maximumWidth() / 2; - - QSimplexConstraint *c = new QSimplexConstraint; + int minimumSize = item->minimumWidth(); + int preferredSize = item->preferredWidth(); + int maximumSize = item->maximumWidth(); AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); addAnchor(item, QGraphicsAnchorLayout::Left, item, - QGraphicsAnchorLayout::HCenter, data); - c->variables.insert(data, 1.0); - - data = new AnchorData(minimumSize, preferredSize, maximumSize); - addAnchor(item, QGraphicsAnchorLayout::HCenter, - item, QGraphicsAnchorLayout::Right, data); - c->variables.insert(data, -1.0); - - itemCenterConstraints[Horizontal].append(c); + QGraphicsAnchorLayout::Right, data); // Vertical - minimumSize = item->minimumHeight() / 2; - preferredSize = item->preferredHeight() / 2; - maximumSize = item->maximumHeight() / 2; - - c = new QSimplexConstraint; + minimumSize = item->minimumHeight(); + preferredSize = item->preferredHeight(); + maximumSize = item->maximumHeight(); data = new AnchorData(minimumSize, preferredSize, maximumSize); addAnchor(item, QGraphicsAnchorLayout::Top, item, - QGraphicsAnchorLayout::VCenter, data); - c->variables.insert(data, 1.0); - - data = new AnchorData(minimumSize, preferredSize, maximumSize); - addAnchor(item, QGraphicsAnchorLayout::VCenter, - item, QGraphicsAnchorLayout::Bottom, data); - c->variables.insert(data, -1.0); - - itemCenterConstraints[Vertical].append(c); + QGraphicsAnchorLayout::Bottom, data); } void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem *item, -- cgit v0.12 From 7bf639f5484bb5fe34cab53ab1dfde048e158995 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 9 Jul 2009 16:42:58 -0300 Subject: QGraphicsAnchorLayout: Create center edges on demand Since the previous commit, center anchors are no longer created when an item is added to the layout. This commit creates only the required anchors, when needed. Signed-off-by: Eduardo M. Fleury Reviewed-by: Anselmo Lacerda S. de Melo --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 62 ++++++++++++++++++++++++ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 1 + 2 files changed, 63 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 1564163..8a37cad 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -505,6 +505,64 @@ void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) QGraphicsAnchorLayout::Bottom, data); } +void QGraphicsAnchorLayoutPrivate::createCenterAnchors( + QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge) +{ + Orientation orientation; + switch (centerEdge) { + case QGraphicsAnchorLayout::HCenter: + orientation = Horizontal; + break; + case QGraphicsAnchorLayout::VCenter: + orientation = Vertical; + break; + default: + // Don't create center edges unless needed + return; + } + + // Check if vertex already exists + if (internalVertex(item, centerEdge)) + return; + + // Orientation code + QGraphicsAnchorLayout::Edge firstEdge; + QGraphicsAnchorLayout::Edge lastEdge; + + if (orientation == Horizontal) { + firstEdge = QGraphicsAnchorLayout::Left; + lastEdge = QGraphicsAnchorLayout::Right; + } else { + firstEdge = QGraphicsAnchorLayout::Top; + lastEdge = QGraphicsAnchorLayout::Bottom; + } + + AnchorVertex *first = internalVertex(item, firstEdge); + AnchorVertex *last = internalVertex(item, lastEdge); + Q_ASSERT(first && last); + + // Create new anchors + AnchorData *oldData = graph[orientation].edgeData(first, last); + + int minimumSize = oldData->minSize / 2; + int preferredSize = oldData->prefSize / 2; + int maximumSize = oldData->maxSize / 2; + + QSimplexConstraint *c = new QSimplexConstraint; + AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); + c->variables.insert(data, 1.0); + addAnchor(item, firstEdge, item, centerEdge, data); + + data = new AnchorData(minimumSize, preferredSize, maximumSize); + c->variables.insert(data, -1.0); + addAnchor(item, centerEdge, item, lastEdge, data); + + itemCenterConstraints[orientation].append(c); + + // Remove old one + removeAnchor(item, firstEdge, item, lastEdge); +} + void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation) { @@ -580,6 +638,10 @@ void QGraphicsAnchorLayoutPrivate::anchor(QGraphicsLayoutItem *firstItem, addChildLayoutItem(secondItem); } + // Create center edges if needed + createCenterAnchors(firstItem, firstEdge); + createCenterAnchors(secondItem, secondEdge); + // Use heuristics to find out what the user meant with this anchor. correctEdgeDirection(firstItem, firstEdge, secondItem, secondEdge); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 9e6c1bc..d036201 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -292,6 +292,7 @@ public: void createLayoutEdges(); void deleteLayoutEdges(); void createItemEdges(QGraphicsLayoutItem *item); + void createCenterAnchors(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge); void removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation); // helper function used by the 4 API functions -- cgit v0.12 From a52114c8ca9020773081c20a1094c078cfdb81fe Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Tue, 14 Jul 2009 18:40:35 -0300 Subject: QGraphicsAnchorLayout: Remove center anchors when not needed Continuing the effort to enforcing center anchors to exist only when needed, this commit removes the central anchors when they are no longer used. Signed-off-by: Eduardo M. Fleury Reviewed-by: Anselmo Lacerda S. de Melo --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 2 + src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 71 +++++++++++++++++++++++- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 1 + 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 31cc911..fe335e8 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -200,6 +200,8 @@ void QGraphicsAnchorLayout::removeAnchor(QGraphicsLayoutItem *firstItem, Edge fi } d->removeAnchor(firstItem, firstEdge, secondItem, secondEdge); + d->removeCenterAnchors(firstItem, firstEdge); + d->removeCenterAnchors(secondItem, secondEdge); invalidate(); } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 8a37cad..9413ec3 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -563,6 +563,67 @@ void QGraphicsAnchorLayoutPrivate::createCenterAnchors( removeAnchor(item, firstEdge, item, lastEdge); } +void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( + QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge) +{ + Orientation orientation; + switch (centerEdge) { + case QGraphicsAnchorLayout::HCenter: + orientation = Horizontal; + break; + case QGraphicsAnchorLayout::VCenter: + orientation = Vertical; + break; + default: + // Don't remove edges that not the center ones + return; + } + + // Check if vertex is used by other than the internal anchors + AnchorVertex *center = internalVertex(item, centerEdge); + if (graph[orientation].adjacentVertices(center).count() > 2) + return; + + // Orientation code + QGraphicsAnchorLayout::Edge firstEdge; + QGraphicsAnchorLayout::Edge lastEdge; + + if (orientation == Horizontal) { + firstEdge = QGraphicsAnchorLayout::Left; + lastEdge = QGraphicsAnchorLayout::Right; + } else { + firstEdge = QGraphicsAnchorLayout::Top; + lastEdge = QGraphicsAnchorLayout::Bottom; + } + + AnchorVertex *first = internalVertex(item, firstEdge); + AnchorVertex *last = internalVertex(item, lastEdge); + Q_ASSERT(first && last); + + // Create new anchor + AnchorData *oldData = graph[orientation].edgeData(first, center); + + int minimumSize = oldData->minSize * 2; + int preferredSize = oldData->prefSize * 2; + int maximumSize = oldData->maxSize * 2; + + AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); + addAnchor(item, firstEdge, item, lastEdge, data); + + // Remove center constraint + for (int i = itemCenterConstraints[orientation].count() - 1; i >= 0; --i) { + if (itemCenterConstraints[orientation][i]->variables.contains(oldData)) { + delete itemCenterConstraints[orientation].takeAt(i); + break; + } + } + + // Remove old anchors + removeAnchor(item, firstEdge, item, centerEdge); + removeAnchor(item, centerEdge, item, lastEdge); +} + + void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation) { @@ -574,12 +635,16 @@ void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem * AnchorVertex *first = internalVertex(item, orientation == Horizontal ? QGraphicsAnchorLayout::Left : QGraphicsAnchorLayout::Top); - AnchorVertex *second = internalVertex(item, orientation == Horizontal ? + AnchorVertex *center = internalVertex(item, orientation == Horizontal ? QGraphicsAnchorLayout::HCenter : QGraphicsAnchorLayout::VCenter); - Q_ASSERT(first && second); - AnchorData *internalAnchor = graph[orientation].edgeData(first, second); + // Skip if no center constraints exist + if (!center) + return; + + Q_ASSERT(first); + AnchorData *internalAnchor = graph[orientation].edgeData(first, center); // Look for our anchor in all item center constraints, then remove it for (int i = 0; i < itemCenterConstraints[orientation].size(); ++i) { diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index d036201..2f1aa97 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -293,6 +293,7 @@ public: void deleteLayoutEdges(); void createItemEdges(QGraphicsLayoutItem *item); void createCenterAnchors(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge); + void removeCenterAnchors(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge); void removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation); // helper function used by the 4 API functions -- cgit v0.12 From 9e701f76f7478ea078e58076c91fee0d2ec505b4 Mon Sep 17 00:00:00 2001 From: Jesus Sanchez-Palencia Date: Mon, 29 Jun 2009 15:07:17 -0300 Subject: QGraphicsAnchorLayoutPrivate: Avoiding extra loops in getGraphParts Signed-off-by: Jesus Sanchez-Palencia --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 9413ec3..26207e1 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1327,12 +1327,14 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) } QList trunkConstraints; + QList nonTrunkConstraints; QSet trunkVariables; trunkVariables += edgeL1; trunkVariables += edgeL2; bool dirty; + bool hasNonTrunkConstraints = false; do { dirty = false; @@ -1358,7 +1360,9 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) it = remainingConstraints.erase(it); dirty = true; } else { - ++it; + nonTrunkConstraints += c; + it = remainingConstraints.erase(it); + hasNonTrunkConstraints = true; } } } while (dirty); @@ -1366,15 +1370,8 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) QList< QList > result; result += trunkConstraints; - if (!remainingConstraints.isEmpty()) { - QList nonTrunkConstraints; - QLinkedList::iterator it = remainingConstraints.begin(); - while (it != remainingConstraints.end()) { - nonTrunkConstraints += *it; - ++it; - } + if (hasNonTrunkConstraints) result += nonTrunkConstraints; - } return result; } -- cgit v0.12 From aa05ec20ee0961f129eb66527892360c17dc3a18 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 23 Jul 2009 19:03:14 -0300 Subject: QGraphicsAnchorLayout: Do not create center anchors in the layout Using same logic as for the items. We now create the center anchors on demand and delete them when not needed anymore. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 106 ++++++++++++++--------- 1 file changed, 63 insertions(+), 43 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 26207e1..7516c06 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -420,11 +420,11 @@ QGraphicsAnchorLayoutPrivate::edgeOrientation(QGraphicsAnchorLayout::Edge edge) /*! \internal - Create two internal anchors to connect the layout edges and its - central anchorage point. - These anchors doesn't have size restrictions other than the fact they - should always have the same size, which is something we enforce later, - when creating restrictions for the Simplex solver. + Create internal anchors to connect the layout edges (Left to Right and + Top to Bottom). + + These anchors doesn't have size restrictions, that will be enforced by + other anchors and items in the layout. */ void QGraphicsAnchorLayoutPrivate::createLayoutEdges() { @@ -432,40 +432,20 @@ void QGraphicsAnchorLayoutPrivate::createLayoutEdges() QGraphicsLayoutItem *layout = q; // Horizontal - QSimplexConstraint *c = new QSimplexConstraint; AnchorData *data = new AnchorData(0, 0, QWIDGETSIZE_MAX); addAnchor(layout, QGraphicsAnchorLayout::Left, layout, - QGraphicsAnchorLayout::HCenter, data); - data->skipInPreferred = 1; - c->variables.insert(data, 1.0); - - data = new AnchorData(0, 0, QWIDGETSIZE_MAX); - addAnchor(layout, QGraphicsAnchorLayout::HCenter, - layout, QGraphicsAnchorLayout::Right, data); + QGraphicsAnchorLayout::Right, data); data->skipInPreferred = 1; - c->variables.insert(data, -1.0); - - itemCenterConstraints[Horizontal].append(c); // Set the Layout Left edge as the root of the horizontal graph. AnchorVertex *v = internalVertex(layout, QGraphicsAnchorLayout::Left); graph[Horizontal].setRootVertex(v); // Vertical - c = new QSimplexConstraint; data = new AnchorData(0, 0, QWIDGETSIZE_MAX); addAnchor(layout, QGraphicsAnchorLayout::Top, layout, - QGraphicsAnchorLayout::VCenter, data); - data->skipInPreferred = 1; - c->variables.insert(data, 1.0); - - data = new AnchorData(0, 0, QWIDGETSIZE_MAX); - addAnchor(layout, QGraphicsAnchorLayout::VCenter, - layout, QGraphicsAnchorLayout::Bottom, data); + QGraphicsAnchorLayout::Bottom, data); data->skipInPreferred = 1; - c->variables.insert(data, -1.0); - - itemCenterConstraints[Vertical].append(c); // Set the Layout Top edge as the root of the vertical graph. v = internalVertex(layout, QGraphicsAnchorLayout::Top); @@ -476,10 +456,8 @@ void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges() { Q_Q(QGraphicsAnchorLayout); - removeAnchor(q, QGraphicsAnchorLayout::Left, q, QGraphicsAnchorLayout::HCenter); - removeAnchor(q, QGraphicsAnchorLayout::HCenter, q, QGraphicsAnchorLayout::Right); - removeAnchor(q, QGraphicsAnchorLayout::Top, q, QGraphicsAnchorLayout::VCenter); - removeAnchor(q, QGraphicsAnchorLayout::VCenter, q, QGraphicsAnchorLayout::Bottom); + removeAnchor(q, QGraphicsAnchorLayout::Left, q, QGraphicsAnchorLayout::Right); + removeAnchor(q, QGraphicsAnchorLayout::Top, q, QGraphicsAnchorLayout::Bottom); } void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) @@ -505,6 +483,17 @@ void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) QGraphicsAnchorLayout::Bottom, data); } +/*! + \internal + + By default, each item in the layout is represented internally as + a single anchor in each direction. For instance, from Left to Right. + + However, to support anchorage of items to the center of items, we + must split this internal anchor into two half-anchors. From Left + to Center and then from Center to Right, with the restriction that + these anchors must have the same time at all times. +*/ void QGraphicsAnchorLayoutPrivate::createCenterAnchors( QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge) { @@ -1110,6 +1099,25 @@ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromDefaults(Orientation or } } +/*! + \internal + + For graph edges ("anchors") that represent items, this method updates their + intrinsic size restrictions, based on the item size hints. + + ################################################################################ + ### TODO: This method is not simplification ready. The fact that the graph may + have been simplified means that not all anchors exist in the graph. + This causes the Q_ASSERT(data) calls below to fail. + + A possible approach is to use a recursive method that goes through + all edges in the graph updating their sizes based on their kind, i.e.: + - Anchors -> update their size based on the style defaults + - ItemAnchors -> update their size based on the item sizeHints + - SimplificationAnchors -> call "update()" on its children and then + calculate its own sizes + ################################################################################ +*/ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orientation) { QPair beginningKey; @@ -1153,6 +1161,7 @@ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orien AnchorData *data; if (center == 0) { data = graph[orientation].edgeData(beginning, end); + Q_ASSERT(data); // Set the anchor nominal sizes to those of the corresponding item data->minSize = min; data->prefSize = pref; @@ -1175,6 +1184,7 @@ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orien // Same as above, for each half data = graph[orientation].edgeData(beginning, center); + Q_ASSERT(data); data->minSize = min; data->prefSize = pref; data->maxSize = max; @@ -1183,6 +1193,7 @@ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orien data->sizeAtMaximum = pref; data = graph[orientation].edgeData(center, end); + Q_ASSERT(data); data->minSize = min; data->prefSize = pref; data->maxSize = max; @@ -1303,20 +1314,28 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) Q_Q(QGraphicsAnchorLayout); // Find layout vertices and edges for the current orientation. - AnchorVertex *layoutFirstVertex = - internalVertex(q, orientation == Horizontal ? - QGraphicsAnchorLayout::Left : QGraphicsAnchorLayout::Top); + AnchorVertex *layoutFirstVertex = \ + internalVertex(q, pickEdge(QGraphicsAnchorLayout::Left, orientation)); + + AnchorVertex *layoutCentralVertex = \ + internalVertex(q, pickEdge(QGraphicsAnchorLayout::HCenter, orientation)); - AnchorVertex *layoutCentralVertex = - internalVertex(q, orientation == Horizontal ? - QGraphicsAnchorLayout::HCenter : QGraphicsAnchorLayout::VCenter); + AnchorVertex *layoutLastVertex = \ + internalVertex(q, pickEdge(QGraphicsAnchorLayout::Right, orientation)); - AnchorVertex *layoutLastVertex = - internalVertex(q, orientation == Horizontal ? - QGraphicsAnchorLayout::Right : QGraphicsAnchorLayout::Bottom); + Q_ASSERT(layoutFirstVertex && layoutLastVertex); - AnchorData *edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutCentralVertex); - AnchorData *edgeL2 = graph[orientation].edgeData(layoutCentralVertex, layoutLastVertex); + AnchorData *edgeL1 = NULL; + AnchorData *edgeL2 = NULL; + + // The layout may have a single anchor between Left and Right or two half anchors + // passing through the center + if (layoutCentralVertex) { + edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutCentralVertex); + edgeL2 = graph[orientation].edgeData(layoutCentralVertex, layoutLastVertex); + } else { + edgeL1 = graph[orientation].edgeData(layoutFirstVertex, layoutLastVertex); + } QLinkedList remainingConstraints; for (int i = 0; i < constraints[orientation].count(); ++i) { @@ -1331,7 +1350,8 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) QSet trunkVariables; trunkVariables += edgeL1; - trunkVariables += edgeL2; + if (edgeL2) + trunkVariables += edgeL2; bool dirty; bool hasNonTrunkConstraints = false; -- cgit v0.12 From f48d5dd7554edef5ac3fa11ebd86eaa52a8e23b6 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Thu, 23 Jul 2009 19:59:05 -0300 Subject: QGraphicsAnchorLayout: Fix center anchor deletion logic We were missing a call to "removeCenterAnchors" in the "removeAnchors" method. To solve that, and also reduce the number of useless calls to that method, this commit: 1) Calls it only from "removeInternalVertex" 2) Does some preliminary testing before calling it 3) Replace the former test inside "removeCenterAnchors" by a Q_ASSERT It also adds other Q_ASSERTs to protect us from regression bugs. Signed-off-by: Eduardo M. Fleury Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 3 --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 18 ++++++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index fe335e8..6360e75 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -200,9 +200,6 @@ void QGraphicsAnchorLayout::removeAnchor(QGraphicsLayoutItem *firstItem, Edge fi } d->removeAnchor(firstItem, firstEdge, secondItem, secondEdge); - d->removeCenterAnchors(firstItem, firstEdge); - d->removeCenterAnchors(secondItem, secondEdge); - invalidate(); } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 7516c06..afe60a1 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -456,6 +456,9 @@ void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges() { Q_Q(QGraphicsAnchorLayout); + Q_ASSERT(internalVertex(q, QGraphicsAnchorLayout::HCenter) == NULL); + Q_ASSERT(internalVertex(q, QGraphicsAnchorLayout::VCenter) == NULL); + removeAnchor(q, QGraphicsAnchorLayout::Left, q, QGraphicsAnchorLayout::Right); removeAnchor(q, QGraphicsAnchorLayout::Top, q, QGraphicsAnchorLayout::Bottom); } @@ -568,11 +571,6 @@ void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( return; } - // Check if vertex is used by other than the internal anchors - AnchorVertex *center = internalVertex(item, centerEdge); - if (graph[orientation].adjacentVertices(center).count() > 2) - return; - // Orientation code QGraphicsAnchorLayout::Edge firstEdge; QGraphicsAnchorLayout::Edge lastEdge; @@ -586,8 +584,10 @@ void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( } AnchorVertex *first = internalVertex(item, firstEdge); + AnchorVertex *center = internalVertex(item, centerEdge); AnchorVertex *last = internalVertex(item, lastEdge); - Q_ASSERT(first && last); + Q_ASSERT(first && center && last); + Q_ASSERT(graph[orientation].adjacentVertices(center).count() == 2); // Create new anchor AnchorData *oldData = graph[orientation].edgeData(first, center); @@ -792,6 +792,12 @@ void QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *ite } else { // Update reference count m_vertexList.insert(pair, v); + + if ((v.second == 2) && + ((edge == QGraphicsAnchorLayout::HCenter) || + (edge == QGraphicsAnchorLayout::VCenter))) { + removeCenterAnchors(item, edge); + } } } -- cgit v0.12 From c08f8182d81e053b4030e8883acb95de1d9aba84 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Fri, 7 Aug 2009 21:46:38 -0300 Subject: QGraphicsAnchorLayout: Adding updateChildrenSizes method to anchors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This method is meant to be called by the AnchorLayout after it has set the sizes of all root anchors of a simplified graph. The idea here it that these anchors will then propagate its new sizes down the simplification tree. Signed-off-by: Eduardo M. Fleury Reviewed-by: Jan-Arve Sæther --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 24 ++++++++++++++++++++++++ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 7 +++++++ 2 files changed, 31 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index afe60a1..4844748 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -45,6 +45,30 @@ #include "qgraphicsanchorlayout_p.h" +void ParallelAnchorData::updateChildrenSizes() +{ + firstEdge->sizeAtMinimum = secondEdge->sizeAtMinimum = sizeAtMinimum; + firstEdge->sizeAtPreferred = secondEdge->sizeAtPreferred = sizeAtPreferred; + firstEdge->sizeAtMaximum = secondEdge->sizeAtMaximum = sizeAtMaximum; + + firstEdge->updateChildrenSizes(); + secondEdge->updateChildrenSizes(); +} + +void SequentialAnchorData::updateChildrenSizes() +{ + qreal minFactor = sizeAtMinimum / minSize; + qreal prefFactor = sizeAtPreferred / prefSize; + qreal maxFactor = sizeAtMaximum / maxSize; + + for (int i = 0; i < m_edges.count(); ++i) { + m_edges[i]->sizeAtMinimum = m_edges[i]->minSize * minFactor; + m_edges[i]->sizeAtPreferred = m_edges[i]->prefSize * prefFactor; + m_edges[i]->sizeAtMaximum = m_edges[i]->maxSize * maxFactor; + m_edges[i]->updateChildrenSizes(); + } +} + QSimplexConstraint *GraphPath::constraint(const GraphPath &path) const { // Calculate diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 2f1aa97..1c0f737 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -145,6 +145,8 @@ struct AnchorData : public QSimplexVariable { sizeAtMinimum(0), sizeAtPreferred(0), sizeAtMaximum(0), skipInPreferred(0), type(Normal), hasSize(false) {} + virtual void updateChildrenSizes() { }; + inline QString toString() const; QString name; @@ -192,6 +194,8 @@ struct SequentialAnchorData : public AnchorData name = QLatin1String("SequentialAnchorData"); } + virtual void updateChildrenSizes(); + void setVertices(const QVector &vertices) { m_children = vertices; @@ -212,6 +216,9 @@ struct ParallelAnchorData : public AnchorData origin = first->origin; name = QString::fromAscii("%1 | %2").arg(first->toString(), second->toString()); } + + virtual void updateChildrenSizes(); + AnchorData* firstEdge; AnchorData* secondEdge; }; -- cgit v0.12 From 2f5516cc5a8990e6d1bc5d8d557c421dc0150265 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Fri, 7 Aug 2009 21:51:11 -0300 Subject: QGraphicsAnchorLayout: Adding calculateGraphs() method documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit That's too important a method to remain without at least some doc ;) Signed-off-by: Eduardo M. Fleury Reviewed-by: Jan-Arve Sæther --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 30 ++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 4844748..52fac1d 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -984,6 +984,29 @@ QList getVariables(QList constraints) return variableSet.toList(); } +/*! + \internal + + 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: + + 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. + + 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. + + 4) Once the root anchors had its sizes calculated, propagate that to the + anchors they represent. +*/ void QGraphicsAnchorLayoutPrivate::calculateGraphs( QGraphicsAnchorLayoutPrivate::Orientation orientation) { @@ -992,13 +1015,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // Reset the nominal sizes of each anchor based on the current item sizes setAnchorSizeHintsFromItems(orientation); -// ### currently crashes - //q->dumpGraph(); -// simplifyGraph(orientation); - //q->dumpGraph(); -// restoreSimplifiedGraph(orientation); // should not be here - //q->dumpGraph(); - // Reset the nominal sizes of each anchor based on the current item sizes setAnchorSizeHintsFromDefaults(orientation); -- cgit v0.12 From e2436e25b066ed8236b9cba3483d4a0b87500c91 Mon Sep 17 00:00:00 2001 From: "Eduardo M. Fleury" Date: Fri, 7 Aug 2009 21:52:46 -0300 Subject: QGraphicsAnchorLayout: Enable graph simplification This commit enables graph simplification in its simplest way, which is to simplify the graph, run simplex and then restore it back. It also adds code to handle cases where the simplification handles the whole graph, and that therefore no simplex needs to be run. Additionally, the updateChildrenSizes() method is called on every root anchor in order to propagate the new sizes down the tree. A know issue appeared after this commit, as the quote below explains: ===== Q_ASSERT error in "Parallel" test That's something that started happening in the Parallel test (tests/auto/qgraphicsanchorlayout) after my commit of today, that enables simplification. I haven't been able to investigate it deeply but that's what I know about it: - It's triggered when the layout is destroyed, more precisely, from within "deleteLayoutEdges". What happens is that the layout structural anchors are missing at that point. - I suspect there's something to do with the simplifyGraph() and/or restoreSimplifyGraph() code. I say that because I tried moving the "restoreSimplifiedGraph" call from the end of the calculateGraph(s) method to the line exactly below the "simplifyGraph()" and the error still happens. That means that simplifying and then immediately restoring the graph might not leave it to the point where it was before the simplification. ==== Signed-off-by: Eduardo M. Fleury --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 117 +++++++++++++++++++---- 1 file changed, 99 insertions(+), 18 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 52fac1d..e44b59a 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -197,6 +197,13 @@ static void simplifySequentialChunk(Graph *graph, sequence->minSize = min; sequence->prefSize = pref; sequence->maxSize = max; + + // Unless these values are overhidden by the simplex solver later-on, + // anchors will keep their own preferred size. + sequence->sizeAtMinimum = pref; + sequence->sizeAtPreferred = pref; + sequence->sizeAtMaximum = pref; + sequence->setVertices(vertices); sequence->origin = data->origin == vertices.last() ? before : after; AnchorData *newAnchor = sequence; @@ -208,6 +215,11 @@ static void simplifySequentialChunk(Graph *graph, newAnchor->minSize = min; newAnchor->prefSize = pref; newAnchor->maxSize = max; + + // Same as above, by default, keep preferred size. + newAnchor->sizeAtMinimum = pref; + newAnchor->sizeAtPreferred = pref; + newAnchor->sizeAtMaximum = pref; } graph->createEdge(before, after, newAnchor); } @@ -1018,6 +1030,14 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // Reset the nominal sizes of each anchor based on the current item sizes setAnchorSizeHintsFromDefaults(orientation); + // Simplify the graph + // ### Ideally we would like to do that if, and only if, anchors had + // been added or removed since the last time this method was called. + // However, as the two setAnchorSizeHints methods above are not + // ready to be run on top of a simplified graph, we must simplify + // and restore it every time we get here. + simplifyGraph(orientation); + // Traverse all graph edges and store the possible paths to each vertex findPaths(orientation); @@ -1059,25 +1079,41 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( AnchorVertex *v = internalVertex(q, end); GraphPath trunkPath = graphPaths[orientation].value(v); - // Solve min and max size hints for trunk - QPair minMax = solveMinMax(trunkConstraints, trunkPath); - sizeHints[orientation][Qt::MinimumSize] = minMax.first; - sizeHints[orientation][Qt::MaximumSize] = minMax.second; - - // Solve for preferred. The objective function is calculated from the constraints - // and variables internally. - solvePreferred(trunkConstraints); - - // Calculate and set the preferred size for the layout from the edge sizes that - // were calculated above. - qreal pref(0.0); - foreach (const AnchorData *ad, trunkPath.positives) { - pref += ad->sizeAtPreferred; - } - foreach (const AnchorData *ad, trunkPath.negatives) { - pref -= ad->sizeAtPreferred; + if (!trunkConstraints.isEmpty()) { + // Solve min and max size hints for trunk + QPair minMax = solveMinMax(trunkConstraints, trunkPath); + sizeHints[orientation][Qt::MinimumSize] = minMax.first; + sizeHints[orientation][Qt::MaximumSize] = minMax.second; + + // Solve for preferred. The objective function is calculated from the constraints + // and variables internally. + solvePreferred(trunkConstraints); + + // Calculate and set the preferred size for the layout from the edge sizes that + // were calculated above. + qreal pref(0.0); + foreach (const AnchorData *ad, trunkPath.positives) { + pref += ad->sizeAtPreferred; + } + foreach (const AnchorData *ad, trunkPath.negatives) { + pref -= ad->sizeAtPreferred; + } + sizeHints[orientation][Qt::PreferredSize] = pref; + } else { + // No Simplex is necessary because the path was simplified all the way to a single + // anchor. + Q_ASSERT(trunkPath.positives.count() == 1); + Q_ASSERT(trunkPath.negatives.count() == 0); + + AnchorData *ad = trunkPath.positives.toList()[0]; + ad->sizeAtMinimum = ad->minSize; + ad->sizeAtPreferred = ad->prefSize; + ad->sizeAtMaximum = ad->maxSize; + + sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum; + sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred; + sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum; } - sizeHints[orientation][Qt::PreferredSize] = pref; // Delete the constraints, we won't use them anymore. qDeleteAll(sizeHintConstraints); @@ -1116,6 +1152,51 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( qDeleteAll(constraints[orientation]); constraints[orientation].clear(); graphPaths[orientation].clear(); // ### + + // Propagate the new sizes down the simplified graph, ie. tell the group anchors + // to set their children anchors sizes. + + // ### Note that we can split the anchors into two categories: + // 1) Those that appeared in at least one constraint and so went through the + // Simplex solver. Either as a Trunk or Non-Trunk variable. + // 2) Those that did not go through the Simplex solver at all. + // + // Anchors of the type (1) had its effective sizes (ie. sizeAtMinimum/Pref/Max) + // properly set by the "solveMinMax" and "solvePreferred" methods. + // + // However, those of type (2), still need to have their effective sizes set, + // in that case, to their own nominal values. + // + // Due to the above reasons, we can't simply iterate on the variables that + // belong to a graph part. We have to iterate through _all_ root anchors + // in graph[orientation]. That's why we collect "allAnchors". We gotta make + // this better somehow. + + // ### Did I say that's ugly? + QSet allAnchors; + QQueue queue; + queue << graph[orientation].rootVertex(); + while (!queue.isEmpty()) { + AnchorVertex *vertex = queue.dequeue(); + QList adjacentVertices = graph[orientation].adjacentVertices(vertex); + for (int i = 0; i < adjacentVertices.count(); ++i) { + AnchorData *edge = graph[orientation].edgeData(vertex, adjacentVertices[i]); + if (allAnchors.contains(edge)) + continue; + allAnchors << edge; + queue << adjacentVertices[i]; + } + } + + // Ok, now that we have all anchors, actually propagate the sizes down its children. + // Note that for anchors that didn't have its effectiveSizes set yet, we use the + // nominal one instead. + QSet::const_iterator iter; + for (iter = allAnchors.constBegin(); iter != allAnchors.constEnd(); ++iter) + (*iter)->updateChildrenSizes(); + + // Restore the graph. See the ### note next to the simplifyGraph() call. + restoreSimplifiedGraph(orientation); } void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromDefaults(Orientation orientation) -- cgit v0.12 From 1d3de98158b9409fa63e2e7cf8d3ca8b77f26285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 12 Aug 2009 09:06:38 +0200 Subject: Added Graph::connections(). This gives a list of all "edges" in the graph, but each edge is specified as a pair of vertices, since you cannot easily know the two AnchorVertex'es of an EdgeData. Added some debug stuff. --- src/gui/graphicsview/qgraph_p.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h index 053114b..3abbe3b 100644 --- a/src/gui/graphicsview/qgraph_p.h +++ b/src/gui/graphicsview/qgraph_p.h @@ -30,6 +30,14 @@ public: return column.key(); } + inline Vertex *from() const { + return row.key(); + } + + inline Vertex *to() const { + return column.key(); + } + inline bool operator==(const const_iterator &o) const { return !(*this != o); } inline bool operator!=(const const_iterator &o) const { if (row == g->m_graph.end()) { @@ -83,6 +91,15 @@ public: void createEdge(Vertex *first, Vertex *second, EdgeData *data) { // Creates a bidirectional edge +#if 0 + qDebug("Graph::createEdge(): %s", + qPrintable(QString::fromAscii("%1-%2") + .arg(first->toString()).arg(second->toString()))); +#endif + if (edgeData(first, second)) { + qWarning(qPrintable(QString::fromAscii("%1-%2 already has an edge") + .arg(first->toString()).arg(second->toString()))); + } createDirectedEdge(first, second, data); createDirectedEdge(second, first, data); } @@ -90,6 +107,11 @@ public: void removeEdge(Vertex *first, Vertex *second) { // Removes a bidirectional edge +#if 0 + qDebug("Graph::removeEdge(): %s", + qPrintable(QString::fromAscii("%1-%2") + .arg(first->toString()).arg(second->toString()))); +#endif EdgeData *data = edgeData(first, second); removeDirectedEdge(first, second); removeDirectedEdge(second, first); @@ -98,6 +120,11 @@ public: EdgeData *takeEdge(Vertex* first, Vertex* second) { +#if 0 + qDebug("Graph::takeEdge(): %s", + qPrintable(QString::fromAscii("%1-%2") + .arg(first->toString()).arg(second->toString()))); +#endif // Removes a bidirectional edge EdgeData *data = edgeData(first, second); if (data) { @@ -129,6 +156,19 @@ public: return setOfVertices; } + QList > connections() const { + QList > conns; + for (const_iterator it = constBegin(); it != constEnd(); ++it) { + Vertex *from = it.from(); + Vertex *to = it.to(); + // do not return (from,to) *and* (to,from) + if (from < to) { + conns.append(qMakePair(from, to)); + } + } + return conns; + } + QString serializeToDot() { // traversal QString strVertices; QString edges; -- cgit v0.12 From d67b7829e7d5d8fbd57b701f4268dc1c6d77f1b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 12 Aug 2009 09:33:37 +0200 Subject: Fix a bug in removeAnchors and reorder how anchors are removed. The problem was that if an item had a center anchor and we removed the left-center anchor before we called removeCenterAnchors(). Suppose the center vertex had 3 edges, (refcount was 3) removeAnchor would then *first* remove the left-center edge, causing the internalVertex refcount to go to 2. That would cause removeInternalVertex() to try to "merge" the two center anchors by calling removeCenterAnchors(). Of course, calling removeCenterAnchors at that point did not work since removeCenterAnchors() assumed that the only two edges connected to the center vertex was its internal left-center and center-right anchors. This was not the case, and the assertion Q_ASSERT(first && center && last); was triggered, since first (or last) was removed at that point. Of course it was not enough to simply call removeCenterAnchors first, because that function assumed that the only two anchors connected to the center vertex was "internal center anchors". removeAnchors is a bit special since we are not really interested in first converting the "internal center anchors" to a single internal anchor because the ultimate goal of that function is to remove *all* anchors. The solution was the additional argument "substitute" to indicate that we really just want to substitute with a simpler anchor (normal behaviour). If not, we really just want to delete the center anchors (don't even try to merge them into a simple internal anchor). --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 97 +++++++++++++++--------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 4 +- 2 files changed, 66 insertions(+), 35 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index e44b59a..6b7a12c 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -592,7 +592,8 @@ void QGraphicsAnchorLayoutPrivate::createCenterAnchors( } void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( - QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge) + QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge, + bool substitute) { Orientation orientation; switch (centerEdge) { @@ -619,22 +620,20 @@ void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( lastEdge = QGraphicsAnchorLayout::Bottom; } - AnchorVertex *first = internalVertex(item, firstEdge); AnchorVertex *center = internalVertex(item, centerEdge); + if (!center) + return; + AnchorVertex *first = internalVertex(item, firstEdge); AnchorVertex *last = internalVertex(item, lastEdge); - Q_ASSERT(first && center && last); - Q_ASSERT(graph[orientation].adjacentVertices(center).count() == 2); - // Create new anchor - AnchorData *oldData = graph[orientation].edgeData(first, center); + Q_ASSERT(first); + Q_ASSERT(center); + Q_ASSERT(last); - int minimumSize = oldData->minSize * 2; - int preferredSize = oldData->prefSize * 2; - int maximumSize = oldData->maxSize * 2; + Graph &g = graph[orientation]; - AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); - addAnchor(item, firstEdge, item, lastEdge, data); + AnchorData *oldData = g.edgeData(first, center); // Remove center constraint for (int i = itemCenterConstraints[orientation].count() - 1; i >= 0; --i) { if (itemCenterConstraints[orientation][i]->variables.contains(oldData)) { @@ -643,9 +642,36 @@ void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( } } - // Remove old anchors - removeAnchor(item, firstEdge, item, centerEdge); - removeAnchor(item, centerEdge, item, lastEdge); + if (substitute) { + // Create the new anchor that should substitute the left-center-right anchors. + AnchorData *oldData = g.edgeData(first, center); + + int minimumSize = oldData->minSize * 2; + int preferredSize = oldData->prefSize * 2; + int maximumSize = oldData->maxSize * 2; + + AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); + addAnchor(item, firstEdge, item, lastEdge, data); + + // Remove old anchors + removeAnchor(item, firstEdge, item, centerEdge); + removeAnchor(item, centerEdge, item, lastEdge); + + } else { + // this is only called from removeAnchors() + // first, remove all non-internal anchors + QList adjacents = g.adjacentVertices(center); + for (int i = 0; i < adjacents.count(); ++i) { + AnchorVertex *v = adjacents.at(i); + if (v->m_item != item) { + removeAnchor(item, centerEdge, v->m_item, v->m_edge); + } + } + // when all non-internal anchors is removed it will automatically merge the + // center anchor into a left-right (or top-bottom) anchor. We must also delete that. + // by this time, the center vertex is deleted and merged into a non-centered internal anchor + removeAnchor(item, firstEdge, item, lastEdge); + } } @@ -832,7 +858,7 @@ void QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *ite if ((v.second == 2) && ((edge == QGraphicsAnchorLayout::HCenter) || (edge == QGraphicsAnchorLayout::VCenter))) { - removeCenterAnchors(item, edge); + removeCenterAnchors(item, edge, true); } } } @@ -856,30 +882,33 @@ void QGraphicsAnchorLayoutPrivate::removeAnchor(QGraphicsLayoutItem *firstItem, removeInternalVertex(secondItem, secondEdge); } -void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item) +void QGraphicsAnchorLayoutPrivate::removeVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge) { - AnchorVertex *v1 = 0; - AnchorVertex *v2 = 0; - QList allVertex; - int edge; - - for (edge = QGraphicsAnchorLayout::Left; edge <= QGraphicsAnchorLayout::Bottom; ++edge) { - // Remove all vertex for all edges - QGraphicsAnchorLayout::Edge e = static_cast(edge); - - if ((v1 = internalVertex(item, e))) { - // Remove all edges - allVertex = graph[edgeOrientation(e)].adjacentVertices(v1); - - foreach (v2, allVertex) { - graph[edgeOrientation(e)].removeEdge(v1, v2); - removeInternalVertex(item, e); - removeInternalVertex(v2->m_item, v2->m_edge); - } + if (AnchorVertex *v = internalVertex(item, edge)) { + Graph &g = graph[edgeOrientation(edge)]; + const QList allVertices = graph[edgeOrientation(edge)].adjacentVertices(v); + AnchorVertex *v2; + foreach (v2, allVertices) { + g.removeEdge(v, v2); + removeInternalVertex(item, edge); + removeInternalVertex(v2->m_item, v2->m_edge); } } } +void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item) +{ + // remove the center anchor first!! + removeCenterAnchors(item, QGraphicsAnchorLayout::HCenter, false); + removeVertex(item, QGraphicsAnchorLayout::Left); + removeVertex(item, QGraphicsAnchorLayout::Right); + + removeCenterAnchors(item, QGraphicsAnchorLayout::VCenter, false); + removeVertex(item, QGraphicsAnchorLayout::Top); + removeVertex(item, QGraphicsAnchorLayout::Bottom); + +} + /*! \internal diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 1c0f737..131d7c3 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -300,7 +300,7 @@ public: void deleteLayoutEdges(); void createItemEdges(QGraphicsLayoutItem *item); void createCenterAnchors(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge); - void removeCenterAnchors(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge); + void removeCenterAnchors(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge, bool substitute = true); void removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation); // helper function used by the 4 API functions @@ -324,6 +324,8 @@ public: void removeAnchors(QGraphicsLayoutItem *item); + void removeVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge); + void correctEdgeDirection(QGraphicsLayoutItem *&firstItem, QGraphicsAnchorLayout::Edge &firstEdge, QGraphicsLayoutItem *&secondItem, -- cgit v0.12 From 14601e46729084f7f822bc7ef402553fba512452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 12 Aug 2009 10:42:27 +0200 Subject: Fix implementation of setAnchorSizeHintsFromItems to be simplification ready. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 113 +++++++++++++---------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 5 + 2 files changed, 70 insertions(+), 48 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 6b7a12c..337f887 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -55,6 +55,15 @@ void ParallelAnchorData::updateChildrenSizes() secondEdge->updateChildrenSizes(); } +void ParallelAnchorData::refreshSizeHints() +{ + firstEdge->refreshSizeHints(); + secondEdge->refreshSizeHints(); + minSize = qMax(firstEdge->minSize, secondEdge->minSize); + prefSize = qMin(firstEdge->prefSize, secondEdge->prefSize); + maxSize = qMin(firstEdge->maxSize, secondEdge->maxSize); +} + void SequentialAnchorData::updateChildrenSizes() { qreal minFactor = sizeAtMinimum / minSize; @@ -69,6 +78,20 @@ void SequentialAnchorData::updateChildrenSizes() } } +void SequentialAnchorData::refreshSizeHints() +{ + minSize = 0; + prefSize = 0; + maxSize = 0; + for (int i = 0; i < m_edges.count(); ++i) { + AnchorData *edge = m_edges.at(i); + edge->refreshSizeHints(); + minSize += edge->minSize; + prefSize += edge->prefSize; + maxSize += edge->maxSize; + } +} + QSimplexConstraint *GraphPath::constraint(const GraphPath &path) const { // Calculate @@ -1057,7 +1080,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( setAnchorSizeHintsFromItems(orientation); // Reset the nominal sizes of each anchor based on the current item sizes - setAnchorSizeHintsFromDefaults(orientation); + //setAnchorSizeHintsFromDefaults(orientation); // Simplify the graph // ### Ideally we would like to do that if, and only if, anchors had @@ -1276,6 +1299,11 @@ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromDefaults(Orientation or */ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orientation) { + Q_Q(QGraphicsAnchorLayout); + Graph &g = graph[orientation]; + QList > conns = g.connections(); + QGraphicsAnchorLayout::Edge centerEdge = pickEdge(QGraphicsAnchorLayout::HCenter, orientation); + QPair beginningKey; QPair centerKey; QPair endKey; @@ -1290,34 +1318,33 @@ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orien endKey.second = QGraphicsAnchorLayout::Bottom; } - foreach (QGraphicsLayoutItem *item, items) { - AnchorVertex *beginning, *center, *end; - qreal min, pref, max; - - beginningKey.first = item; - centerKey.first = item; - endKey.first = item; + for (int i = 0; i < conns.count(); ++i) { + AnchorVertex *from = conns.at(i).first; + AnchorVertex *to = conns.at(i).second; - beginning = internalVertex(beginningKey); - center = internalVertex(centerKey); - end = internalVertex(endKey); + QGraphicsLayoutItem *item = from->m_item; + qreal min, pref, max; - if (orientation == Horizontal) { - min = item->minimumWidth(); - pref = item->preferredWidth(); - max = item->maximumWidth(); - } else { - min = item->minimumHeight(); - pref = item->preferredHeight(); - max = item->maximumHeight(); - } + AnchorData *data = g.edgeData(from, to); + Q_ASSERT(data); + // Internal item anchor + if (item != q && item == to->m_item) { + // internal item anchor: get size from sizeHint + if (orientation == Horizontal) { + min = item->minimumWidth(); + pref = item->preferredWidth(); + max = item->maximumWidth(); + } else { + min = item->minimumHeight(); + pref = item->preferredHeight(); + max = item->maximumHeight(); + } - // To support items that are represented by a single anchor as well as - // those that have been divided into two halfs, we must do this check. - AnchorData *data; - if (center == 0) { - data = graph[orientation].edgeData(beginning, end); - Q_ASSERT(data); + if (from->m_edge == centerEdge || to->m_edge == centerEdge) { + min /= 2; + pref /= 2; + max /= 2; + } // Set the anchor nominal sizes to those of the corresponding item data->minSize = min; data->prefSize = pref; @@ -1333,29 +1360,19 @@ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orien data->sizeAtMinimum = pref; data->sizeAtPreferred = pref; data->sizeAtMaximum = pref; + } else if (data->type != AnchorData::Normal) { + data->refreshSizeHints(); } else { - min = min / 2; - pref = pref / 2; - max = max / 2; - - // Same as above, for each half - data = graph[orientation].edgeData(beginning, center); - Q_ASSERT(data); - data->minSize = min; - data->prefSize = pref; - data->maxSize = max; - data->sizeAtMinimum = pref; - data->sizeAtPreferred = pref; - data->sizeAtMaximum = pref; - - data = graph[orientation].edgeData(center, end); - Q_ASSERT(data); - data->minSize = min; - data->prefSize = pref; - data->maxSize = max; - data->sizeAtMinimum = pref; - data->sizeAtPreferred = pref; - data->sizeAtMaximum = pref; + // anchors between items, their sizes may depend on the style. + if (!data->hasSize) { + qreal s = effectiveSpacing(orientation); + data->minSize = s; + data->prefSize = s; + data->maxSize = s; + data->sizeAtMinimum = s; + data->sizeAtPreferred = s; + data->sizeAtMaximum = s; + } } } } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 131d7c3..b637f08 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -147,6 +147,8 @@ struct AnchorData : public QSimplexVariable { virtual void updateChildrenSizes() { }; + virtual void refreshSizeHints() { }; + inline QString toString() const; QString name; @@ -195,6 +197,7 @@ struct SequentialAnchorData : public AnchorData } virtual void updateChildrenSizes(); + virtual void refreshSizeHints(); void setVertices(const QVector &vertices) { @@ -218,6 +221,8 @@ struct ParallelAnchorData : public AnchorData } virtual void updateChildrenSizes(); + virtual void refreshSizeHints(); + AnchorData* firstEdge; AnchorData* secondEdge; -- cgit v0.12 From 3b91bdf93159adcb73c66f64d494a5bdc190c693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 12 Aug 2009 11:12:51 +0200 Subject: Disable simplification and restoration since it still has problems. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 337f887..5469431 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1088,7 +1088,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // However, as the two setAnchorSizeHints methods above are not // ready to be run on top of a simplified graph, we must simplify // and restore it every time we get here. - simplifyGraph(orientation); + //simplifyGraph(orientation); // Traverse all graph edges and store the possible paths to each vertex findPaths(orientation); @@ -1248,7 +1248,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( (*iter)->updateChildrenSizes(); // Restore the graph. See the ### note next to the simplifyGraph() call. - restoreSimplifiedGraph(orientation); + //restoreSimplifiedGraph(orientation); } void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromDefaults(Orientation orientation) -- cgit v0.12 From 489685ef0724c852d964504853b86be39bddc7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 12 Aug 2009 10:35:48 +0200 Subject: restoring simplified anchors did not work properly recursively. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 78 ++++++++++++++++-------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 5469431..9a1cdd0 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -413,6 +413,54 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O } } +static void restoreSimplifiedAnchor(Graph &g, + AnchorData *edge, + AnchorVertex *before, + AnchorVertex *after) +{ + Q_ASSERT(edge->type != AnchorData::Normal); +#if 0 + static const char *anchortypes[] = {"Normal", + "Sequential", + "Parallel"}; + qDebug("Restoring %s edge.", anchortypes[int(edge->type)]); +#endif + if (edge->type == AnchorData::Sequential) { + SequentialAnchorData* seqEdge = static_cast(edge); + // restore the sequential anchor + AnchorVertex *prev = before; + AnchorVertex *last = after; + if (edge->origin != prev) + qSwap(last, prev); + + for (int i = 0; i < seqEdge->m_edges.count(); ++i) { + AnchorVertex *v1 = (i < seqEdge->m_children.count()) ? seqEdge->m_children.at(i) : last; + AnchorData *data = seqEdge->m_edges.at(i); + if (data->type != AnchorData::Normal) { + restoreSimplifiedAnchor(g, data, prev, v1); + } else { + g.createEdge(prev, v1, data); + } + prev = v1; + } + g.removeEdge(before, after); + } else if (edge->type == AnchorData::Parallel) { + ParallelAnchorData* parallelEdge = static_cast(edge); + AnchorData *parallelEdges[2] = {parallelEdge->firstEdge, + parallelEdge->secondEdge}; + // must remove the old anchor first + g.removeEdge(before, after); + for (int i = 0; i < 2; ++i) { + AnchorData *data = parallelEdges[i]; + if (data->type != AnchorData::Normal) { + restoreSimplifiedAnchor(g, data, before, after); + } else { + g.createEdge(before, after, data); + } + } + } +} + void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientation) { Q_Q(QGraphicsAnchorLayout); @@ -438,33 +486,9 @@ void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientatio if (visited.contains(next)) continue; - QQueue queue; - queue.enqueue(g.edgeData(v, next)); - while (!queue.isEmpty()) { - AnchorData *edge = queue.dequeue(); - if (edge->type == AnchorData::Sequential) { - SequentialAnchorData* seqEdge = static_cast(edge); - // restore the sequential anchor - AnchorVertex *prev = v; - AnchorVertex *last = next; - if (edge->origin != prev) - qSwap(last, prev); - - for (int i = 0; i < seqEdge->m_edges.count(); ++i) { - AnchorVertex *v1 = (i < seqEdge->m_children.count()) ? seqEdge->m_children.at(i) : last; - AnchorData *data = seqEdge->m_edges.at(i); - g.createEdge(prev, v1, data); - prev = v1; - } - g.removeEdge(v, next); - } else if (edge->type == AnchorData::Parallel) { - ParallelAnchorData* parallelEdge = static_cast(edge); - queue.enqueue(parallelEdge->firstEdge); - queue.enqueue(parallelEdge->secondEdge); - g.removeEdge(v, next); - } - } - stack.push(next); + AnchorData *edge = g.edgeData(v, next); + if (edge->type != AnchorData::Normal) + restoreSimplifiedAnchor(g, edge, v, next); } visited.insert(v); } -- cgit v0.12 From ae67fcb0538c0f47c77eef0ed39955e33671343e Mon Sep 17 00:00:00 2001 From: Artur Duque de Souza Date: Wed, 12 Aug 2009 11:33:14 -0300 Subject: Update Anchor Layout API More user friendly API for anchor layouts. Changed method names and also provided more methods to receive spacing. Signed-off-by: Artur Duque de Souza Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 16 +++--- src/gui/graphicsview/qgraphicsanchorlayout.h | 70 +++++++++++++++++++------- 2 files changed, 59 insertions(+), 27 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 6360e75..902f0b2 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -131,10 +131,10 @@ void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, * \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. */ -void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, - Qt::Corner firstCorner, - QGraphicsLayoutItem *secondItem, - Qt::Corner secondCorner) +void QGraphicsAnchorLayout::anchorCorner(QGraphicsLayoutItem *firstItem, + Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, + Qt::Corner secondCorner) { Q_D(QGraphicsAnchorLayout); @@ -157,10 +157,10 @@ void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, * By calling this function the caller can specify the magnitude of the anchor with \a spacing. * */ -void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, - Qt::Corner firstCorner, - QGraphicsLayoutItem *secondItem, - Qt::Corner secondCorner, qreal spacing) +void QGraphicsAnchorLayout::anchorCorner(QGraphicsLayoutItem *firstItem, + Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, + Qt::Corner secondCorner, qreal spacing) { Q_D(QGraphicsAnchorLayout); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h index 1f622eb..249996e 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -78,22 +78,30 @@ public: QGraphicsLayoutItem *secondItem, Edge secondEdge, qreal spacing); - void anchor(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner, - QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner); + void anchorCorner(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner); - void anchor(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner, - QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner, - qreal spacing); + void anchorCorner(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner, + qreal spacing); void removeAnchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, QGraphicsLayoutItem *secondItem, Edge secondEdge); - inline void fillWidth(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo = 0); - inline void fillHeight(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo = 0); - inline void fill(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo = 0); + inline void anchorWidth(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo = 0); + inline void anchorWidth(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo, qreal spacing); + + inline void anchorHeight(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo = 0); + inline void anchorHeight(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo, qreal spacing); + + inline void anchorGeometry(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo = 0); + inline void anchorGeometry(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo, qreal spacing); void setSpacing(qreal spacing, Qt::Orientations orientations = Qt::Horizontal|Qt::Vertical); qreal spacing(Qt::Orientation) const; @@ -115,8 +123,8 @@ private: Q_DECLARE_PRIVATE(QGraphicsAnchorLayout) }; -void QGraphicsAnchorLayout::fillWidth(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo) +void QGraphicsAnchorLayout::anchorWidth(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo) { if (!relativeTo) relativeTo = this; @@ -125,8 +133,16 @@ void QGraphicsAnchorLayout::fillWidth(QGraphicsLayoutItem *item, anchor(item, Right, relativeTo, Right); } -void QGraphicsAnchorLayout::fillHeight(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo) +void QGraphicsAnchorLayout::anchorWidth(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo, + qreal spacing) +{ + anchor(relativeTo, Left, item, Left, spacing); + anchor(item, Right, relativeTo, Right, spacing); +} + +void QGraphicsAnchorLayout::anchorHeight(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo) { if (!relativeTo) relativeTo = this; @@ -135,14 +151,30 @@ void QGraphicsAnchorLayout::fillHeight(QGraphicsLayoutItem *item, anchor(item, Bottom, relativeTo, Bottom); } -void QGraphicsAnchorLayout::fill(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo) +void QGraphicsAnchorLayout::anchorHeight(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo, + qreal spacing) +{ + anchor(relativeTo, Top, item, Top, spacing); + anchor(item, Bottom, relativeTo, Bottom, spacing); +} + +void QGraphicsAnchorLayout::anchorGeometry(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo) { if (!relativeTo) relativeTo = this; - fillWidth(item, relativeTo); - fillHeight(item, relativeTo); + anchorWidth(item, relativeTo); + anchorHeight(item, relativeTo); +} + +void QGraphicsAnchorLayout::anchorGeometry(QGraphicsLayoutItem *item, + QGraphicsLayoutItem *relativeTo, + qreal spacing) +{ + anchorWidth(item, relativeTo, spacing); + anchorHeight(item, relativeTo, spacing); } #endif -- cgit v0.12 From 376c4fdd263f59f6739d045d441e07a97f5c5122 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Wed, 12 Aug 2009 17:26:52 -0300 Subject: Revert "QGraphicsAnchorLayoutPrivate: Avoiding extra loops in getGraphParts" This reverts commit 9e701f76f7478ea078e58076c91fee0d2ec505b4 and also add a comment explaining why is needed to delay the creation of the nonTrunkVariables to a later moment. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 9a1cdd0..e3914b8 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1543,7 +1543,6 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) } QList trunkConstraints; - QList nonTrunkConstraints; QSet trunkVariables; trunkVariables += edgeL1; @@ -1551,7 +1550,6 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) trunkVariables += edgeL2; bool dirty; - bool hasNonTrunkConstraints = false; do { dirty = false; @@ -1577,9 +1575,15 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) it = remainingConstraints.erase(it); dirty = true; } else { - nonTrunkConstraints += c; - it = remainingConstraints.erase(it); - hasNonTrunkConstraints = true; + // Note that we don't erase the constraint if it's not + // a match, since in a next iteration of a do-while we + // can pass on it again and it will be a match. + // + // For example: if trunk share a variable with + // remainingConstraints[1] and it shares with + // remainingConstraints[0], we need a second iteration + // of the do-while loop to match both. + ++it; } } } while (dirty); @@ -1587,8 +1591,15 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) QList< QList > result; result += trunkConstraints; - if (hasNonTrunkConstraints) + if (!remainingConstraints.isEmpty()) { + QList nonTrunkConstraints; + QLinkedList::iterator it = remainingConstraints.begin(); + while (it != remainingConstraints.end()) { + nonTrunkConstraints += *it; + ++it; + } result += nonTrunkConstraints; + } return result; } -- cgit v0.12 From 9fe513c2809cfc0446fdde6870b23e0094ac955f Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Wed, 12 Aug 2009 17:39:57 -0300 Subject: QGraphicsAnchorLayout: add a dumper function for AnchorData Patch from Jan-Arve. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 18 ++++++++++++++++++ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index e3914b8..f0e0660 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -92,6 +92,24 @@ void SequentialAnchorData::refreshSizeHints() } } +void AnchorData::dump(int indent) { + if (type == Parallel) { + qDebug("%*s type: parallel:", indent, ""); + ParallelAnchorData *p = static_cast(this); + p->firstEdge->dump(indent+2); + p->secondEdge->dump(indent+2); + } else if (type == Sequential) { + SequentialAnchorData *s = static_cast(this); + int kids = s->m_edges.count(); + qDebug("%*s type: sequential(%d):", indent, "", kids); + for (int i = 0; i < kids; ++i) { + s->m_edges.at(i)->dump(indent+2); + } + } else { + qDebug("%*s type: Normal:", indent, ""); + } +} + QSimplexConstraint *GraphPath::constraint(const GraphPath &path) const { // Calculate diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index b637f08..3b5f64b 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -149,6 +149,8 @@ struct AnchorData : public QSimplexVariable { virtual void refreshSizeHints() { }; + void dump(int indent = 2); + inline QString toString() const; QString name; -- cgit v0.12 From 114e4b6ca9a1871f75dc5b9d443ab1e344eb99af Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Wed, 12 Aug 2009 17:53:54 -0300 Subject: QGraphicsAnchorLayout: fix restore simplification logic Patch from Jan-Arve. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 44 +++++++----------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index f0e0660..7e0541e 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -461,19 +461,16 @@ static void restoreSimplifiedAnchor(Graph &g, } prev = v1; } - g.removeEdge(before, after); } else if (edge->type == AnchorData::Parallel) { ParallelAnchorData* parallelEdge = static_cast(edge); AnchorData *parallelEdges[2] = {parallelEdge->firstEdge, - parallelEdge->secondEdge}; - // must remove the old anchor first - g.removeEdge(before, after); + parallelEdge->secondEdge}; for (int i = 0; i < 2; ++i) { AnchorData *data = parallelEdges[i]; - if (data->type != AnchorData::Normal) { - restoreSimplifiedAnchor(g, data, before, after); - } else { + if (data->type == AnchorData::Normal) { g.createEdge(before, after, data); + } else { + restoreSimplifiedAnchor(g, data, before, after); } } } @@ -483,32 +480,17 @@ void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientatio { Q_Q(QGraphicsAnchorLayout); Graph &g = graph[orientation]; - AnchorVertex *v = g.rootVertex(); - - if (!v) - return; - QSet visited; - QStack stack; - stack.push(v); - QGraphicsAnchorLayout::Edge layoutCenterEdge = pickEdge(QGraphicsAnchorLayout::HCenter, orientation); - // walk depth-first. - while (!stack.isEmpty()) { - v = stack.pop(); - QList vertices = g.adjacentVertices(v); - const int count = vertices.count(); - for (int i = 0; i < count; ++i) { - AnchorVertex *next = vertices.at(i); - if (next->m_item == q && next->m_edge == layoutCenterEdge) - continue; - if (visited.contains(next)) - continue; - - AnchorData *edge = g.edgeData(v, next); - if (edge->type != AnchorData::Normal) - restoreSimplifiedAnchor(g, edge, v, next); + QList > connections = g.connections(); + for (int i = 0; i < connections.count(); ++i) { + AnchorVertex *v1 = connections.at(i).first; + AnchorVertex *v2 = connections.at(i).second; + AnchorData *edge = g.edgeData(v1, v2); + if (edge->type != AnchorData::Normal) { + AnchorData *oldEdge = g.takeEdge(v1, v2); + restoreSimplifiedAnchor(g, edge, v1, v2); + delete oldEdge; } - visited.insert(v); } } -- cgit v0.12 From 548583f449432363fad8fe547ce073bb754d46be Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 13 Aug 2009 15:07:34 -0300 Subject: QGraphicsAnchorLayout: fix merging of preferredSizeHint In simplification, the preferred size of a parallel group should get the maximum preferred size (and bound it to the maximum size). This follows the principle that for an layout item: 'growing is better than shrinking', and is consistent with what our linear program also do. This patch also make sure that the sizeAt* are properly initialized to the preferred size, the idea is if there's no restriction upon an anchor, it'll keep it's preferred size no matter what. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 7e0541e..d878722 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -57,11 +57,22 @@ void ParallelAnchorData::updateChildrenSizes() void ParallelAnchorData::refreshSizeHints() { + // ### 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. + firstEdge->refreshSizeHints(); secondEdge->refreshSizeHints(); + minSize = qMax(firstEdge->minSize, secondEdge->minSize); - prefSize = qMin(firstEdge->prefSize, secondEdge->prefSize); maxSize = qMin(firstEdge->maxSize, secondEdge->maxSize); + + prefSize = qMax(firstEdge->prefSize, secondEdge->prefSize); + prefSize = qMin(prefSize, maxSize); + + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; } void SequentialAnchorData::updateChildrenSizes() @@ -90,6 +101,10 @@ void SequentialAnchorData::refreshSizeHints() prefSize += edge->prefSize; maxSize += edge->maxSize; } + + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; } void AnchorData::dump(int indent) { @@ -251,8 +266,11 @@ static void simplifySequentialChunk(Graph *graph, if (AnchorData *oldAnchor = graph->takeEdge(before, after)) { newAnchor = new ParallelAnchorData(oldAnchor, sequence); min = qMax(oldAnchor->minSize, sequence->minSize); - pref = qMax(oldAnchor->prefSize, sequence->prefSize); max = qMin(oldAnchor->maxSize, sequence->maxSize); + + pref = qMax(oldAnchor->prefSize, sequence->prefSize); + pref = qMin(pref, max); + newAnchor->minSize = min; newAnchor->prefSize = pref; newAnchor->maxSize = max; -- cgit v0.12 From d9634121cf977f6b03c75135ced06d4f038ada1a Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 13 Aug 2009 15:31:37 -0300 Subject: QGraphicsAnchorLayout: use pickEdge() Signed-off-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index d878722..ee2144c 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1164,13 +1164,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // objective function. // Retrieve that path - QGraphicsAnchorLayout::Edge end; - if (orientation == Horizontal) { - end = QGraphicsAnchorLayout::Right; - } else { - end = QGraphicsAnchorLayout::Bottom; - } - AnchorVertex *v = internalVertex(q, end); + AnchorVertex *v = internalVertex(q, pickEdge(QGraphicsAnchorLayout::Right, orientation)); GraphPath trunkPath = graphPaths[orientation].value(v); if (!trunkConstraints.isEmpty()) { -- cgit v0.12 From c938d16249e24c297f2b36b4711a3e592de5e7d6 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 13 Aug 2009 15:21:08 -0300 Subject: QGraphicsAnchorLayout: simplify updating of anchor sizes Instead of going through all anchors after the calculation of the graphs, do it as early as possible for each part (trunk and nonTrunks) and recursively updates the children in case of a simplified graph. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 58 ++++++------------------ 1 file changed, 13 insertions(+), 45 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index ee2144c..e75bf19 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1177,6 +1177,15 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // and variables internally. solvePreferred(trunkConstraints); + // Propagate the new sizes down the simplified graph, ie. tell the + // group anchors to set their children anchors sizes. + + // ### we calculated variables already a few times, can't we reuse that? + QList trunkVariables = getVariables(trunkConstraints); + + for (int i = 0; i < trunkVariables.count(); ++i) + trunkVariables.at(i)->updateChildrenSizes(); + // Calculate and set the preferred size for the layout from the edge sizes that // were calculated above. qreal pref(0.0); @@ -1198,6 +1207,9 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( ad->sizeAtPreferred = ad->prefSize; ad->sizeAtMaximum = ad->maxSize; + // Propagate + ad->updateChildrenSizes(); + sizeHints[orientation][Qt::MinimumSize] = ad->sizeAtMinimum; sizeHints[orientation][Qt::PreferredSize] = ad->sizeAtPreferred; sizeHints[orientation][Qt::MaximumSize] = ad->sizeAtMaximum; @@ -1228,6 +1240,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( Q_ASSERT(ad); ad->sizeAtMinimum = ad->sizeAtPreferred; ad->sizeAtMaximum = ad->sizeAtPreferred; + ad->updateChildrenSizes(); } // Delete the constraints, we won't use them anymore. @@ -1240,51 +1253,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( qDeleteAll(constraints[orientation]); constraints[orientation].clear(); graphPaths[orientation].clear(); // ### - - // Propagate the new sizes down the simplified graph, ie. tell the group anchors - // to set their children anchors sizes. - - // ### Note that we can split the anchors into two categories: - // 1) Those that appeared in at least one constraint and so went through the - // Simplex solver. Either as a Trunk or Non-Trunk variable. - // 2) Those that did not go through the Simplex solver at all. - // - // Anchors of the type (1) had its effective sizes (ie. sizeAtMinimum/Pref/Max) - // properly set by the "solveMinMax" and "solvePreferred" methods. - // - // However, those of type (2), still need to have their effective sizes set, - // in that case, to their own nominal values. - // - // Due to the above reasons, we can't simply iterate on the variables that - // belong to a graph part. We have to iterate through _all_ root anchors - // in graph[orientation]. That's why we collect "allAnchors". We gotta make - // this better somehow. - - // ### Did I say that's ugly? - QSet allAnchors; - QQueue queue; - queue << graph[orientation].rootVertex(); - while (!queue.isEmpty()) { - AnchorVertex *vertex = queue.dequeue(); - QList adjacentVertices = graph[orientation].adjacentVertices(vertex); - for (int i = 0; i < adjacentVertices.count(); ++i) { - AnchorData *edge = graph[orientation].edgeData(vertex, adjacentVertices[i]); - if (allAnchors.contains(edge)) - continue; - allAnchors << edge; - queue << adjacentVertices[i]; - } - } - - // Ok, now that we have all anchors, actually propagate the sizes down its children. - // Note that for anchors that didn't have its effectiveSizes set yet, we use the - // nominal one instead. - QSet::const_iterator iter; - for (iter = allAnchors.constBegin(); iter != allAnchors.constEnd(); ++iter) - (*iter)->updateChildrenSizes(); - - // Restore the graph. See the ### note next to the simplifyGraph() call. - //restoreSimplifiedGraph(orientation); } void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromDefaults(Orientation orientation) -- cgit v0.12 From f95dfb70e5c963b5c2502b94c2c391994b0e17ee Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 13 Aug 2009 15:36:29 -0300 Subject: QGraphicsAnchorLayout: remove unused function The function setAnchorSizeHintsFromDefaults() is not necessary, since a more updated version (ready or almost-ready for simplification) was added: setAnchorSizeHintsFromItems(). Signed-off-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 30 ------------------------ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 1 - 2 files changed, 31 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index e75bf19..83ba3b5 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1121,9 +1121,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // 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 - //setAnchorSizeHintsFromDefaults(orientation); - // Simplify the graph // ### Ideally we would like to do that if, and only if, anchors had // been added or removed since the last time this method was called. @@ -1255,33 +1252,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( graphPaths[orientation].clear(); // ### } -void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromDefaults(Orientation orientation) -{ - Graph &g = graph[orientation]; - QSet setOfVertices = g.vertices(); - - for (QSet::const_iterator it = setOfVertices.begin(); it != setOfVertices.end(); ++it) { - AnchorVertex *v = *it; - QList adjacents = g.adjacentVertices(v); - for (int i = 0; i < adjacents.count(); ++i) { - AnchorVertex *v1 = adjacents.at(i); - AnchorData *data = g.edgeData(v, v1); - if (!data->hasSize) { - bool forward = data->origin == v; - if (forward) { - qreal s = effectiveSpacing(orientation); - data->minSize = s; - data->prefSize = s; - data->maxSize = s; - data->sizeAtMinimum = s; - data->sizeAtPreferred = s; - data->sizeAtMaximum = s; - } - } - } - } -} - /*! \internal diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 3b5f64b..fa79f6b 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -347,7 +347,6 @@ public: void calculateGraphs(); void calculateGraphs(Orientation orientation); void setAnchorSizeHintsFromItems(Orientation orientation); - void setAnchorSizeHintsFromDefaults(Orientation orientation); void findPaths(Orientation orientation); void constraintsFromPaths(Orientation orientation); QList constraintsFromSizeHints(const QList &anchors); -- cgit v0.12 From 37fd523c7e784b83e2c308218ac312122bd8b414 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 13 Aug 2009 15:51:37 -0300 Subject: QGraphicsAnchorLayout: adding small debug messages Useful for verifying whether Simplex is being triggered or not. Signed-off-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraph_p.h | 3 ++- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h index 3abbe3b..2666623 100644 --- a/src/gui/graphicsview/qgraph_p.h +++ b/src/gui/graphicsview/qgraph_p.h @@ -182,10 +182,11 @@ public: EdgeData *data = edgeData(v, v1); bool forward = data->origin == v; if (forward) { - edges += QString::fromAscii("%1->%2 [label=\"[%3,%4]\" dir=both color=\"#000000:#a0a0a0\"] \n") + edges += QString::fromAscii("%1->%2 [label=\"[%3,%4,%5]\" dir=both color=\"#000000:#a0a0a0\"] \n") .arg(v->toString()) .arg(v1->toString()) .arg(data->minSize) + .arg(data->prefSize) .arg(data->maxSize) ; } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 83ba3b5..de14c31 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1165,6 +1165,9 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( GraphPath trunkPath = graphPaths[orientation].value(v); if (!trunkConstraints.isEmpty()) { + qDebug("Simplex used for trunk of %s", + orientation == Horizontal ? "Horizontal" : "Vertical"); + // Solve min and max size hints for trunk QPair minMax = solveMinMax(trunkConstraints, trunkPath); sizeHints[orientation][Qt::MinimumSize] = minMax.first; @@ -1194,6 +1197,9 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( } sizeHints[orientation][Qt::PreferredSize] = pref; } else { + qDebug("Simplex NOT used for trunk of %s", + orientation == Horizontal ? "Horizontal" : "Vertical"); + // No Simplex is necessary because the path was simplified all the way to a single // anchor. Q_ASSERT(trunkPath.positives.count() == 1); -- cgit v0.12 From 834caef5e14358e019dc7a1e7dcbdd4f46eb14cd Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 13 Aug 2009 16:55:42 -0300 Subject: QGraphicsAnchorLayout: add a few more tests Adding tests that were helpful during debugging simplification - example: a replica of the anchorlayout example, which has a few interesting cases. - parallel2: a small pararell test case, subset of example. - snakeOppositeDirections: snake but with connections in the opposite directions (this will end up with anchors with opposite directions). - fairDistributionOppositeDirections: same as before, but in the end, this should be taken care by our API which makes internally the same as fairDistribution. Some work on adding QCOMPARE() statements may still be needed for this examples. Note that is interesting to notice (maybe we can make this available for the test checking automatically) whether the simplex is being used or not when simplification is active. Signed-off-by: Caio Marcelo de Oliveira Filho --- .../tst_qgraphicsanchorlayout.cpp | 266 +++++++++++++++++++++ 1 file changed, 266 insertions(+) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 10b55d7..8b46685 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -11,8 +11,12 @@ private slots: void simple(); void diagonal(); void parallel(); + void parallel2(); void snake(); + void snakeOppositeDirections(); void fairDistribution(); + void fairDistributionOppositeDirections(); + void example(); }; class RectWidget : public QGraphicsWidget @@ -233,6 +237,50 @@ void tst_QGraphicsAnchorLayout::parallel() QCOMPARE(p.size(), layoutMaximumSize); } +void tst_QGraphicsAnchorLayout::parallel2() +{ + QGraphicsWidget *a = createItem(QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0), + QSizeF(200.0, 100.0)); + + QGraphicsWidget *b = createItem(QSizeF(100.0, 100.0), + QSizeF(150.0, 100.0), + QSizeF(190.0, 100.0)); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); + l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); + l->anchor(b, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + + l->anchorWidth(l, a); + l->anchor(l, QGraphicsAnchorLayout::Left, b, QGraphicsAnchorLayout::Left, 0); + l->anchor(b, QGraphicsAnchorLayout::Right, a, QGraphicsAnchorLayout::Right, 0); + + QCOMPARE(l->count(), 2); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + + QCOMPARE(layoutMinimumSize, QSizeF(100.0, 200.0)); + QCOMPARE(layoutPreferredSize, QSizeF(150.0, 200.0)); + QCOMPARE(layoutMaximumSize, QSizeF(190.0, 200.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(p.size(), layoutMaximumSize); +} + void tst_QGraphicsAnchorLayout::snake() { QGraphicsWidget *a = createItem(QSizeF(50.0, 100.0), @@ -292,6 +340,68 @@ void tst_QGraphicsAnchorLayout::snake() QCOMPARE(p.size(), layoutMaximumSize); } +void tst_QGraphicsAnchorLayout::snakeOppositeDirections() +{ + QGraphicsWidget *a = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0)); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(20.0, 100.0), + QSizeF(40.0, 100.0)); + + QGraphicsWidget *c = createItem(QSizeF(50.0, 100.0), + QSizeF(70.0, 100.0), + QSizeF(100.0, 100.0)); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); + l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); + l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); + + // Both a and c are 'pointing' to b + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Right, 0); + l->anchor(c, QGraphicsAnchorLayout::Left, b, QGraphicsAnchorLayout::Left, 0); + + l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + + QCOMPARE(l->count(), 3); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(60.0, 300.0)); + QCOMPARE(layoutPreferredSize, QSizeF(120.0, 300.0)); + QCOMPARE(layoutMaximumSize, QSizeF(190.0, 300.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 50.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(10.0, 100.0, 40.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(10.0, 200.0, 50.0, 100.0)); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 70.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(50.0, 100.0, 20.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(50.0, 200.0, 70.0, 100.0)); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 100.0, 100.0)); + QCOMPARE(b->geometry(), QRectF(90.0, 100.0, 10.0, 100.0)); + QCOMPARE(c->geometry(), QRectF(90.0, 200.0, 100.0, 100.0)); + QCOMPARE(p.size(), layoutMaximumSize); +} + void tst_QGraphicsAnchorLayout::fairDistribution() { QGraphicsWidget *a = createItem(QSizeF(10.0, 100.0), @@ -362,6 +472,162 @@ void tst_QGraphicsAnchorLayout::fairDistribution() QCOMPARE(p.size(), layoutMaximumSize); } +void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() +{ + QGraphicsWidget *a = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0)); + + QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0)); + + QGraphicsWidget *c = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0)); + + QGraphicsWidget *d = createItem(QSizeF(10.0, 100.0), + QSizeF(50.0, 100.0), + QSizeF(100.0, 100.0)); + + QGraphicsWidget *e = createItem(QSizeF(60.0, 100.0), + QSizeF(220.0, 100.0), + QSizeF(600.0, 100.0)); + + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); + l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); + l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); + l->anchor(d, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top, 0); + l->anchor(e, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + + l->anchor(a, QGraphicsAnchorLayout::Left, l, QGraphicsAnchorLayout::Left, 0); + l->anchor(b, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Right, 0); + l->anchor(c, QGraphicsAnchorLayout::Left, b, QGraphicsAnchorLayout::Right, 0); + l->anchor(d, QGraphicsAnchorLayout::Left, c, QGraphicsAnchorLayout::Right, 0); + l->anchor(d, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->anchorWidth(l, e, 0); + + QCOMPARE(l->count(), 5); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(60.0, 500.0)); + QCOMPARE(layoutPreferredSize, QSizeF(220.0, 500.0)); + QCOMPARE(layoutMaximumSize, QSizeF(400.0, 500.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(a->size(), b->size()); + QCOMPARE(a->size(), c->size()); + QCOMPARE(a->size(), d->size()); + QCOMPARE(e->size().width(), 4 * a->size().width()); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(a->size(), b->size()); + QCOMPARE(a->size(), c->size()); + QCOMPARE(a->size(), d->size()); + QCOMPARE(e->size().width(), 4 * a->size().width()); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(a->size(), b->size()); + QCOMPARE(a->size(), c->size()); + QCOMPARE(a->size(), d->size()); + QCOMPARE(e->size().width(), 4 * a->size().width()); + QCOMPARE(p.size(), layoutMaximumSize); +} + +void tst_QGraphicsAnchorLayout::example() +{ + QSizeF min(30, 100); + QSizeF pref(210, 100); + QSizeF max(300, 100); + + QGraphicsWidget *a = createItem(min, pref, max); + QGraphicsWidget *b = createItem(min, pref, max); + QGraphicsWidget *c = createItem(min, pref, max); + QGraphicsWidget *d = createItem(min, pref, max); + QGraphicsWidget *e = createItem(min, pref, max); + QGraphicsWidget *f = createItem(QSizeF(30, 50), QSizeF(150, 50), max); + QGraphicsWidget *g = createItem(QSizeF(30, 50), QSizeF(30, 100), max); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + // vertical + l->anchor(a, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top, 0); + l->anchor(b, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top, 0); + + l->anchor(c, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Bottom, 0); + l->anchor(c, QGraphicsAnchorLayout::Top, b, QGraphicsAnchorLayout::Bottom, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top, 0); + + l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + l->anchor(e, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + + l->anchor(c, QGraphicsAnchorLayout::Top, f, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::VCenter, f, QGraphicsAnchorLayout::Bottom, 0); + l->anchor(f, QGraphicsAnchorLayout::Bottom, g, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, g, QGraphicsAnchorLayout::Bottom, 0); + + // horizontal + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); + l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left, 0); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left, 0); + + l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left, 0); + l->anchor(c, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); + + l->anchor(b, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->anchor(e, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->anchor(d, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); + + l->anchor(l, QGraphicsAnchorLayout::Left, f, QGraphicsAnchorLayout::Left, 0); + l->anchor(l, QGraphicsAnchorLayout::Left, g, QGraphicsAnchorLayout::Left, 0); + l->anchor(f, QGraphicsAnchorLayout::Right, g, QGraphicsAnchorLayout::Right, 0); + + QCOMPARE(l->count(), 7); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + + QCOMPARE(layoutMinimumSize, QSizeF(90.0, 300.0)); + QCOMPARE(layoutPreferredSize, QSizeF(510.0, 300.0)); + QCOMPARE(layoutMaximumSize, QSizeF(570.0, 300.0)); + + p.resize(layoutMinimumSize); + QCOMPARE(p.size(), layoutMinimumSize); + QCOMPARE(a->size(), e->size()); + QCOMPARE(b->size(), d->size()); + QCOMPARE(f->size(), g->size()); + + p.resize(layoutPreferredSize); + QCOMPARE(p.size(), layoutPreferredSize); + QCOMPARE(a->size(), e->size()); + QCOMPARE(b->size(), d->size()); + QCOMPARE(f->size(), g->size()); + + p.resize(layoutMaximumSize); + QCOMPARE(p.size(), layoutMaximumSize); + QCOMPARE(a->size(), e->size()); + QCOMPARE(b->size(), d->size()); + QCOMPARE(f->size(), g->size()); +} QTEST_MAIN(tst_QGraphicsAnchorLayout) #include "tst_qgraphicsanchorlayout.moc" -- cgit v0.12 From fd8c247f5fcf56c2dfe65c6dc88e6cc39ae1ea44 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 13 Aug 2009 17:05:15 -0300 Subject: QGraphicsAnchorLayout: enable simplification Enable simplification by default. Allow disable during runtime by using the environment variable QT_ANCHORLAYOUT_NO_SIMPLIFICATION=1 when running the code. Signed-off-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index de14c31..09fe1b9 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1127,7 +1127,9 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // However, as the two setAnchorSizeHints methods above are not // ready to be run on top of a simplified graph, we must simplify // and restore it every time we get here. - //simplifyGraph(orientation); + static bool simplification = qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty(); + if (simplification) + simplifyGraph(orientation); // Traverse all graph edges and store the possible paths to each vertex findPaths(orientation); @@ -1256,6 +1258,9 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( qDeleteAll(constraints[orientation]); constraints[orientation].clear(); graphPaths[orientation].clear(); // ### + + if (simplification) + restoreSimplifiedGraph(orientation); } /*! -- cgit v0.12 From 8014408e95b920b98214040a340f68750f38a53b Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 13 Aug 2009 18:04:50 -0300 Subject: QGraphicsAnchorLayout: implement "deep" simplification Re-start the simplification procedure every time there's a change it the adjacent count of a vertex -- which happens when a new parallel anchor is created. The original algorithm stopped some simplifications too early, demanding the simplex unnecessarily. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 39 ++++++++++++++++++++---- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 2 ++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 09fe1b9..73fc025 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -218,7 +218,7 @@ inline static qreal checkAdd(qreal a, qreal b) * anchors between \a before and \a after, and can be restored back to the anchors it is a * placeholder for. */ -static void simplifySequentialChunk(Graph *graph, +static bool simplifySequentialChunk(Graph *graph, AnchorVertex *before, const QVector &vertices, AnchorVertex *after) @@ -281,6 +281,23 @@ static void simplifySequentialChunk(Graph *graph, newAnchor->sizeAtMaximum = pref; } graph->createEdge(before, after, newAnchor); + + // True if we created a parallel anchor + return newAnchor != sequence; +} + +void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) +{ + AnchorVertex *rootVertex = graph[orientation].rootVertex(); + + if (!rootVertex) + return; + + bool dirty; + int count = 0; + do { + dirty = simplifyGraphIteration(orientation); + } while (dirty); } /*! @@ -312,15 +329,13 @@ static void simplifySequentialChunk(Graph *graph, * sequence. This is ok, but that sequence should not be affected by stretch factors. * */ -void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::Orientation orientation) +bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutPrivate::Orientation orientation) { Q_Q(QGraphicsAnchorLayout); Graph &g = graph[orientation]; AnchorVertex *v = g.rootVertex(); - if (!v) - return; - QSet visited; + QSet visited; QStack stack; stack.push(v); QVector candidates; @@ -328,6 +343,8 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O const QGraphicsAnchorLayout::Edge centerEdge = pickEdge(QGraphicsAnchorLayout::HCenter, orientation); const QGraphicsAnchorLayout::Edge layoutEdge = oppositeEdge(v->m_edge); + bool dirty = false; + // walk depth-first. while (!stack.isEmpty()) { v = stack.pop(); @@ -422,7 +439,10 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O subCandidates.prepend(candidates.at(intervalFrom - 1)); } while (intervalFrom < intervalTo - 1); } - simplifySequentialChunk(&g, intervalVertexFrom, subCandidates, intervalVertexTo); + if (simplifySequentialChunk(&g, intervalVertexFrom, subCandidates, intervalVertexTo)) { + dirty = true; + break; + } // finished simplification of chunk with same direction } if (forward == (prev == data->origin)) @@ -433,7 +453,11 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O } prev = v1; } + + if (dirty) + break; } + if (endOfSequence) candidates.clear(); @@ -445,8 +469,11 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(QGraphicsAnchorLayoutPrivate::O continue; stack.push(next); } + visited.insert(v); } + + return dirty; } static void restoreSimplifiedAnchor(Graph &g, diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index fa79f6b..af58065 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -343,7 +343,9 @@ public: // Activation methods void simplifyGraph(Orientation orientation); + bool simplifyGraphIteration(Orientation orientation); void restoreSimplifiedGraph(Orientation orientation); + void calculateGraphs(); void calculateGraphs(Orientation orientation); void setAnchorSizeHintsFromItems(Orientation orientation); -- cgit v0.12 From ac4239eb5eb68ee18864378f643b5f38a3d07af1 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 13 Aug 2009 18:33:18 -0300 Subject: QGraphicsAnchorLayout: test for proportional distribution This test (which fails right now) check whether a sequential anchor is distributing proportionally the size between its parts. This should pass when we make calculateVertexPositions() work together with the simplified graph. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- .../tst_qgraphicsanchorlayout.cpp | 62 ++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 8b46685..cacb3a6 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -16,6 +16,7 @@ private slots: void snakeOppositeDirections(); void fairDistribution(); void fairDistributionOppositeDirections(); + void proportionalPreferred(); void example(); }; @@ -547,6 +548,67 @@ void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() QCOMPARE(p.size(), layoutMaximumSize); } +void tst_QGraphicsAnchorLayout::proportionalPreferred() +{ + QSizeF min(0, 100); + + QGraphicsWidget *a = createItem(min, QSizeF(10, 100), QSizeF(20, 100)); + QGraphicsWidget *b = createItem(min, QSizeF(20, 100), QSizeF(30, 100)); + QGraphicsWidget *c = createItem(min, QSizeF(14, 100), QSizeF(20, 100)); + QGraphicsWidget *d = createItem(min, QSizeF(10, 100), QSizeF(20, 100)); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->setContentsMargins(0, 0, 0, 0); + + l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); + l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); + l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top, 0); + l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); + l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + + l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); + l->anchor(l, QGraphicsAnchorLayout::Left, b, QGraphicsAnchorLayout::Left, 0); + l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left, 0); + l->anchor(a, QGraphicsAnchorLayout::Right, d, QGraphicsAnchorLayout::Left, 0); + l->anchor(b, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->anchor(d, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + + QCOMPARE(l->count(), 4); + + QGraphicsWidget p; + p.setLayout(l); + + QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); + QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); + QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); + + QCOMPARE(layoutMinimumSize, QSizeF(0, 400)); + QCOMPARE(layoutPreferredSize, QSizeF(24, 400)); + QCOMPARE(layoutMaximumSize, QSizeF(30, 400)); + + p.resize(layoutMinimumSize); + QCOMPARE(p.size(), layoutMinimumSize); + + p.resize(layoutPreferredSize); + QCOMPARE(c->size().width(), d->size().width()); + QCOMPARE(p.size(), layoutPreferredSize); + + p.resize(layoutMaximumSize); + QCOMPARE(p.size(), layoutMaximumSize); + + p.resize(QSizeF(12, 400)); + + // Proportionality between size given and preferred size, this + // should be respected in this graph for (a) and (b)|(c). + qreal factor = 12.0 / 24.0; + + QCOMPARE(c->size().width(), d->size().width()); + QCOMPARE(a->size().width() * factor, 10 * factor); + QCOMPARE(c->size().width() * factor, 14 * factor); + QCOMPARE(p.size(), QSizeF(12, 400)); +} + void tst_QGraphicsAnchorLayout::example() { QSizeF min(30, 100); -- cgit v0.12 From 23441f49a23cbf936b60140c5c8a6d5cb3ca00a7 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 13 Aug 2009 19:32:18 -0300 Subject: QGraphicsAnchorLayout: fix expected values for parallel test We changed the parallel example because the expected values for preferred size are wrong. Before we had a parallel anchor ("d|e") that should have the maximum preferred of the both children, so this end up being 170. This parallel is in a sequence with another item ("b") with a preferred of 150 ending up with 320. This group is in parallel with "c" that has a maximum of 300. So in the preferred size of the layout, the group was constraint to 300, which would be proportionally distributed between "b" and "d|e", and 50%/50% as the test expected. The proportional distribution is a feature and we changed the test to illustrate that more. We gave a bump in "c" maximum to 350 and "b" maximum to 300, so the preferred is 100% for both "b" and "d|e", and the proportional feature shows up in the maximum allocation, which is not 100% for the group (b and d|e). In the group, the maximum possible is 500 but the layout only allows 350, so the sizes need to be proportionally, but in relation not to zero, but to the preferred which is 320 (this is the same logic used for setGeometry()). So the growth is 30 / (500 - 320) = 30 / 180 = 1/6. This 1/6 should be the factor given to the children (b and d|e), taking their preferred sizes as starting points. Note that the test still fails, but serve as a milestone to see when the feature is correct implemented. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- .../tst_qgraphicsanchorlayout.cpp | 82 +++++++++++----------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index cacb3a6..530e51e 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -37,12 +37,14 @@ public: static QGraphicsWidget *createItem(const QSizeF &minimum = QSizeF(100.0, 100.0), const QSizeF &preferred = QSize(150.0, 100.0), - const QSizeF &maximum = QSizeF(200.0, 100.0)) + const QSizeF &maximum = QSizeF(200.0, 100.0), + const QString &name = QString()) { QGraphicsWidget *w = new RectWidget; w->setMinimumSize(minimum); w->setPreferredSize(preferred); w->setMaximumSize(maximum); + w->setData(0, name); return w; } @@ -149,29 +151,29 @@ void tst_QGraphicsAnchorLayout::diagonal() void tst_QGraphicsAnchorLayout::parallel() { - QGraphicsWidget *a = createItem(QSizeF(100.0, 100.0), - QSizeF(150.0, 100.0), - QSizeF(200.0, 100.0)); + QGraphicsWidget *a = createItem(QSizeF(100, 100), + QSizeF(150, 100), + QSizeF(200, 100), "A"); - QGraphicsWidget *b = createItem(QSizeF(100.0, 100.0), - QSizeF(150.0, 100.0), - QSizeF(200.0, 100.0)); + QGraphicsWidget *b = createItem(QSizeF(100, 100), + QSizeF(150, 100), + QSizeF(300, 100), "B"); - QGraphicsWidget *c = createItem(QSizeF(100.0, 100.0), - QSizeF(200.0, 100.0), - QSizeF(300.0, 100.0)); + QGraphicsWidget *c = createItem(QSizeF(100, 100), + QSizeF(200, 100), + QSizeF(350, 100), "C"); - QGraphicsWidget *d = createItem(QSizeF(100.0, 100.0), - QSizeF(170.0, 100.0), - QSizeF(200.0, 100.0)); + QGraphicsWidget *d = createItem(QSizeF(100, 100), + QSizeF(170, 100), + QSizeF(200, 100), "D"); - QGraphicsWidget *e = createItem(QSizeF(150.0, 100.0), - QSizeF(150.0, 100.0), - QSizeF(200.0, 100.0)); + QGraphicsWidget *e = createItem(QSizeF(150, 100), + QSizeF(150, 100), + QSizeF(200, 100), "E"); - QGraphicsWidget *f = createItem(QSizeF(100.0, 100.0), - QSizeF(150.0, 100.0), - QSizeF(200.0, 100.0)); + QGraphicsWidget *f = createItem(QSizeF(100, 100), + QSizeF(150, 100), + QSizeF(200, 100), "F"); QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); @@ -203,38 +205,38 @@ void tst_QGraphicsAnchorLayout::parallel() QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); - QCOMPARE(layoutMinimumSize, QSizeF(450.0, 600.0)); - QCOMPARE(layoutPreferredSize, QSizeF(600.0, 600.0)); - QCOMPARE(layoutMaximumSize, QSizeF(700.0, 600.0)); + QCOMPARE(layoutMinimumSize, QSizeF(450, 600)); + QCOMPARE(layoutPreferredSize, QSizeF(620, 600)); + QCOMPARE(layoutMaximumSize, QSizeF(750, 600)); p.resize(layoutMinimumSize); - QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 100.0, 100.0)); - QCOMPARE(b->geometry(), QRectF(100.0, 100.0, 100.0, 100.0)); - QCOMPARE(c->geometry(), QRectF(100.0, 200.0, 250.0, 100.0)); - QCOMPARE(d->geometry(), QRectF(200.0, 300.0, 150.0, 100.0)); - QCOMPARE(e->geometry(), QRectF(200.0, 400.0, 150.0, 100.0)); - QCOMPARE(f->geometry(), QRectF(350.0, 500.0, 100.0, 100.0)); + QCOMPARE(a->geometry(), QRectF(0, 0, 100, 100)); + QCOMPARE(b->geometry(), QRectF(100, 100, 100, 100)); + QCOMPARE(c->geometry(), QRectF(100, 200, 250, 100)); + QCOMPARE(d->geometry(), QRectF(200, 300, 150, 100)); + QCOMPARE(e->geometry(), QRectF(200, 400, 150, 100)); + QCOMPARE(f->geometry(), QRectF(350, 500, 100, 100)); QCOMPARE(p.size(), layoutMinimumSize); p.resize(layoutPreferredSize); - QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 150.0, 100.0)); - QCOMPARE(b->geometry(), QRectF(150.0, 100.0, 150.0, 100.0)); - QCOMPARE(c->geometry(), QRectF(150.0, 200.0, 300.0, 100.0)); - QCOMPARE(d->geometry(), QRectF(300.0, 300.0, 150.0, 100.0)); - QCOMPARE(e->geometry(), QRectF(300.0, 400.0, 150.0, 100.0)); - QCOMPARE(f->geometry(), QRectF(450.0, 500.0, 150.0, 100.0)); + QCOMPARE(a->geometry(), QRectF(0, 0, 150, 100)); + QCOMPARE(b->geometry(), QRectF(150, 100, 150, 100)); + QCOMPARE(c->geometry(), QRectF(150, 200, 320, 100)); + QCOMPARE(d->geometry(), QRectF(300, 300, 170, 100)); + QCOMPARE(e->geometry(), QRectF(300, 400, 170, 100)); + QCOMPARE(f->geometry(), QRectF(470, 500, 150, 100)); QCOMPARE(p.size(), layoutPreferredSize); // Maximum size depends on simplification / fair distribution // Without that, test may or may not pass, depending on the // solution found by the solver at runtime. p.resize(layoutMaximumSize); - QCOMPARE(a->geometry(), QRectF(0.0, 0.0, 200.0, 100.0)); - QCOMPARE(b->geometry(), QRectF(200.0, 100.0, 150.0, 100.0)); - QCOMPARE(c->geometry(), QRectF(200.0, 200.0, 300.0, 100.0)); - QCOMPARE(d->geometry(), QRectF(350.0, 300.0, 150.0, 100.0)); - QCOMPARE(e->geometry(), QRectF(350.0, 400.0, 150.0, 100.0)); - QCOMPARE(f->geometry(), QRectF(500.0, 500.0, 200.0, 100.0)); + QCOMPARE(a->geometry(), QRectF(0, 0, 200, 100)); + QCOMPARE(b->geometry(), QRectF(200, 100, 175, 100)); + QCOMPARE(c->geometry(), QRectF(200, 200, 350, 100)); + QCOMPARE(d->geometry(), QRectF(375, 300, 175, 100)); + QCOMPARE(e->geometry(), QRectF(375, 400, 175, 100)); + QCOMPARE(f->geometry(), QRectF(550, 500, 200, 100)); QCOMPARE(p.size(), layoutMaximumSize); } -- cgit v0.12 From fcd2540c1e5190b9edaebc6842263630a5d2da2b Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Thu, 13 Aug 2009 20:53:48 -0300 Subject: QGraphicsAnchorLayout: prepare ground for doing less simplifications Add variables to track whether the graph is simplified or not, as well as make the functions that need it simplified/restored call the corresponding functions. The functions look at the state variables, so it's safe to just call them. Also added Q_ASSERT() through the code paths that SHOULD have the full graph (not simplified), for making easy to track bugs in future refactorings. Ironically this commit cause more calls to simplify/restore right now, but the state will be better when we fix some functions to be simplification compatible ;-) Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 10 ++++ src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 59 ++++++++++++++++++------ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 3 ++ 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 902f0b2..9846988 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -221,6 +221,12 @@ qreal QGraphicsAnchorLayout::spacing(Qt::Orientation orientation) const void QGraphicsAnchorLayout::setGeometry(const QRectF &geom) { Q_D(QGraphicsAnchorLayout); + + // ### REMOVE IT WHEN calculateVertexPositions and setItemsGeometries are + // simplification compatible! + d->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Horizontal); + d->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Vertical); + QGraphicsLayout::setGeometry(geom); d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Horizontal); d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Vertical); @@ -235,6 +241,10 @@ void QGraphicsAnchorLayout::removeAt(int index) if (!item) return; + // Removing an item affects both horizontal and vertical graphs + d->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Horizontal); + d->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Vertical); + d->removeCenterConstraints(item, QGraphicsAnchorLayoutPrivate::Horizontal); d->removeCenterConstraints(item, QGraphicsAnchorLayoutPrivate::Vertical); d->removeAnchors(item); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 73fc025..75e40c4 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -168,8 +168,10 @@ QString GraphPath::toString() const QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate() : calculateGraphCacheDirty(1) { - for (int i = 0; i < NOrientations; ++i) + for (int i = 0; i < NOrientations; ++i) { spacing[i] = -1; + graphSimplified[i] = false; + } } QGraphicsAnchorLayout::Edge QGraphicsAnchorLayoutPrivate::oppositeEdge( @@ -288,6 +290,13 @@ static bool simplifySequentialChunk(Graph *graph, void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) { + if (graphSimplified[orientation]) + return; + graphSimplified[orientation] = true; + + qDebug("Simplifying Graph for %s", + orientation == Horizontal ? "Horizontal" : "Vertical"); + AnchorVertex *rootVertex = graph[orientation].rootVertex(); if (!rootVertex) @@ -523,6 +532,13 @@ static void restoreSimplifiedAnchor(Graph &g, void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientation) { + if (!graphSimplified[orientation]) + return; + graphSimplified[orientation] = false; + + qDebug("Restoring Simplified Graph for %s", + orientation == Horizontal ? "Horizontal" : "Vertical"); + Q_Q(QGraphicsAnchorLayout); Graph &g = graph[orientation]; @@ -593,6 +609,8 @@ void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges() void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) { + Q_ASSERT(!graphSimplified[Horizontal] && !graphSimplified[Vertical]); + items.append(item); // Horizontal @@ -641,6 +659,8 @@ void QGraphicsAnchorLayoutPrivate::createCenterAnchors( return; } + Q_ASSERT(!graphSimplified[orientation]); + // Check if vertex already exists if (internalVertex(item, centerEdge)) return; @@ -700,6 +720,8 @@ void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( return; } + Q_ASSERT(!graphSimplified[orientation]); + // Orientation code QGraphicsAnchorLayout::Edge firstEdge; QGraphicsAnchorLayout::Edge lastEdge; @@ -770,6 +792,8 @@ void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation) { + Q_ASSERT(!graphSimplified[orientation]); + // Remove the item center constraints associated to this item // ### This is a temporary solution. We should probably use a better // data structure to hold items and/or their associated constraints @@ -829,6 +853,10 @@ void QGraphicsAnchorLayoutPrivate::anchor(QGraphicsLayoutItem *firstItem, return; } + // Guarantee that the graph is no simplified when adding this anchor, + // anchor manipulation always happen in the full graph + restoreSimplifiedGraph(edgeOrientation(firstEdge)); + // In QGraphicsAnchorLayout, items are represented in its internal // graph as four anchors that connect: // - Left -> HCenter @@ -887,6 +915,10 @@ void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, QGraphicsAnchorLayout::Edge secondEdge, AnchorData *data) { + // Guarantee that the graph is no simplified when adding this anchor, + // anchor manipulation always happen in the full graph + restoreSimplifiedGraph(edgeOrientation(firstEdge)); + // Is the Vertex (firstItem, firstEdge) already represented in our // internal structure? AnchorVertex *v1 = addInternalVertex(firstItem, firstEdge); @@ -960,6 +992,10 @@ void QGraphicsAnchorLayoutPrivate::removeAnchor(QGraphicsLayoutItem *firstItem, QGraphicsLayoutItem *secondItem, QGraphicsAnchorLayout::Edge secondEdge) { + // Guarantee that the graph is no simplified when adding this anchor, + // anchor manipulation always happen in the full graph + restoreSimplifiedGraph(edgeOrientation(firstEdge)); + // Look for both vertices AnchorVertex *v1 = internalVertex(firstItem, firstEdge); AnchorVertex *v2 = internalVertex(secondItem, secondEdge); @@ -990,6 +1026,8 @@ void QGraphicsAnchorLayoutPrivate::removeVertex(QGraphicsLayoutItem *item, QGrap void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item) { + Q_ASSERT(!graphSimplified[Horizontal] && !graphSimplified[Vertical]); + // remove the center anchor first!! removeCenterAnchors(item, QGraphicsAnchorLayout::HCenter, false); removeVertex(item, QGraphicsAnchorLayout::Left); @@ -1090,14 +1128,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs() if (!calculateGraphCacheDirty) return; - //simplifyGraph(Horizontal); - //simplifyGraph(Vertical); - //Q_Q(QGraphicsAnchorLayout); - //q->dumpGraph(); - //restoreSimplifiedGraph(Horizontal); // should not be here, but currently crashes if not - //restoreSimplifiedGraph(Vertical); // should not be here, but currently crashes if not - - calculateGraphs(Horizontal); calculateGraphs(Vertical); @@ -1145,6 +1175,10 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( { Q_Q(QGraphicsAnchorLayout); + // ### REMOVE IT WHEN calculateVertexPositions and setItemsGeometries are + // simplification compatible! + restoreSimplifiedGraph(orientation); + // Reset the nominal sizes of each anchor based on the current item sizes setAnchorSizeHintsFromItems(orientation); @@ -1154,9 +1188,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // However, as the two setAnchorSizeHints methods above are not // ready to be run on top of a simplified graph, we must simplify // and restore it every time we get here. - static bool simplification = qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty(); - if (simplification) - simplifyGraph(orientation); + simplifyGraph(orientation); // Traverse all graph edges and store the possible paths to each vertex findPaths(orientation); @@ -1285,9 +1317,6 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( qDeleteAll(constraints[orientation]); constraints[orientation].clear(); graphPaths[orientation].clear(); // ### - - if (simplification) - restoreSimplifiedGraph(orientation); } /*! diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index af58065..21a4a3f 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -403,5 +403,8 @@ public: Interval interpolationInterval[2]; qreal interpolationProgress[2]; + // ### + bool graphSimplified[2]; + uint calculateGraphCacheDirty : 1; }; -- cgit v0.12 From 49286394bcb4e7c945c9d05d4b0a4f93a8a4b72f Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Fri, 14 Aug 2009 01:15:24 -0300 Subject: QGraphicsAnchorLayout: size hint updated in a simplified graph This commit makes setAnchorSizeHintsFromItems() support a simplified graph. Instead of going recursively, since AnchorData doesn't have all needed information, we make a list with all anchors/vertexpairs with dependencies first. That way we are sure to fill all information, and the group anchors can peek at its children anchors (dependencies) and they'll have updated information. This solutions doesn't require refreshSizeHints(). Alternative solutions that we might consider are: complementing the AnchorData information or making refreshSizeHints() take two AnchorVertex as parameters and use the recursive approach. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 314 ++++++++++++++--------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 5 - 2 files changed, 196 insertions(+), 123 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 75e40c4..9989e83 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -55,26 +55,6 @@ void ParallelAnchorData::updateChildrenSizes() secondEdge->updateChildrenSizes(); } -void ParallelAnchorData::refreshSizeHints() -{ - // ### 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. - - firstEdge->refreshSizeHints(); - secondEdge->refreshSizeHints(); - - minSize = qMax(firstEdge->minSize, secondEdge->minSize); - maxSize = qMin(firstEdge->maxSize, secondEdge->maxSize); - - prefSize = qMax(firstEdge->prefSize, secondEdge->prefSize); - prefSize = qMin(prefSize, maxSize); - - sizeAtMinimum = prefSize; - sizeAtPreferred = prefSize; - sizeAtMaximum = prefSize; -} - void SequentialAnchorData::updateChildrenSizes() { qreal minFactor = sizeAtMinimum / minSize; @@ -89,24 +69,6 @@ void SequentialAnchorData::updateChildrenSizes() } } -void SequentialAnchorData::refreshSizeHints() -{ - minSize = 0; - prefSize = 0; - maxSize = 0; - for (int i = 0; i < m_edges.count(); ++i) { - AnchorData *edge = m_edges.at(i); - edge->refreshSizeHints(); - minSize += edge->minSize; - prefSize += edge->prefSize; - maxSize += edge->maxSize; - } - - sizeAtMinimum = prefSize; - sizeAtPreferred = prefSize; - sizeAtMaximum = prefSize; -} - void AnchorData::dump(int indent) { if (type == Parallel) { qDebug("%*s type: parallel:", indent, ""); @@ -1175,21 +1137,12 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( { Q_Q(QGraphicsAnchorLayout); - // ### REMOVE IT WHEN calculateVertexPositions and setItemsGeometries are - // simplification compatible! - restoreSimplifiedGraph(orientation); + // Simplify the graph + simplifyGraph(orientation); // Reset the nominal sizes of each anchor based on the current item sizes setAnchorSizeHintsFromItems(orientation); - // Simplify the graph - // ### Ideally we would like to do that if, and only if, anchors had - // been added or removed since the last time this method was called. - // However, as the two setAnchorSizeHints methods above are not - // ready to be run on top of a simplified graph, we must simplify - // and restore it every time we get here. - simplifyGraph(orientation); - // Traverse all graph edges and store the possible paths to each vertex findPaths(orientation); @@ -1319,6 +1272,126 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( graphPaths[orientation].clear(); // ### } +// ### +static QPair >, QList > +getAnchorsDependenciesFirst(Graph &g) +{ + QList > conns(g.connections()); + + QStack > vertices; + QStack anchors; + + // Fill AnchorData to match vertices + for (int i = 0; i < conns.count(); ++i) { + QPair vertexPair = conns.at(i); + AnchorData *data = g.edgeData(vertexPair.first, vertexPair.second); + Q_ASSERT(data); + vertices.push(vertexPair); + anchors.push(data); + } + + QList > allVertices; + QList allAnchors; + + // Build list of all edges + while (!vertices.isEmpty()) { + QPair vertexPair = vertices.pop(); + AnchorData *data = anchors.pop(); + allVertices.prepend(vertexPair); + allAnchors.prepend(data); + + if (data->type == AnchorData::Parallel) { + ParallelAnchorData *p = static_cast(data); + + // Prepend dependencies so they are before the parent anchor in + // the result list + allVertices.prepend(vertexPair); + allAnchors.prepend(p->firstEdge); + allVertices.prepend(vertexPair); + allAnchors.prepend(p->secondEdge); + + // Push dependencies so they are 'recursively' processed + vertices.push(vertexPair); + anchors.push(p->firstEdge); + vertices.push(vertexPair); + anchors.push(p->secondEdge); + + } else if (data->type == AnchorData::Sequential) { + SequentialAnchorData *s = static_cast(data); + + AnchorVertex *prev = vertexPair.first; + AnchorVertex *last = vertexPair.second; + + if (s->origin != prev) + qSwap(last, prev); + + for (int i = 0; i < s->m_edges.count(); ++i) { + AnchorVertex *v1 = (i < s->m_children.count()) ? s->m_children.at(i) : last; + AnchorData *data = s->m_edges.at(i); + QPair pair(prev, v1); + + // Prepend dependencies in result list + allVertices.prepend(pair); + allAnchors.prepend(data); + + // Push dependencies in the stack so they are processed + vertices.push(pair); + anchors.push(data); + + prev = v1; + } + } + } + + return qMakePair(allVertices, allAnchors); +} + +static void setInternalAnchorSizeHint(AnchorVertex *from, AnchorVertex *to, + AnchorData *data, QGraphicsAnchorLayoutPrivate::Orientation orientation) +{ + qreal min; + qreal pref; + qreal max; + bool hasCenter = false; + QGraphicsLayoutItem *item = from->m_item; + + if (orientation == QGraphicsAnchorLayoutPrivate::Horizontal) { + min = item->minimumWidth(); + pref = item->preferredWidth(); + max = item->maximumWidth(); + hasCenter = (from->m_edge == QGraphicsAnchorLayout::HCenter + || to->m_edge == QGraphicsAnchorLayout::HCenter); + } else { + min = item->minimumHeight(); + pref = item->preferredHeight(); + max = item->maximumHeight(); + hasCenter = (from->m_edge == QGraphicsAnchorLayout::VCenter + || to->m_edge == QGraphicsAnchorLayout::VCenter); + } + + if (hasCenter) { + min /= 2; + pref /= 2; + max /= 2; + } + + // Set the anchor nominal sizes to those of the corresponding item + data->minSize = min; + data->prefSize = pref; + data->maxSize = max; + + // Set the anchor effective sizes to preferred. + // + // Note: The idea here is that all items should remain at their + // preferred size unless where that's impossible. In cases where + // the item is subject to restrictions (anchored to the layout + // edges, for instance), the simplex solver will be run to + // recalculate and override the values we set here. + data->sizeAtMinimum = pref; + data->sizeAtPreferred = pref; + data->sizeAtMaximum = pref; +} + /*! \internal @@ -1342,78 +1415,83 @@ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orien { Q_Q(QGraphicsAnchorLayout); Graph &g = graph[orientation]; - QList > conns = g.connections(); - QGraphicsAnchorLayout::Edge centerEdge = pickEdge(QGraphicsAnchorLayout::HCenter, orientation); - - QPair beginningKey; - QPair centerKey; - QPair endKey; - - if (orientation == Horizontal) { - beginningKey.second = QGraphicsAnchorLayout::Left; - centerKey.second = QGraphicsAnchorLayout::HCenter; - endKey.second = QGraphicsAnchorLayout::Right; - } else { - beginningKey.second = QGraphicsAnchorLayout::Top; - centerKey.second = QGraphicsAnchorLayout::VCenter; - endKey.second = QGraphicsAnchorLayout::Bottom; - } - - for (int i = 0; i < conns.count(); ++i) { - AnchorVertex *from = conns.at(i).first; - AnchorVertex *to = conns.at(i).second; - - QGraphicsLayoutItem *item = from->m_item; - qreal min, pref, max; - AnchorData *data = g.edgeData(from, to); - Q_ASSERT(data); - // Internal item anchor - if (item != q && item == to->m_item) { - // internal item anchor: get size from sizeHint - if (orientation == Horizontal) { - min = item->minimumWidth(); - pref = item->preferredWidth(); - max = item->maximumWidth(); - } else { - min = item->minimumHeight(); - pref = item->preferredHeight(); - max = item->maximumHeight(); + // First we build a list of edges (actually vertex pairs) in reverse + // order of dependency, so an item dependencies appear before the + // item. Then we traverse the list filling the size hint information. + // + // This two stage is necessary because the leaves themselves (AnchorData) + // doesn't have access to the pair of Anchor Vertices that they represent. + + QPair >, QList > all; + all = getAnchorsDependenciesFirst(g); + + QList > allVertices = all.first; + QList allAnchors = all.second; + + for (int i = 0; i < allAnchors.size(); ++i) { + AnchorVertex *from = allVertices.at(i).first; + AnchorVertex *to = allVertices.at(i).second; + AnchorData *data = allAnchors.at(i); + + // Internal anchor that is not the layout + if (from->m_item != q && from->m_item == to->m_item) { + // If you have two vertices from the same item, it must + // not be a complex anchor, since if a center was created, + // it must have at least 3 adjacent vertices (2 from the + // item, and 1 that 'created' it), so it's "un-simplifiable". + Q_ASSERT(data->type == AnchorData::Normal); + + setInternalAnchorSizeHint(from, to, data, orientation); + + } else if (data->type == AnchorData::Parallel) { + ParallelAnchorData *p = static_cast(data); + AnchorData *first = p->firstEdge; + AnchorData *second = p->secondEdge; + + // ### 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. + + p->minSize = qMax(first->minSize, second->minSize); + p->maxSize = qMin(first->maxSize, second->maxSize); + + p->prefSize = qMax(first->prefSize, second->prefSize); + p->prefSize = qMin(p->prefSize, p->maxSize); + + // See comment in setInternalAnchorSizeHint() about sizeAt* values + p->sizeAtMinimum = p->prefSize; + p->sizeAtPreferred = p->prefSize; + p->sizeAtMaximum = p->prefSize; + + } else if (data->type == AnchorData::Sequential) { + SequentialAnchorData *s = static_cast(data); + + s->minSize = 0; + s->prefSize = 0; + s->maxSize = 0; + + for (int i = 0; i < s->m_edges.count(); ++i) { + AnchorData *edge = s->m_edges.at(i); + s->minSize += edge->minSize; + s->prefSize += edge->prefSize; + s->maxSize += edge->maxSize; } - if (from->m_edge == centerEdge || to->m_edge == centerEdge) { - min /= 2; - pref /= 2; - max /= 2; - } - // Set the anchor nominal sizes to those of the corresponding item - data->minSize = min; - data->prefSize = pref; - data->maxSize = max; - - // Set the anchor effective sizes to preferred. - // Note: The idea here is that all items should remain at - // their preferred size unless where that's impossible. - // In cases where the item is subject to restrictions - // (anchored to the layout edges, for instance), the - // simplex solver will be run to recalculate and override - // the values we set here. - data->sizeAtMinimum = pref; - data->sizeAtPreferred = pref; - data->sizeAtMaximum = pref; - } else if (data->type != AnchorData::Normal) { - data->refreshSizeHints(); - } else { - // anchors between items, their sizes may depend on the style. - if (!data->hasSize) { - qreal s = effectiveSpacing(orientation); - data->minSize = s; - data->prefSize = s; - data->maxSize = s; - data->sizeAtMinimum = s; - data->sizeAtPreferred = s; - data->sizeAtMaximum = s; - } + // See comment in setInternalAnchorSizeHint() about sizeAt* values + s->sizeAtMinimum = s->prefSize; + s->sizeAtPreferred = s->prefSize; + s->sizeAtMaximum = s->prefSize; + + } else if (!data->hasSize) { + // Anchor has no data defined, look for style information + qreal s = effectiveSpacing(orientation); + data->minSize = s; + data->prefSize = s; + data->maxSize = s; + data->sizeAtMinimum = s; + data->sizeAtPreferred = s; + data->sizeAtMaximum = s; } } } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 21a4a3f..3951910 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -147,8 +147,6 @@ struct AnchorData : public QSimplexVariable { virtual void updateChildrenSizes() { }; - virtual void refreshSizeHints() { }; - void dump(int indent = 2); inline QString toString() const; @@ -199,7 +197,6 @@ struct SequentialAnchorData : public AnchorData } virtual void updateChildrenSizes(); - virtual void refreshSizeHints(); void setVertices(const QVector &vertices) { @@ -223,8 +220,6 @@ struct ParallelAnchorData : public AnchorData } virtual void updateChildrenSizes(); - virtual void refreshSizeHints(); - AnchorData* firstEdge; AnchorData* secondEdge; -- cgit v0.12 From 15f052a58cdd2c916d7194915dcd82912ba7097b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Fri, 14 Aug 2009 09:48:16 +0200 Subject: Added QEXPECT_FAIL, for tests that we must fix, but we know they fail. --- tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 530e51e..d94cd82 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -232,6 +232,7 @@ void tst_QGraphicsAnchorLayout::parallel() // solution found by the solver at runtime. p.resize(layoutMaximumSize); QCOMPARE(a->geometry(), QRectF(0, 0, 200, 100)); + QEXPECT_FAIL("", "see commit 23441f49a23cbf936b60140c5c8a6d5cb3ca00a7 for explanation", Abort); QCOMPARE(b->geometry(), QRectF(200, 100, 175, 100)); QCOMPARE(c->geometry(), QRectF(200, 200, 350, 100)); QCOMPARE(d->geometry(), QRectF(375, 300, 175, 100)); @@ -606,6 +607,7 @@ void tst_QGraphicsAnchorLayout::proportionalPreferred() qreal factor = 12.0 / 24.0; QCOMPARE(c->size().width(), d->size().width()); + QEXPECT_FAIL("", "see commit 23441f49a23cbf936b60140c5c8a6d5cb3ca00a7 for explanation", Abort); QCOMPARE(a->size().width() * factor, 10 * factor); QCOMPARE(c->size().width() * factor, 14 * factor); QCOMPARE(p.size(), QSizeF(12, 400)); @@ -678,6 +680,7 @@ void tst_QGraphicsAnchorLayout::example() QCOMPARE(p.size(), layoutMinimumSize); QCOMPARE(a->size(), e->size()); QCOMPARE(b->size(), d->size()); + QEXPECT_FAIL("", "please fix this test", Abort); QCOMPARE(f->size(), g->size()); p.resize(layoutPreferredSize); -- cgit v0.12 From 6bd756353d1be7eadcf56f9c8415283d0d1c4cc3 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Fri, 14 Aug 2009 16:02:36 -0300 Subject: QGraphicsAnchorLayout: add both vertex information in anchor data Make AnchorData aware of both vertices instead only one (the origin). This information is useful for many functions when working in simplified mode. Changed the name "origin" to "from", and add an "to" field. Also change setAnchorSizeHintsFromItems() and its helpers to make use of this information. In a later commit we will rollback to using recursion for initializing the size hint information. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraph_p.h | 2 +- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 64 ++++++++++++------------ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 23 ++++++--- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h index 2666623..6cb843f 100644 --- a/src/gui/graphicsview/qgraph_p.h +++ b/src/gui/graphicsview/qgraph_p.h @@ -180,7 +180,7 @@ public: for (int i = 0; i < adjacents.count(); ++i) { Vertex *v1 = adjacents.at(i); EdgeData *data = edgeData(v, v1); - bool forward = data->origin == v; + bool forward = data->from == v; if (forward) { edges += QString::fromAscii("%1->%2 [label=\"[%3,%4,%5]\" dir=both color=\"#000000:#a0a0a0\"] \n") .arg(v->toString()) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 9989e83..62e94a2 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -225,7 +225,16 @@ static bool simplifySequentialChunk(Graph *graph, sequence->sizeAtMaximum = pref; sequence->setVertices(vertices); - sequence->origin = data->origin == vertices.last() ? before : after; + + sequence->from = before; + sequence->to = after; + + // data here is the last edge in the sequence + // ### this seems to be here for supporting reverse order sequences, + // but doesnt seem to be used right now + if (data->from != vertices.last()) + qSwap(sequence->from, sequence->to); + AnchorData *newAnchor = sequence; if (AnchorData *oldAnchor = graph->takeEdge(before, after)) { newAnchor = new ParallelAnchorData(oldAnchor, sequence); @@ -362,7 +371,7 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP AnchorVertex *prev = beforeSequence; int intervalFrom = 0; - // Check for directionality (origin). We don't want to destroy that information, + // Check for directionality (from). We don't want to destroy that information, // thus we only combine anchors with the same direction. // "i" is the index *including* the beforeSequence and afterSequence vertices. @@ -372,10 +381,10 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP AnchorData *data = g.edgeData(prev, v1); Q_ASSERT(data); if (i == 1) { - forward = (prev == data->origin ? true : false); - } else if (forward != (prev == data->origin) || atVertexAfter) { + forward = (prev == data->from ? true : false); + } else if (forward != (prev == data->from) || atVertexAfter) { int intervalTo = i; - if (forward != (prev == data->origin)) + if (forward != (prev == data->from)) --intervalTo; // intervalFrom and intervalTo should now be indices to the vertex before and @@ -416,7 +425,7 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP } // finished simplification of chunk with same direction } - if (forward == (prev == data->origin)) + if (forward == (prev == data->from)) --intervalTo; intervalFrom = intervalTo; @@ -464,7 +473,7 @@ static void restoreSimplifiedAnchor(Graph &g, // restore the sequential anchor AnchorVertex *prev = before; AnchorVertex *last = after; - if (edge->origin != prev) + if (edge->from != prev) qSwap(last, prev); for (int i = 0; i < seqEdge->m_edges.count(); ++i) { @@ -894,7 +903,8 @@ void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, // 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. - data->origin = v1; + data->from = v1; + data->to = v2; data->name = QString::fromAscii("%1 --to--> %2").arg(v1->toString()).arg(v2->toString()); graph[edgeOrientation(firstEdge)].createEdge(v1, v2, data); @@ -1272,9 +1282,8 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( graphPaths[orientation].clear(); // ### } -// ### -static QPair >, QList > -getAnchorsDependenciesFirst(Graph &g) +// ### REMOVE ME: call recursion using children methods before +static QList getAnchorsDependenciesFirst(Graph &g) { QList > conns(g.connections()); @@ -1290,14 +1299,12 @@ getAnchorsDependenciesFirst(Graph &g) anchors.push(data); } - QList > allVertices; QList allAnchors; // Build list of all edges while (!vertices.isEmpty()) { QPair vertexPair = vertices.pop(); AnchorData *data = anchors.pop(); - allVertices.prepend(vertexPair); allAnchors.prepend(data); if (data->type == AnchorData::Parallel) { @@ -1305,15 +1312,11 @@ getAnchorsDependenciesFirst(Graph &g) // Prepend dependencies so they are before the parent anchor in // the result list - allVertices.prepend(vertexPair); allAnchors.prepend(p->firstEdge); - allVertices.prepend(vertexPair); allAnchors.prepend(p->secondEdge); // Push dependencies so they are 'recursively' processed - vertices.push(vertexPair); anchors.push(p->firstEdge); - vertices.push(vertexPair); anchors.push(p->secondEdge); } else if (data->type == AnchorData::Sequential) { @@ -1322,7 +1325,7 @@ getAnchorsDependenciesFirst(Graph &g) AnchorVertex *prev = vertexPair.first; AnchorVertex *last = vertexPair.second; - if (s->origin != prev) + if (s->from != prev) qSwap(last, prev); for (int i = 0; i < s->m_edges.count(); ++i) { @@ -1331,11 +1334,9 @@ getAnchorsDependenciesFirst(Graph &g) QPair pair(prev, v1); // Prepend dependencies in result list - allVertices.prepend(pair); allAnchors.prepend(data); // Push dependencies in the stack so they are processed - vertices.push(pair); anchors.push(data); prev = v1; @@ -1343,7 +1344,7 @@ getAnchorsDependenciesFirst(Graph &g) } } - return qMakePair(allVertices, allAnchors); + return allAnchors; } static void setInternalAnchorSizeHint(AnchorVertex *from, AnchorVertex *to, @@ -1420,19 +1421,16 @@ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orien // order of dependency, so an item dependencies appear before the // item. Then we traverse the list filling the size hint information. // - // This two stage is necessary because the leaves themselves (AnchorData) - // doesn't have access to the pair of Anchor Vertices that they represent. + // ### use recursion instead - QPair >, QList > all; - all = getAnchorsDependenciesFirst(g); - - QList > allVertices = all.first; - QList allAnchors = all.second; + QList allAnchors = getAnchorsDependenciesFirst(g); for (int i = 0; i < allAnchors.size(); ++i) { - AnchorVertex *from = allVertices.at(i).first; - AnchorVertex *to = allVertices.at(i).second; AnchorData *data = allAnchors.at(i); + AnchorVertex *from = data->from; + AnchorVertex *to = data->to; + + Q_ASSERT(from && to); // Internal anchor that is not the layout if (from->m_item != q && from->m_item == to->m_item) { @@ -1530,7 +1528,7 @@ void QGraphicsAnchorLayoutPrivate::findPaths(Orientation orientation) visited.insert(edge); GraphPath current = graphPaths[orientation].value(pair.first); - if (edge->origin == pair.first) + if (edge->from == pair.first) current.positives.insert(edge); else current.negatives.insert(edge); @@ -1774,7 +1772,7 @@ void QGraphicsAnchorLayoutPrivate::calculateVertexPositions( qreal distance; AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second); - if (edge->origin == pair.first) { + if (edge->from == pair.first) { distance = pair.first->distance + interpolateEdge(edge); } else { distance = pair.first->distance - interpolateEdge(edge); @@ -1841,7 +1839,7 @@ qreal QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorData *edge) { qreal lower, upper; - Orientation orientation = edgeOrientation(edge->origin->m_edge); + Orientation orientation = edgeOrientation(edge->from->m_edge); if (interpolationInterval[orientation] == MinToPreferred) { lower = edge->sizeAtMinimum; diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 3951910..eb1f8f6 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -130,18 +130,21 @@ struct AnchorData : public QSimplexVariable { Parallel }; AnchorData(qreal minimumSize, qreal preferredSize, qreal maximumSize) - : QSimplexVariable(), minSize(minimumSize), prefSize(preferredSize), + : QSimplexVariable(), from(0), to(0), + minSize(minimumSize), prefSize(preferredSize), maxSize(maximumSize), sizeAtMinimum(preferredSize), sizeAtPreferred(preferredSize), sizeAtMaximum(preferredSize), skipInPreferred(0), type(Normal), hasSize(true) {} AnchorData(qreal size) - : QSimplexVariable(), minSize(size), prefSize(size), maxSize(size), + : QSimplexVariable(), from(0), to(0), + minSize(size), prefSize(size), maxSize(size), sizeAtMinimum(size), sizeAtPreferred(size), sizeAtMaximum(size), skipInPreferred(0), type(Normal), hasSize(true) {} AnchorData() - : QSimplexVariable(), minSize(0), prefSize(0), maxSize(0), + : QSimplexVariable(), from(0), to(0), + minSize(0), prefSize(0), maxSize(0), sizeAtMinimum(0), sizeAtPreferred(0), sizeAtMaximum(0), skipInPreferred(0), type(Normal), hasSize(false) {} @@ -153,7 +156,8 @@ struct AnchorData : public QSimplexVariable { QString name; // Anchor is semantically directed - AnchorVertex *origin; + AnchorVertex *from; + AnchorVertex *to; // Size restrictions of this edge. For anchors internal to items, these // values are derived from the respective item size hints. For anchors @@ -175,7 +179,8 @@ struct AnchorData : public QSimplexVariable { uint hasSize : 1; // if false, get size from style. protected: AnchorData(Type type, qreal size = 0) - : QSimplexVariable(), minSize(size), prefSize(size), + : QSimplexVariable(), from(0), to(0), + minSize(size), prefSize(size), maxSize(size), sizeAtMinimum(size), sizeAtPreferred(size), sizeAtMaximum(size), skipInPreferred(0), type(type), hasSize(true) {} @@ -214,8 +219,12 @@ struct ParallelAnchorData : public AnchorData : AnchorData(AnchorData::Parallel), firstEdge(first), secondEdge(second) { - Q_ASSERT(first->origin == second->origin); - origin = first->origin; + // ### Those asserts force that both child anchors have the same direction, + // but can't we simplify a pair of anchors in opposite directions? + Q_ASSERT(first->from == second->from); + Q_ASSERT(first->to == second->to); + from = first->from; + to = first->to; name = QString::fromAscii("%1 | %2").arg(first->toString(), second->toString()); } -- cgit v0.12 From 916a74ef9703da783894c4a49b4897ef57cc474c Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Fri, 14 Aug 2009 18:02:21 -0300 Subject: QGraphicsAnchorLayout: use a recursive approach for size hints Bring back the refreshSizeHints() now that we have locally the necessary information to get the size hints given an arbitrary anchor data. We also created a bit to set whether the AnchorData is an anchor of a layout (i.e. connects the layout with something else) or not. This costs one pointer more in the AnchorData, but simplifies a good deal of code and avoid traversing the graph to get this information back, so I think it's worthwhile. Signed-off-by: Caio Marcelo de Oliveira Filho Reviewed-by: Artur Duque de Souza --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 314 +++++++++-------------- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 20 +- 2 files changed, 130 insertions(+), 204 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 62e94a2..d7bee19 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -45,6 +45,56 @@ #include "qgraphicsanchorlayout_p.h" +void AnchorData::refreshSizeHints(qreal effectiveSpacing) +{ + if (!isLayoutAnchor && from->m_item == to->m_item) { + bool hasCenter = false; + QGraphicsLayoutItem *item = from->m_item; + + if (QGraphicsAnchorLayoutPrivate::edgeOrientation(from->m_edge) + == QGraphicsAnchorLayoutPrivate::Horizontal) { + minSize = item->minimumWidth(); + prefSize = item->preferredWidth(); + maxSize = item->maximumWidth(); + hasCenter = (from->m_edge == QGraphicsAnchorLayout::HCenter + || to->m_edge == QGraphicsAnchorLayout::HCenter); + } else { + minSize = item->minimumHeight(); + prefSize = item->preferredHeight(); + maxSize = item->maximumHeight(); + hasCenter = (from->m_edge == QGraphicsAnchorLayout::VCenter + || to->m_edge == QGraphicsAnchorLayout::VCenter); + } + + if (hasCenter) { + minSize /= 2; + prefSize /= 2; + maxSize /= 2; + } + + // Set the anchor effective sizes to preferred. + // + // Note: The idea here is that all items should remain at their + // preferred size unless where that's impossible. In cases where + // the item is subject to restrictions (anchored to the layout + // edges, for instance), the simplex solver will be run to + // recalculate and override the values we set here. + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; + + } else if (!hasSize) { + // Anchor has no size defined, use given default information + minSize = effectiveSpacing; + prefSize = effectiveSpacing; + maxSize = effectiveSpacing; + + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; + } +} + void ParallelAnchorData::updateChildrenSizes() { firstEdge->sizeAtMinimum = secondEdge->sizeAtMinimum = sizeAtMinimum; @@ -55,6 +105,28 @@ void ParallelAnchorData::updateChildrenSizes() secondEdge->updateChildrenSizes(); } +void ParallelAnchorData::refreshSizeHints(qreal effectiveSpacing) +{ + // First refresh children information + firstEdge->refreshSizeHints(effectiveSpacing); + secondEdge->refreshSizeHints(effectiveSpacing); + + // ### 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); + + prefSize = qMax(firstEdge->prefSize, secondEdge->prefSize); + prefSize = qMin(prefSize, maxSize); + + // See comment in AnchorData::refreshSizeHints() about sizeAt* values + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; +} + void SequentialAnchorData::updateChildrenSizes() { qreal minFactor = sizeAtMinimum / minSize; @@ -69,6 +141,29 @@ void SequentialAnchorData::updateChildrenSizes() } } +void SequentialAnchorData::refreshSizeHints(qreal effectiveSpacing) +{ + minSize = 0; + prefSize = 0; + maxSize = 0; + + for (int i = 0; i < m_edges.count(); ++i) { + AnchorData *edge = m_edges.at(i); + + // First refresh children information + edge->refreshSizeHints(effectiveSpacing); + + minSize += edge->minSize; + prefSize += edge->prefSize; + maxSize += edge->maxSize; + } + + // See comment in AnchorData::refreshSizeHints() about sizeAt* values + sizeAtMinimum = prefSize; + sizeAtPreferred = prefSize; + sizeAtMaximum = prefSize; +} + void AnchorData::dump(int indent) { if (type == Parallel) { qDebug("%*s type: parallel:", indent, ""); @@ -235,9 +330,19 @@ static bool simplifySequentialChunk(Graph *graph, if (data->from != vertices.last()) qSwap(sequence->from, sequence->to); + // Note that since layout 'edges' can't be simplified away from + // the graph, it's safe to assume that if there's a layout + // 'edge', it'll be in the boundaries of the sequence. + sequence->isLayoutAnchor = (sequence->m_edges.first()->isLayoutAnchor + || sequence->m_edges.last()->isLayoutAnchor); + AnchorData *newAnchor = sequence; if (AnchorData *oldAnchor = graph->takeEdge(before, after)) { newAnchor = new ParallelAnchorData(oldAnchor, sequence); + + newAnchor->isLayoutAnchor = (oldAnchor->isLayoutAnchor + || sequence->isLayoutAnchor); + min = qMax(oldAnchor->minSize, sequence->minSize); max = qMin(oldAnchor->maxSize, sequence->maxSize); @@ -886,6 +991,8 @@ void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, QGraphicsAnchorLayout::Edge secondEdge, AnchorData *data) { + Q_Q(QGraphicsAnchorLayout); + // Guarantee that the graph is no simplified when adding this anchor, // anchor manipulation always happen in the full graph restoreSimplifiedGraph(edgeOrientation(firstEdge)); @@ -907,6 +1014,9 @@ void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, data->to = v2; data->name = QString::fromAscii("%1 --to--> %2").arg(v1->toString()).arg(v2->toString()); + // Keep track of anchors that are connected to the layout 'edges' + data->isLayoutAnchor = (v1->m_item == q || v2->m_item == q); + graph[edgeOrientation(firstEdge)].createEdge(v1, v2, data); } @@ -1282,215 +1392,23 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( graphPaths[orientation].clear(); // ### } -// ### REMOVE ME: call recursion using children methods before -static QList getAnchorsDependenciesFirst(Graph &g) -{ - QList > conns(g.connections()); - - QStack > vertices; - QStack anchors; - - // Fill AnchorData to match vertices - for (int i = 0; i < conns.count(); ++i) { - QPair vertexPair = conns.at(i); - AnchorData *data = g.edgeData(vertexPair.first, vertexPair.second); - Q_ASSERT(data); - vertices.push(vertexPair); - anchors.push(data); - } - - QList allAnchors; - - // Build list of all edges - while (!vertices.isEmpty()) { - QPair vertexPair = vertices.pop(); - AnchorData *data = anchors.pop(); - allAnchors.prepend(data); - - if (data->type == AnchorData::Parallel) { - ParallelAnchorData *p = static_cast(data); - - // Prepend dependencies so they are before the parent anchor in - // the result list - allAnchors.prepend(p->firstEdge); - allAnchors.prepend(p->secondEdge); - - // Push dependencies so they are 'recursively' processed - anchors.push(p->firstEdge); - anchors.push(p->secondEdge); - - } else if (data->type == AnchorData::Sequential) { - SequentialAnchorData *s = static_cast(data); - - AnchorVertex *prev = vertexPair.first; - AnchorVertex *last = vertexPair.second; - - if (s->from != prev) - qSwap(last, prev); - - for (int i = 0; i < s->m_edges.count(); ++i) { - AnchorVertex *v1 = (i < s->m_children.count()) ? s->m_children.at(i) : last; - AnchorData *data = s->m_edges.at(i); - QPair pair(prev, v1); - - // Prepend dependencies in result list - allAnchors.prepend(data); - - // Push dependencies in the stack so they are processed - anchors.push(data); - - prev = v1; - } - } - } - - return allAnchors; -} - -static void setInternalAnchorSizeHint(AnchorVertex *from, AnchorVertex *to, - AnchorData *data, QGraphicsAnchorLayoutPrivate::Orientation orientation) -{ - qreal min; - qreal pref; - qreal max; - bool hasCenter = false; - QGraphicsLayoutItem *item = from->m_item; - - if (orientation == QGraphicsAnchorLayoutPrivate::Horizontal) { - min = item->minimumWidth(); - pref = item->preferredWidth(); - max = item->maximumWidth(); - hasCenter = (from->m_edge == QGraphicsAnchorLayout::HCenter - || to->m_edge == QGraphicsAnchorLayout::HCenter); - } else { - min = item->minimumHeight(); - pref = item->preferredHeight(); - max = item->maximumHeight(); - hasCenter = (from->m_edge == QGraphicsAnchorLayout::VCenter - || to->m_edge == QGraphicsAnchorLayout::VCenter); - } - - if (hasCenter) { - min /= 2; - pref /= 2; - max /= 2; - } - - // Set the anchor nominal sizes to those of the corresponding item - data->minSize = min; - data->prefSize = pref; - data->maxSize = max; - - // Set the anchor effective sizes to preferred. - // - // Note: The idea here is that all items should remain at their - // preferred size unless where that's impossible. In cases where - // the item is subject to restrictions (anchored to the layout - // edges, for instance), the simplex solver will be run to - // recalculate and override the values we set here. - data->sizeAtMinimum = pref; - data->sizeAtPreferred = pref; - data->sizeAtMaximum = pref; -} - /*! \internal For graph edges ("anchors") that represent items, this method updates their intrinsic size restrictions, based on the item size hints. - - ################################################################################ - ### TODO: This method is not simplification ready. The fact that the graph may - have been simplified means that not all anchors exist in the graph. - This causes the Q_ASSERT(data) calls below to fail. - - A possible approach is to use a recursive method that goes through - all edges in the graph updating their sizes based on their kind, i.e.: - - Anchors -> update their size based on the style defaults - - ItemAnchors -> update their size based on the item sizeHints - - SimplificationAnchors -> call "update()" on its children and then - calculate its own sizes - ################################################################################ */ void QGraphicsAnchorLayoutPrivate::setAnchorSizeHintsFromItems(Orientation orientation) { - Q_Q(QGraphicsAnchorLayout); Graph &g = graph[orientation]; + QList > vertices = g.connections(); - // First we build a list of edges (actually vertex pairs) in reverse - // order of dependency, so an item dependencies appear before the - // item. Then we traverse the list filling the size hint information. - // - // ### use recursion instead - - QList allAnchors = getAnchorsDependenciesFirst(g); - - for (int i = 0; i < allAnchors.size(); ++i) { - AnchorData *data = allAnchors.at(i); - AnchorVertex *from = data->from; - AnchorVertex *to = data->to; - - Q_ASSERT(from && to); - - // Internal anchor that is not the layout - if (from->m_item != q && from->m_item == to->m_item) { - // If you have two vertices from the same item, it must - // not be a complex anchor, since if a center was created, - // it must have at least 3 adjacent vertices (2 from the - // item, and 1 that 'created' it), so it's "un-simplifiable". - Q_ASSERT(data->type == AnchorData::Normal); - - setInternalAnchorSizeHint(from, to, data, orientation); + qreal spacing = effectiveSpacing(orientation); - } else if (data->type == AnchorData::Parallel) { - ParallelAnchorData *p = static_cast(data); - AnchorData *first = p->firstEdge; - AnchorData *second = p->secondEdge; - - // ### 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. - - p->minSize = qMax(first->minSize, second->minSize); - p->maxSize = qMin(first->maxSize, second->maxSize); - - p->prefSize = qMax(first->prefSize, second->prefSize); - p->prefSize = qMin(p->prefSize, p->maxSize); - - // See comment in setInternalAnchorSizeHint() about sizeAt* values - p->sizeAtMinimum = p->prefSize; - p->sizeAtPreferred = p->prefSize; - p->sizeAtMaximum = p->prefSize; - - } else if (data->type == AnchorData::Sequential) { - SequentialAnchorData *s = static_cast(data); - - s->minSize = 0; - s->prefSize = 0; - s->maxSize = 0; - - for (int i = 0; i < s->m_edges.count(); ++i) { - AnchorData *edge = s->m_edges.at(i); - s->minSize += edge->minSize; - s->prefSize += edge->prefSize; - s->maxSize += edge->maxSize; - } - - // See comment in setInternalAnchorSizeHint() about sizeAt* values - s->sizeAtMinimum = s->prefSize; - s->sizeAtPreferred = s->prefSize; - s->sizeAtMaximum = s->prefSize; - - } else if (!data->hasSize) { - // Anchor has no data defined, look for style information - qreal s = effectiveSpacing(orientation); - data->minSize = s; - data->prefSize = s; - data->maxSize = s; - data->sizeAtMinimum = s; - data->sizeAtPreferred = s; - data->sizeAtMaximum = s; - } + 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); } } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index eb1f8f6..ce4eaf7 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -134,21 +134,25 @@ struct AnchorData : public QSimplexVariable { minSize(minimumSize), prefSize(preferredSize), maxSize(maximumSize), sizeAtMinimum(preferredSize), sizeAtPreferred(preferredSize), sizeAtMaximum(preferredSize), - skipInPreferred(0), type(Normal), hasSize(true) {} + skipInPreferred(0), type(Normal), hasSize(true), + isLayoutAnchor(false) {} AnchorData(qreal size) : QSimplexVariable(), from(0), to(0), minSize(size), prefSize(size), maxSize(size), sizeAtMinimum(size), sizeAtPreferred(size), sizeAtMaximum(size), - skipInPreferred(0), type(Normal), hasSize(true) {} + skipInPreferred(0), type(Normal), hasSize(true), + isLayoutAnchor(false) {} AnchorData() : QSimplexVariable(), from(0), to(0), minSize(0), prefSize(0), maxSize(0), sizeAtMinimum(0), sizeAtPreferred(0), sizeAtMaximum(0), - skipInPreferred(0), type(Normal), hasSize(false) {} + skipInPreferred(0), type(Normal), hasSize(false), + isLayoutAnchor(false) {} virtual void updateChildrenSizes() { }; + virtual void refreshSizeHints(qreal effectiveSpacing); void dump(int indent = 2); @@ -175,15 +179,17 @@ struct AnchorData : public QSimplexVariable { qreal sizeAtMaximum; uint skipInPreferred : 1; - uint type : 2; // either Normal, Sequential or Parallel - uint hasSize : 1; // if false, get size from style. + 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' protected: AnchorData(Type type, qreal size = 0) : QSimplexVariable(), from(0), to(0), minSize(size), prefSize(size), maxSize(size), sizeAtMinimum(size), sizeAtPreferred(size), sizeAtMaximum(size), - skipInPreferred(0), type(type), hasSize(true) {} + skipInPreferred(0), type(type), hasSize(true), + isLayoutAnchor(false) {} }; inline QString AnchorData::toString() const @@ -202,6 +208,7 @@ struct SequentialAnchorData : public AnchorData } virtual void updateChildrenSizes(); + virtual void refreshSizeHints(qreal effectiveSpacing); void setVertices(const QVector &vertices) { @@ -229,6 +236,7 @@ struct ParallelAnchorData : public AnchorData } virtual void updateChildrenSizes(); + virtual void refreshSizeHints(qreal effectiveSpacing); AnchorData* firstEdge; AnchorData* secondEdge; -- cgit v0.12 From 22e7ee32913484e25ae7bc61083f6a28bfaecfe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Mon, 17 Aug 2009 10:40:03 +0200 Subject: Add autotest for setSpacing() --- .../tst_qgraphicsanchorlayout.cpp | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index d94cd82..16a9af2 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -18,6 +18,8 @@ private slots: void fairDistributionOppositeDirections(); void proportionalPreferred(); void example(); + void setSpacing(); + }; class RectWidget : public QGraphicsWidget @@ -696,5 +698,45 @@ void tst_QGraphicsAnchorLayout::example() QCOMPARE(f->size(), g->size()); } +void tst_QGraphicsAnchorLayout::setSpacing() +{ + QSizeF min(10, 10); + QSizeF pref(20, 20); + QSizeF max(50, 50); + + QGraphicsWidget *a = createItem(min, pref, max); + QGraphicsWidget *b = createItem(min, pref, max); + QGraphicsWidget *c = createItem(min, pref, max); + + QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; + l->anchorCorner(l, Qt::TopLeftCorner, a, Qt::TopLeftCorner); + l->anchorCorner(b, Qt::TopRightCorner, l, Qt::TopRightCorner); + l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left); + + l->anchorWidth(c); + + l->anchor(a, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top); + l->anchor(c, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + + QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); + + p->setLayout(l); + l->setSpacing(1); + + // don't let the style influence the test. + l->setContentsMargins(0, 0, 0, 0); + + QGraphicsScene scene; + scene.addItem(p); + QGraphicsView *view = new QGraphicsView(&scene); + view->show(); + p->show(); + + QApplication::processEvents(); + QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20)); + QCOMPARE(b->geometry(), QRectF(21, 0, 20, 20)); + QCOMPARE(c->geometry(), QRectF(0, 21, 41, 20)); +} + QTEST_MAIN(tst_QGraphicsAnchorLayout) #include "tst_qgraphicsanchorlayout.moc" -- cgit v0.12 From 45305b592b13040d249dd672b2ab3520b827d135 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Mon, 17 Aug 2009 10:41:18 +0200 Subject: Change the member QGALPrivate::spacing to QGALPrivate::spacings. This was done to avoid any potential shadowing problems (we already use the variable name 'spacing' several places) --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 4 ++-- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 4 ++-- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 9846988..98e4b32 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -207,9 +207,9 @@ void QGraphicsAnchorLayout::setSpacing(qreal spacing, Qt::Orientations orientati { Q_D(QGraphicsAnchorLayout); if (orientations & Qt::Horizontal) - d->spacing[0] = spacing; + d->spacings[0] = spacing; if (orientations & Qt::Vertical) - d->spacing[1] = spacing; + d->spacings[1] = spacing; } qreal QGraphicsAnchorLayout::spacing(Qt::Orientation orientation) const diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index d7bee19..0e35566 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -226,7 +226,7 @@ QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate() : calculateGraphCacheDirty(1) { for (int i = 0; i < NOrientations; ++i) { - spacing[i] = -1; + spacings[i] = -1; graphSimplified[i] = false; } } @@ -1179,7 +1179,7 @@ void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&fi qreal QGraphicsAnchorLayoutPrivate::effectiveSpacing(Orientation orientation) const { Q_Q(const QGraphicsAnchorLayout); - qreal s = spacing[orientation]; + qreal s = spacings[orientation]; if (s < 0) { QGraphicsLayoutItem *parent = q->parentLayoutItem(); while (parent && parent->isLayout()) { diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index ce4eaf7..7b210cd 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -390,7 +390,7 @@ public: GraphPath path); void solvePreferred(QList constraints); - qreal spacing[NOrientations]; + qreal spacings[NOrientations]; // Size hints from simplex engine qreal sizeHints[2][3]; -- cgit v0.12 From a00fe5c3890ba3b80ff37657d988fb52502f6f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Mon, 17 Aug 2009 11:45:33 +0200 Subject: Small change in the "spacing API". In order to be consistent with QGraphicsGridLayout(QGGL), we remove the void setSpacing(Qt::Orientations ); qreal spacing(Qt::Orientations ); in favor to void setHorizontalSpacing(qreal spacing); void setVerticalSpacing(qreal spacing); void setSpacing(qreal spacing); qreal horizontalSpacing() const; qreal verticalSpacing() const; The API for setting and retrieving spacings should now be the same as in QGGL. --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 64 +++++++++++++++++++++--- src/gui/graphicsview/qgraphicsanchorlayout.h | 7 ++- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 1 + 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 98e4b32..9084c2c 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -79,6 +79,14 @@ QGraphicsAnchorLayout::~QGraphicsAnchorLayout() * * \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. + * + * 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 + * horizontal spacing. The same behaviour applies to Bottom to Top anchors, (but they will use + * the default vertical spacing). For all other anchor combinations, the spacing will be 0. + * All anchoring functions will follow this rule. + * + * \sa removeAnchor, anchorCorner, anchorWidth, anchorHeight, anchorGeometry */ void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, @@ -203,19 +211,61 @@ void QGraphicsAnchorLayout::removeAnchor(QGraphicsLayoutItem *firstItem, Edge fi invalidate(); } -void QGraphicsAnchorLayout::setSpacing(qreal spacing, Qt::Orientations orientations /*= Qt::Horizontal|Qt::Vertical*/) +/*! + Sets the default horizontal spacing for the anchor layout to \a spacing. + + \sa horizontalSpacing, setVerticalSpacing, setSpacing +*/ +void QGraphicsAnchorLayout::setHorizontalSpacing(qreal spacing) +{ + Q_D(QGraphicsAnchorLayout); + d->spacings[0] = spacing; +} + +/*! + Sets the default vertical spacing for the anchor layout to \a spacing. + + \sa verticalSpacing, setHorizontalSpacing, setSpacing +*/ +void QGraphicsAnchorLayout::setVerticalSpacing(qreal spacing) +{ + Q_D(QGraphicsAnchorLayout); + d->spacings[1] = spacing; +} + +/*! + Sets the default horizontal and the default vertical spacing for the anchor layout to \a spacing. + + If an item is anchored with no spacing associated with the anchor, it will use the default + spacing. + \sa setHorizontalSpacing, setVerticalSpacing +*/ +void QGraphicsAnchorLayout::setSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); - if (orientations & Qt::Horizontal) - d->spacings[0] = spacing; - if (orientations & Qt::Vertical) - d->spacings[1] = spacing; + d->spacings[0] = d->spacings[1] = spacing; +} + +/*! + Returns the default horizontal spacing for the anchor layout. + + \sa verticalSpacing, setHorizontalSpacing +*/ +qreal QGraphicsAnchorLayout::horizontalSpacing() const +{ + Q_D(const QGraphicsAnchorLayout); + return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Horizontal); } -qreal QGraphicsAnchorLayout::spacing(Qt::Orientation orientation) const +/*! + Returns the default vertical spacing for the anchor layout. + + \sa horizontalSpacing, setVerticalSpacing +*/ +qreal QGraphicsAnchorLayout::verticalSpacing() const { Q_D(const QGraphicsAnchorLayout); - return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Orientation(orientation - 1)); + return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Vertical); } void QGraphicsAnchorLayout::setGeometry(const QRectF &geom) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h index 249996e..8010b31 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -103,8 +103,11 @@ public: inline void anchorGeometry(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo, qreal spacing); - void setSpacing(qreal spacing, Qt::Orientations orientations = Qt::Horizontal|Qt::Vertical); - qreal spacing(Qt::Orientation) const; + void setHorizontalSpacing(qreal spacing); + void setVerticalSpacing(qreal spacing); + void setSpacing(qreal spacing); + qreal horizontalSpacing() const; + qreal verticalSpacing() const; void removeAt(int index); void setGeometry(const QRectF &rect); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 0e35566..b1ce6d1 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1181,6 +1181,7 @@ qreal QGraphicsAnchorLayoutPrivate::effectiveSpacing(Orientation orientation) co Q_Q(const QGraphicsAnchorLayout); qreal s = spacings[orientation]; if (s < 0) { + // ### make sure behaviour is the same as in QGraphicsGridLayout QGraphicsLayoutItem *parent = q->parentLayoutItem(); while (parent && parent->isLayout()) { parent = parent->parentLayoutItem(); -- cgit v0.12 From f2f5efc551648c01a384dbc36cec0d966ccbebbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Mon, 17 Aug 2009 12:06:55 +0200 Subject: Clean up public API: move dumpGraph() to private API. Hopefully we won't need it more... --- examples/graphicsview/anchorlayout/main.cpp | 2 -- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 18 ------------------ src/gui/graphicsview/qgraphicsanchorlayout.h | 4 ---- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 17 +++++++++++++++++ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 6 ++++++ 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/examples/graphicsview/anchorlayout/main.cpp b/examples/graphicsview/anchorlayout/main.cpp index 5e383fa..ecb2675 100644 --- a/examples/graphicsview/anchorlayout/main.cpp +++ b/examples/graphicsview/anchorlayout/main.cpp @@ -126,8 +126,6 @@ int main(int argc, char **argv) return 0; #endif - l->dumpGraph(); - scene->addItem(w); scene->setBackgroundBrush(Qt::darkGreen); QGraphicsView *view = new QGraphicsView(scene); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index 9084c2c..b3dac18 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -350,21 +350,3 @@ QSizeF QGraphicsAnchorLayout::sizeHint(Qt::SizeHint which, const QSizeF &constra return engineSizeHint + QSizeF(left + right, top + bottom); } - -//////// DEBUG ///////// -#include -void QGraphicsAnchorLayout::dumpGraph() -{ - Q_D(QGraphicsAnchorLayout); - - QFile file(QString::fromAscii("anchorlayout.dot")); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) - qWarning("Could not write to %s", file.fileName().toLocal8Bit().constData()); - - QString str = QString::fromAscii("digraph anchorlayout {\nnode [shape=\"rect\"]\n%1}"); - QString dotContents = d->graph[0].serializeToDot(); - dotContents += d->graph[1].serializeToDot(); - file.write(str.arg(dotContents).toLocal8Bit()); - - file.close(); -} diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h index 8010b31..f0ffcbd 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -117,10 +117,6 @@ public: void invalidate(); QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; - ///////// DEBUG ///////// - void dumpGraph(); -protected: - private: Q_DISABLE_COPY(QGraphicsAnchorLayout) Q_DECLARE_PRIVATE(QGraphicsAnchorLayout) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index b1ce6d1..88b4ff3 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1879,3 +1879,20 @@ void QGraphicsAnchorLayoutPrivate::solvePreferred(QList co qDeleteAll(preferredConstraints); qDeleteAll(preferredVariables); } + +#ifdef QT_DEBUG +#include +void QGraphicsAnchorLayoutPrivate::dumpGraph() +{ + QFile file(QString::fromAscii("anchorlayout.dot")); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) + qWarning("Could not write to %s", file.fileName().toLocal8Bit().constData()); + + QString str = QString::fromAscii("digraph anchorlayout {\nnode [shape=\"rect\"]\n%1}"); + QString dotContents = graph[0].serializeToDot(); + dotContents += graph[1].serializeToDot(); + file.write(str.arg(dotContents).toLocal8Bit()); + + file.close(); +} +#endif diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 7b210cd..add4dd3 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -390,6 +390,11 @@ public: GraphPath path); void solvePreferred(QList constraints); +#ifdef QT_DEBUG + void dumpGraph(); +#endif + + qreal spacings[NOrientations]; // Size hints from simplex engine qreal sizeHints[2][3]; @@ -420,3 +425,4 @@ public: uint calculateGraphCacheDirty : 1; }; + -- cgit v0.12 From c9aa27d0ae9f3492c19da1e4c75178b8fcb764be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Mon, 17 Aug 2009 13:48:01 +0200 Subject: Add missing call to invalidate() in all functions that modify the spacing. --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index b3dac18..ff4706d 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -220,6 +220,7 @@ void QGraphicsAnchorLayout::setHorizontalSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); d->spacings[0] = spacing; + invalidate(); } /*! @@ -231,6 +232,7 @@ void QGraphicsAnchorLayout::setVerticalSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); d->spacings[1] = spacing; + invalidate(); } /*! @@ -244,6 +246,7 @@ void QGraphicsAnchorLayout::setSpacing(qreal spacing) { Q_D(QGraphicsAnchorLayout); d->spacings[0] = d->spacings[1] = spacing; + invalidate(); } /*! -- cgit v0.12 From 39e13f96004d520bb2124e1f7bf9ff8fcc04f6b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Mon, 17 Aug 2009 13:49:39 +0200 Subject: More tests for setSpacing --- .../qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 16a9af2..57f04cc 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -736,6 +736,21 @@ void tst_QGraphicsAnchorLayout::setSpacing() QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20)); QCOMPARE(b->geometry(), QRectF(21, 0, 20, 20)); QCOMPARE(c->geometry(), QRectF(0, 21, 41, 20)); + + l->setHorizontalSpacing(4); + QApplication::processEvents(); + p->adjustSize(); + QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20)); + QCOMPARE(b->geometry(), QRectF(24, 0, 20, 20)); + QCOMPARE(c->geometry(), QRectF(0, 21, 44, 20)); + + l->setVerticalSpacing(0); + QApplication::processEvents(); + p->adjustSize(); + QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20)); + QCOMPARE(b->geometry(), QRectF(24, 0, 20, 20)); + QCOMPARE(c->geometry(), QRectF(0, 20, 44, 20)); + } QTEST_MAIN(tst_QGraphicsAnchorLayout) -- cgit v0.12 From 91d53f247c4615c8e5d3ea0cab0e3f494b46f6e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Mon, 17 Aug 2009 15:18:57 +0200 Subject: Clean up example --- examples/graphicsview/anchorlayout/main.cpp | 74 ++----------------------- examples/graphicsview/anchorlayout/noGUI.cpp | 83 ---------------------------- 2 files changed, 6 insertions(+), 151 deletions(-) delete mode 100644 examples/graphicsview/anchorlayout/noGUI.cpp diff --git a/examples/graphicsview/anchorlayout/main.cpp b/examples/graphicsview/anchorlayout/main.cpp index ecb2675..1c87fa0 100644 --- a/examples/graphicsview/anchorlayout/main.cpp +++ b/examples/graphicsview/anchorlayout/main.cpp @@ -3,24 +3,6 @@ #include #include -//#define BENCHMARK_RUN 1 -#define CALLGRIND_DEBUG 1 - -#if defined(BENCHMARK_RUN) - -// Callgrind command line: -// valgrind --tool=callgrind --instr-atstart=no ./anchorlayout >/dev/null 2>&1 - -#if defined(CALLGRIND_DEBUG) -#include -#else -#define CALLGRIND_START_INSTRUMENTATION do { } while (0) -#define CALLGRIND_STOP_INSTRUMENTATION do { } while (0) -#endif - -#endif - - static QGraphicsProxyWidget *createItem(const QSizeF &minimum = QSizeF(100.0, 100.0), const QSizeF &preferred = QSize(150.0, 100.0), const QSizeF &maximum = QSizeF(200.0, 100.0), @@ -36,34 +18,12 @@ static QGraphicsProxyWidget *createItem(const QSizeF &minimum = QSizeF(100.0, 10 return w; } -class Test : public QObject -{ - Q_OBJECT; -public: - Test(QGraphicsWidget *wi, QGraphicsLayout* la) - : QObject(), w(wi), l(la), first(true) {} - - QGraphicsWidget *w; - QGraphicsLayout *l; - bool first; - -public slots: - void onTimer() { - if (first) { - first = false; - w->setContentsMargins(10, 10, 10, 10); - } else { - l->setContentsMargins(10, 10, 10, 10); - } - } -}; - int main(int argc, char **argv) { QApplication app(argc, argv); - QGraphicsScene *scene = new QGraphicsScene(); - scene->setSceneRect(0, 0, 800, 480); + QGraphicsScene scene; + scene.setSceneRect(0, 0, 800, 480); QSizeF min(30, 100); QSizeF pref(210, 100); @@ -116,32 +76,10 @@ int main(int argc, char **argv) l->anchor(l, QGraphicsAnchorLayout::Left, g, QGraphicsAnchorLayout::Left, 0); l->anchor(f, QGraphicsAnchorLayout::Right, g, QGraphicsAnchorLayout::Right, 0); -#ifdef BENCHMARK_RUN - CALLGRIND_START_INSTRUMENTATION; - for (int i = 0; i < 100; i++) { - l->invalidate(); - l->d_ptr->calculateGraphs(); - } - CALLGRIND_STOP_INSTRUMENTATION; - return 0; -#endif - - scene->addItem(w); - scene->setBackgroundBrush(Qt::darkGreen); - QGraphicsView *view = new QGraphicsView(scene); + scene.addItem(w); + scene.setBackgroundBrush(Qt::darkGreen); + QGraphicsView *view = new QGraphicsView(&scene); view->show(); - Test test(w, l); - QTimer timer; - test.connect(&timer, SIGNAL(timeout()), SLOT(onTimer())); - timer.start(5000); - - int rc = app.exec(); - - delete scene; - delete view; - - return rc; + return app.exec(); } - -#include "main.moc" diff --git a/examples/graphicsview/anchorlayout/noGUI.cpp b/examples/graphicsview/anchorlayout/noGUI.cpp deleted file mode 100644 index 3f04f9a..0000000 --- a/examples/graphicsview/anchorlayout/noGUI.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include -#include "qgraphicsanchorlayout.h" - -// DEBUG -#include "qgraphicsanchorlayout_p.h" - -static QGraphicsWidget *createItem(const QSizeF &minimum = QSizeF(100.0, 100.0), - const QSizeF &preferred = QSize(150.0, 100.0), - const QSizeF &maximum = QSizeF(200.0, 100.0)) -{ - QGraphicsWidget *w = new QGraphicsWidget; - w->setMinimumSize(minimum); - w->setPreferredSize(preferred); - w->setMaximumSize(maximum); - return w; -} - - -int main(int argc, char **argv) -{ - Q_UNUSED(argc); - Q_UNUSED(argv); - - - QGraphicsWidget *a = createItem(QSizeF(50.0, 100.0), - QSizeF(70.0, 100.0), - QSizeF(100.0, 200.0)); - a->setData(0, "A"); - - QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), - QSizeF(20.0, 100.0), - QSizeF(40.0, 200.0)); - b->setData(0, "B"); - - QGraphicsWidget *c = createItem(QSizeF(50.0, 100.0), - QSizeF(70.0, 100.0), - QSizeF(100.0, 200.0)); - c->setData(0, "C"); - - QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; - - l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top); - l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top); - l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top); - l->anchor(c, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); - - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Right); - l->anchor(b, QGraphicsAnchorLayout::Left, c, QGraphicsAnchorLayout::Left); - l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right); - - l->dumpGraph(); - - QSizeF layoutMinimumSize = l->effectiveSizeHint(Qt::MinimumSize); - QSizeF layoutMaximumSize = l->effectiveSizeHint(Qt::MaximumSize); - QSizeF layoutPreferredSize = l->effectiveSizeHint(Qt::PreferredSize); - - - qWarning() << "Layout Min/Pref/Max: " << layoutMinimumSize - << " " << layoutPreferredSize - << " " << layoutMaximumSize; - - - qWarning() << "\nSetting minimum size"; - l->setGeometry(QRectF(QPointF(0,0), layoutMinimumSize)); - qWarning() << "A: " << a->geometry(); - qWarning() << "B: " << b->geometry(); - qWarning() << "C: " << c->geometry(); - - qWarning() << "\nSetting maximum size"; - l->setGeometry(QRectF(QPointF(0,0), layoutMaximumSize)); - qWarning() << "A: " << a->geometry(); - qWarning() << "B: " << b->geometry(); - qWarning() << "C: " << c->geometry(); - - qWarning() << "\nSetting size 85.0"; - l->setGeometry(QRectF(QPointF(0,0), QSizeF(85.0, 200.0))); - qWarning() << "A: " << a->geometry(); - qWarning() << "B: " << b->geometry(); - qWarning() << "C: " << c->geometry(); - - return 0; -} -- cgit v0.12 From a9457f0a626a306b4ac7e3ab6a6a89a92f724fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Mon, 17 Aug 2009 17:25:31 +0200 Subject: Fill in some missing pieces in the documentation. Need to work more on the overview. --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 122 ++++++++++++++++++++++++- 1 file changed, 121 insertions(+), 1 deletion(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index ff4706d..e7eec77 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -39,6 +39,46 @@ ** ****************************************************************************/ +/*! + \class QGraphicsAnchorLayout + \brief The QGraphicsAnchorLayout class provides a layout where one can anchor widgets + together in Graphics View. + \since 4.6 + \ingroup appearance + \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(). + 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, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left); + \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 QGraphicsLinearLayout + QGraphicsLinearLayout 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. + + \section1 Spacing within QGraphicsAnchorLayout + + 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). +*/ + #include "qgraphicsanchorlayout_p.h" QGraphicsAnchorLayout::QGraphicsAnchorLayout(QGraphicsLayoutItem *parent) @@ -130,7 +170,7 @@ void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, * has the same effect as * * \code - * layout->anchor(layout, Qt::TopLeft, b, Qt::TopLeft); + * layout->anchorCorner(layout, Qt::TopLeft, b, Qt::TopLeft); * \endcode * * If there is already an anchor between the edge pairs, it will be replaced by the anchors that @@ -185,6 +225,68 @@ void QGraphicsAnchorLayout::anchorCorner(QGraphicsLayoutItem *firstItem, invalidate(); } +/*! + \fn QGraphicsAnchorLayout::anchorWidth(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo = 0) + + This convenience function is equivalent to calling + \code + if (!relativeTo) + relativeTo = lay; + lay->anchor(relativeTo, QGraphicsAnchorLayout::Left, item, QGraphicsAnchorLayout::Left); + lay->anchor(relativeTo, QGraphicsAnchorLayout::Right, item, QGraphicsAnchorLayout::Right); + \endcode +*/ + +/*! + \fn QGraphicsAnchorLayout::anchorWidth(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo, qreal spacing) + + \overload + + By calling this function the caller can specify the magnitude of the anchor with \a spacing. +*/ + +/*! + \fn QGraphicsAnchorLayout::anchorHeight(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo = 0) + + This convenience function is equivalent to calling + \code + if (!relativeTo) + relativeTo = lay; + lay->anchor(relativeTo, QGraphicsAnchorLayout::Top, item, QGraphicsAnchorLayout::Top); + lay->anchor(relativeTo, QGraphicsAnchorLayout::Bottom, item, QGraphicsAnchorLayout::Bottom); + \endcode +*/ + +/*! + \fn QGraphicsAnchorLayout::anchorHeight(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo, qreal spacing) + + \overload + + By calling this function the caller can specify the magnitude of the anchor with \a spacing. +*/ + +/*! + \fn QGraphicsAnchorLayout::anchorGeometry(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo = 0) + + This convenience function is equivalent to calling + \code + anchorWidth(item, relativeTo); + anchorHeight(item, relativeTo); + \endcode +*/ + +/*! + \fn QGraphicsAnchorLayout::anchorGeometry(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo, qreal spacing) + + \overload + + By calling this function the caller can specify the magnitude of the anchor with \a spacing. +*/ + +/*! + Removes the anchor between the edge \a firstEdge of item \a firstItem and the edge \a secondEdge + of item \a secondItem. If such an anchor does not exist, the layout will be left unchanged. +*/ void QGraphicsAnchorLayout::removeAnchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, QGraphicsLayoutItem *secondItem, Edge secondEdge) { @@ -271,6 +373,9 @@ qreal QGraphicsAnchorLayout::verticalSpacing() const return d->effectiveSpacing(QGraphicsAnchorLayoutPrivate::Vertical); } +/*! + \reimp +*/ void QGraphicsAnchorLayout::setGeometry(const QRectF &geom) { Q_D(QGraphicsAnchorLayout); @@ -286,6 +391,9 @@ void QGraphicsAnchorLayout::setGeometry(const QRectF &geom) d->setItemsGeometries(); } +/*! + Removing an item will also remove any of the anchors associated with it. +*/ void QGraphicsAnchorLayout::removeAt(int index) { Q_D(QGraphicsAnchorLayout); @@ -307,18 +415,27 @@ void QGraphicsAnchorLayout::removeAt(int index) invalidate(); } +/*! + \reimp +*/ int QGraphicsAnchorLayout::count() const { Q_D(const QGraphicsAnchorLayout); return d->items.size(); } +/*! + \reimp +*/ QGraphicsLayoutItem *QGraphicsAnchorLayout::itemAt(int index) const { Q_D(const QGraphicsAnchorLayout); return d->items.value(index); } +/*! + \reimp +*/ void QGraphicsAnchorLayout::invalidate() { Q_D(QGraphicsAnchorLayout); @@ -326,6 +443,9 @@ void QGraphicsAnchorLayout::invalidate() d->calculateGraphCacheDirty = 1; } +/*! + \reimp +*/ QSizeF QGraphicsAnchorLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const { Q_UNUSED(which); -- cgit v0.12 From 47ee590221f70f16bb27bf20f858dc60c9fc14ca Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Mon, 17 Aug 2009 12:26:16 -0300 Subject: QGraphicsAnchorLayout: calculate vertex positions with simplified graph When traversing the graph looking for calculating the distances, also enter inside the complex edges to find (and calculate) the distances for the vertices that were simplified. This require a little tweak in the traversal, now we can't always skip an edge when both vertices are already visited, because complex anchors can hide vertices inside (a sequential anchor in practice remove vertices from the full graph). As a bonus we now pass on the 'example' test (as expected) and the example now works fine. Signed-off-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 5 - src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 120 ++++++++++++++++----- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 7 +- .../tst_qgraphicsanchorlayout.cpp | 1 - 4 files changed, 100 insertions(+), 33 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index e7eec77..a0e1101 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -380,11 +380,6 @@ void QGraphicsAnchorLayout::setGeometry(const QRectF &geom) { Q_D(QGraphicsAnchorLayout); - // ### REMOVE IT WHEN calculateVertexPositions and setItemsGeometries are - // simplification compatible! - d->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Horizontal); - d->restoreSimplifiedGraph(QGraphicsAnchorLayoutPrivate::Vertical); - QGraphicsLayout::setGeometry(geom); d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Horizontal); d->calculateVertexPositions(QGraphicsAnchorLayoutPrivate::Vertical); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 88b4ff3..b20d358 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1670,7 +1670,7 @@ void QGraphicsAnchorLayoutPrivate::calculateVertexPositions( root->distance = widgetMargin + layoutMargin; visited.insert(root); - // Add initial edges to the queue + // Add initial edges to the queue foreach (AnchorVertex *v, graph[orientation].adjacentVertices(root)) { queue.enqueue(qMakePair(root, v)); } @@ -1678,29 +1678,25 @@ void QGraphicsAnchorLayoutPrivate::calculateVertexPositions( // Do initial calculation required by "interpolateEdge()" setupEdgesInterpolation(orientation); - // Traverse the graph and calculate vertex positions. + // Traverse the graph and calculate vertex positions, we need to + // visit all pairs since each of them could have a sequential + // anchor inside, which hides more vertices. while (!queue.isEmpty()) { QPair pair = queue.dequeue(); + AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second); - if (visited.contains(pair.second)) + // Both vertices were interpolated, and the anchor itself can't have other + // anchors inside (it's not a complex anchor). + if (edge->type == AnchorData::Normal && visited.contains(pair.second)) continue; - visited.insert(pair.second); - - // The distance to the next vertex is equal the distance to the - // previous one plus (or less) the size of the edge between them. - qreal distance; - AnchorData *edge = graph[orientation].edgeData(pair.first, pair.second); - if (edge->from == pair.first) { - distance = pair.first->distance + interpolateEdge(edge); - } else { - distance = pair.first->distance - interpolateEdge(edge); - } - pair.second->distance = distance; + visited.insert(pair.second); + interpolateEdge(pair.first, edge, orientation); - foreach (AnchorVertex *v, - graph[orientation].adjacentVertices(pair.second)) { - queue.enqueue(qMakePair(pair.second, v)); + QList adjacents = graph[orientation].adjacentVertices(pair.second); + for (int i = 0; i < adjacents.count(); ++i) { + if (!visited.contains(adjacents.at(i))) + queue.enqueue(qMakePair(pair.second, adjacents.at(i))); } } } @@ -1750,16 +1746,21 @@ void QGraphicsAnchorLayoutPrivate::setupEdgesInterpolation( - the layout is at its preferred size. - the layout is at its maximum size. - These three key values are calculated in advance using linear programming - (more expensive), then subsequential resizes of the parent layout require - a simple interpolation. -*/ -qreal QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorData *edge) + These three key values are calculated in advance using linear + programming (more expensive) or the simplification algorithm, then + subsequential resizes of the parent layout require a simple + interpolation. + + If the edge is sequential or parallel, it's possible to have more + vertices to be initalized, so it calls specialized functions that + will recurse back to interpolateEdge(). + */ +void QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorVertex *base, + AnchorData *edge, + Orientation orientation) { qreal lower, upper; - Orientation orientation = edgeOrientation(edge->from->m_edge); - if (interpolationInterval[orientation] == MinToPreferred) { lower = edge->sizeAtMinimum; upper = edge->sizeAtPreferred; @@ -1768,9 +1769,76 @@ qreal QGraphicsAnchorLayoutPrivate::interpolateEdge(AnchorData *edge) upper = edge->sizeAtMaximum; } - return (interpolationProgress[orientation] * (upper - lower)) + lower; + qreal edgeDistance = (interpolationProgress[orientation] * (upper - lower)) + lower; + + Q_ASSERT(edge->from == base || edge->to == base); + + if (edge->from == base) + edge->to->distance = base->distance + edgeDistance; + else + edge->from->distance = base->distance - edgeDistance; + + // Process child anchors + if (edge->type == AnchorData::Sequential) + interpolateSequentialEdges(edge->from, + static_cast(edge), + orientation); + else if (edge->type == AnchorData::Parallel) + interpolateParallelEdges(edge->from, + static_cast(edge), + orientation); +} + +void QGraphicsAnchorLayoutPrivate::interpolateParallelEdges( + AnchorVertex *base, ParallelAnchorData *data, Orientation orientation) +{ + // In parallels the boundary vertices are already calculate, we + // just need to look for sequential groups inside, because only + // them may have new vertices associated. + + // First edge + if (data->firstEdge->type == AnchorData::Sequential) + interpolateSequentialEdges(base, + static_cast(data->firstEdge), + orientation); + else if (data->firstEdge->type == AnchorData::Parallel) + interpolateParallelEdges(base, + static_cast(data->firstEdge), + orientation); + + // Second edge + if (data->secondEdge->type == AnchorData::Sequential) + interpolateSequentialEdges(base, + static_cast(data->secondEdge), + orientation); + else if (data->secondEdge->type == AnchorData::Parallel) + interpolateParallelEdges(base, + static_cast(data->secondEdge), + orientation); } +void QGraphicsAnchorLayoutPrivate::interpolateSequentialEdges( + AnchorVertex *base, SequentialAnchorData *data, Orientation orientation) +{ + AnchorVertex *prev = base; + + // ### I'm not sure whether this assumption is safe. If not, + // consider that m_edges.last() could be used instead (so + // at(0) would be the one to be treated specially). + Q_ASSERT(base == data->m_edges.at(0)->to || base == data->m_edges.at(0)->from); + + // Skip the last + for (int i = 0; i < data->m_edges.count() - 1; ++i) { + AnchorData *child = data->m_edges.at(i); + interpolateEdge(prev, child, orientation); + prev = child->to; + } + + // Treat the last specially, since we already calculated it's end + // vertex, so it's only interesting if it's a complex one + if (data->m_edges.last()->type != AnchorData::Normal) + interpolateEdge(prev, data->m_edges.last(), orientation); +} QPair QGraphicsAnchorLayoutPrivate::solveMinMax(QList constraints, diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index add4dd3..b6cef4e 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -381,9 +381,14 @@ public: // Geometry interpolation methods void setItemsGeometries(); + void calculateVertexPositions(Orientation orientation); void setupEdgesInterpolation(Orientation orientation); - qreal interpolateEdge(AnchorData *edge); + void interpolateEdge(AnchorVertex *base, AnchorData *edge, Orientation orientation); + void interpolateSequentialEdges(AnchorVertex *base, SequentialAnchorData *edge, + Orientation orientation); + void interpolateParallelEdges(AnchorVertex *base, ParallelAnchorData *edge, + Orientation orientation); // Linear Programming solver methods QPair solveMinMax(QList constraints, diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 57f04cc..e663f40 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -682,7 +682,6 @@ void tst_QGraphicsAnchorLayout::example() QCOMPARE(p.size(), layoutMinimumSize); QCOMPARE(a->size(), e->size()); QCOMPARE(b->size(), d->size()); - QEXPECT_FAIL("", "please fix this test", Abort); QCOMPARE(f->size(), g->size()); p.resize(layoutPreferredSize); -- cgit v0.12 From 3d8d478a6ee108e25f28f8b52db9a6e86ebce8e1 Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Mon, 17 Aug 2009 12:32:29 -0300 Subject: QGraphicsAnchorLayout: add names to items in examples Having the items named helps when using debugging functions to identify the anchors. Cause no harm to add them to the tests. --- .../tst_qgraphicsanchorlayout.cpp | 66 +++++++++++----------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index e663f40..3bab0ce 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -71,11 +71,11 @@ void tst_QGraphicsAnchorLayout::diagonal() QSizeF pref(70, 100); QSizeF max(100, 100); - QGraphicsWidget *a = createItem(min, pref, max); - QGraphicsWidget *b = createItem(min, pref, max); - QGraphicsWidget *c = createItem(min, pref, max); - QGraphicsWidget *d = createItem(min, pref, max); - QGraphicsWidget *e = createItem(min, pref, max); + QGraphicsWidget *a = createItem(min, pref, max, "A"); + QGraphicsWidget *b = createItem(min, pref, max, "B"); + QGraphicsWidget *c = createItem(min, pref, max, "C"); + QGraphicsWidget *d = createItem(min, pref, max, "D"); + QGraphicsWidget *e = createItem(min, pref, max, "E"); QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); @@ -247,11 +247,11 @@ void tst_QGraphicsAnchorLayout::parallel2() { QGraphicsWidget *a = createItem(QSizeF(70.0, 100.0), QSizeF(100.0, 100.0), - QSizeF(200.0, 100.0)); + QSizeF(200.0, 100.0), "A"); QGraphicsWidget *b = createItem(QSizeF(100.0, 100.0), QSizeF(150.0, 100.0), - QSizeF(190.0, 100.0)); + QSizeF(190.0, 100.0), "B"); QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); @@ -291,15 +291,15 @@ void tst_QGraphicsAnchorLayout::snake() { QGraphicsWidget *a = createItem(QSizeF(50.0, 100.0), QSizeF(70.0, 100.0), - QSizeF(100.0, 100.0)); + QSizeF(100.0, 100.0), "A"); QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), QSizeF(20.0, 100.0), - QSizeF(40.0, 100.0)); + QSizeF(40.0, 100.0), "B"); QGraphicsWidget *c = createItem(QSizeF(50.0, 100.0), QSizeF(70.0, 100.0), - QSizeF(100.0, 100.0)); + QSizeF(100.0, 100.0), "C"); QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); @@ -350,15 +350,15 @@ void tst_QGraphicsAnchorLayout::snakeOppositeDirections() { QGraphicsWidget *a = createItem(QSizeF(50.0, 100.0), QSizeF(70.0, 100.0), - QSizeF(100.0, 100.0)); + QSizeF(100.0, 100.0), "A"); QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), QSizeF(20.0, 100.0), - QSizeF(40.0, 100.0)); + QSizeF(40.0, 100.0), "B"); QGraphicsWidget *c = createItem(QSizeF(50.0, 100.0), QSizeF(70.0, 100.0), - QSizeF(100.0, 100.0)); + QSizeF(100.0, 100.0), "C"); QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); @@ -412,19 +412,19 @@ void tst_QGraphicsAnchorLayout::fairDistribution() { QGraphicsWidget *a = createItem(QSizeF(10.0, 100.0), QSizeF(50.0, 100.0), - QSizeF(100.0, 100.0)); + QSizeF(100.0, 100.0), "A"); QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), QSizeF(50.0, 100.0), - QSizeF(100.0, 100.0)); + QSizeF(100.0, 100.0), "B"); QGraphicsWidget *c = createItem(QSizeF(10.0, 100.0), QSizeF(50.0, 100.0), - QSizeF(100.0, 100.0)); + QSizeF(100.0, 100.0), "C"); QGraphicsWidget *d = createItem(QSizeF(60.0, 100.0), QSizeF(165.0, 100.0), - QSizeF(600.0, 100.0)); + QSizeF(600.0, 100.0), "D"); QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; @@ -482,23 +482,23 @@ void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() { QGraphicsWidget *a = createItem(QSizeF(10.0, 100.0), QSizeF(50.0, 100.0), - QSizeF(100.0, 100.0)); + QSizeF(100.0, 100.0), "A"); QGraphicsWidget *b = createItem(QSizeF(10.0, 100.0), QSizeF(50.0, 100.0), - QSizeF(100.0, 100.0)); + QSizeF(100.0, 100.0), "B"); QGraphicsWidget *c = createItem(QSizeF(10.0, 100.0), QSizeF(50.0, 100.0), - QSizeF(100.0, 100.0)); + QSizeF(100.0, 100.0), "C"); QGraphicsWidget *d = createItem(QSizeF(10.0, 100.0), QSizeF(50.0, 100.0), - QSizeF(100.0, 100.0)); + QSizeF(100.0, 100.0), "D"); QGraphicsWidget *e = createItem(QSizeF(60.0, 100.0), QSizeF(220.0, 100.0), - QSizeF(600.0, 100.0)); + QSizeF(600.0, 100.0), "E"); QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; @@ -557,10 +557,10 @@ void tst_QGraphicsAnchorLayout::proportionalPreferred() { QSizeF min(0, 100); - QGraphicsWidget *a = createItem(min, QSizeF(10, 100), QSizeF(20, 100)); - QGraphicsWidget *b = createItem(min, QSizeF(20, 100), QSizeF(30, 100)); - QGraphicsWidget *c = createItem(min, QSizeF(14, 100), QSizeF(20, 100)); - QGraphicsWidget *d = createItem(min, QSizeF(10, 100), QSizeF(20, 100)); + QGraphicsWidget *a = createItem(min, QSizeF(10, 100), QSizeF(20, 100), "A"); + QGraphicsWidget *b = createItem(min, QSizeF(20, 100), QSizeF(30, 100), "B"); + QGraphicsWidget *c = createItem(min, QSizeF(14, 100), QSizeF(20, 100), "C"); + QGraphicsWidget *d = createItem(min, QSizeF(10, 100), QSizeF(20, 100), "D"); QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); @@ -621,13 +621,13 @@ void tst_QGraphicsAnchorLayout::example() QSizeF pref(210, 100); QSizeF max(300, 100); - QGraphicsWidget *a = createItem(min, pref, max); - QGraphicsWidget *b = createItem(min, pref, max); - QGraphicsWidget *c = createItem(min, pref, max); - QGraphicsWidget *d = createItem(min, pref, max); - QGraphicsWidget *e = createItem(min, pref, max); - QGraphicsWidget *f = createItem(QSizeF(30, 50), QSizeF(150, 50), max); - QGraphicsWidget *g = createItem(QSizeF(30, 50), QSizeF(30, 100), max); + QGraphicsWidget *a = createItem(min, pref, max, "A"); + QGraphicsWidget *b = createItem(min, pref, max, "B"); + QGraphicsWidget *c = createItem(min, pref, max, "C"); + QGraphicsWidget *d = createItem(min, pref, max, "D"); + QGraphicsWidget *e = createItem(min, pref, max, "E"); + QGraphicsWidget *f = createItem(QSizeF(30, 50), QSizeF(150, 50), max, "F"); + QGraphicsWidget *g = createItem(QSizeF(30, 50), QSizeF(30, 100), max, "G"); QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); -- cgit v0.12 From 961ce72bba27e1e91a0a1ba456058cdd880558eb Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Mon, 17 Aug 2009 13:57:55 -0300 Subject: QGraphicsAnchorLayout: hide some debug messages --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index b20d358..11dc59e 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -370,8 +370,10 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) return; graphSimplified[orientation] = true; +#if 0 qDebug("Simplifying Graph for %s", orientation == Horizontal ? "Horizontal" : "Vertical"); +#endif AnchorVertex *rootVertex = graph[orientation].rootVertex(); @@ -612,8 +614,10 @@ void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientatio return; graphSimplified[orientation] = false; +#if 0 qDebug("Restoring Simplified Graph for %s", orientation == Horizontal ? "Horizontal" : "Vertical"); +#endif Q_Q(QGraphicsAnchorLayout); Graph &g = graph[orientation]; @@ -1300,8 +1304,10 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( GraphPath trunkPath = graphPaths[orientation].value(v); if (!trunkConstraints.isEmpty()) { +#if 0 qDebug("Simplex used for trunk of %s", orientation == Horizontal ? "Horizontal" : "Vertical"); +#endif // Solve min and max size hints for trunk QPair minMax = solveMinMax(trunkConstraints, trunkPath); @@ -1332,8 +1338,10 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( } sizeHints[orientation][Qt::PreferredSize] = pref; } else { +#if 0 qDebug("Simplex NOT used for trunk of %s", orientation == Horizontal ? "Horizontal" : "Vertical"); +#endif // No Simplex is necessary because the path was simplified all the way to a single // anchor. -- cgit v0.12 From 9979d162a3aec02fb74890b4131b5bb745af61cd Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Mon, 17 Aug 2009 18:27:58 -0300 Subject: QGraphicsAnchorLayout: add QT_ANCHORLAYOUT_NO_SIMPLIFICATION back For debugging purposes, if QT_ANCHORLAYOUT_NO_SIMPLIFICATION environment variable is set, the layout won't use simplification. Useful for some debuggings. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 11dc59e..4d67d58 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -366,6 +366,10 @@ static bool simplifySequentialChunk(Graph *graph, void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) { + static bool noSimplification = !qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty(); + if (noSimplification) + return; + if (graphSimplified[orientation]) return; graphSimplified[orientation] = true; -- cgit v0.12 From f127c4708db2f6825d7b37b9aa5dec52f9ee989c Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Mon, 17 Aug 2009 18:31:16 -0300 Subject: QGraphicsAnchorLayout: missing restoreSimplifiedGraph call When creating an anchor we might have to create the edges for an item if the item is new. In this case both dimensions are affected. The call is clearly missing since createItemEdges() declared this dependency via Q_ASSERT. Patch from Jan-Arve. Reviewed-by: Caio Marcelo de Oliveira Filho --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 4d67d58..9f18781 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -950,10 +950,12 @@ void QGraphicsAnchorLayoutPrivate::anchor(QGraphicsLayoutItem *firstItem, // Ensure that the internal anchors have been created for both items. if (firstItem != q && !items.contains(firstItem)) { + restoreSimplifiedGraph(edgeOrientation(firstEdge) == Horizontal ? Vertical : Horizontal); createItemEdges(firstItem); addChildLayoutItem(firstItem); } if (secondItem != q && !items.contains(secondItem)) { + restoreSimplifiedGraph(edgeOrientation(firstEdge) == Horizontal ? Vertical : Horizontal); createItemEdges(secondItem); addChildLayoutItem(secondItem); } -- cgit v0.12 From a5a925748ff3893e4acf2d41bd8aee0d11a3285a Mon Sep 17 00:00:00 2001 From: Caio Marcelo de Oliveira Filho Date: Mon, 17 Aug 2009 19:52:17 -0300 Subject: QGraphicsAnchorLayout: fix calculation of sizeAt* values for Sequential This commit implements what's described in the previous commit QGraphicsAnchorLayout: fix expected values for parallel test When filling the sizeAt* values (the three points used for interpolation when setting geometry), now sequential anchors distribute the sizes to the children in a fair way, i.e. proportionally in relation to the existing min/pref/max hints. Each value is defined in relation of the pref (either as a shrinkage in pref or a grow in pref). In both cases the shrinking/growing factor is the same for all children. When we implement support for QSizePolicies, this distribution might be changed by setting size policies for a certain item or anchor. This makes two tests work -- so no more expected fail. Also fixed a typo in one test's expected results. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 80 ++++++++++++++++++++-- .../tst_qgraphicsanchorlayout.cpp | 6 +- 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 9f18781..d54ea8d 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -127,17 +127,83 @@ void ParallelAnchorData::refreshSizeHints(qreal effectiveSpacing) sizeAtMaximum = prefSize; } +static qreal getFactor(qreal value, qreal min, qreal pref, qreal max, bool *minToPref) +{ + // ### Maybe remove some of the assertions? (since outside is asserting us) + Q_ASSERT(value > min || qFuzzyCompare(value, min)); + Q_ASSERT(value < max || qFuzzyCompare(value, max)); + + if (qFuzzyCompare(value, min)) { + *minToPref = true; + return 0.0; + + } else if (qFuzzyCompare(value, pref)) { + *minToPref = true; + return 1.0; + + } else if (qFuzzyCompare(value, max)) { + *minToPref = false; + return 1.0; + + } else if (value < pref) { + *minToPref = true; + // Since value < pref and value != pref and min <= value, + // we can assert that min < pref. + Q_ASSERT(min < pref); + return (value - min) / (pref - min); + + } else { + *minToPref = false; + // Since value > pref and value != pref and max >= value, + // we can assert that max > pref. + Q_ASSERT(max > pref); + return (value - pref) / (max - pref); + } +} + void SequentialAnchorData::updateChildrenSizes() { - qreal minFactor = sizeAtMinimum / minSize; - qreal prefFactor = sizeAtPreferred / prefSize; - qreal maxFactor = sizeAtMaximum / maxSize; + // ### REMOVE ME + // ### check whether we are guarantee to get those or we need to warn stuff at this + // point. + Q_ASSERT(sizeAtMinimum > minSize || qFuzzyCompare(sizeAtMinimum, minSize)); + Q_ASSERT(sizeAtMinimum < maxSize || qFuzzyCompare(sizeAtMinimum, maxSize)); + Q_ASSERT(sizeAtPreferred > minSize || qFuzzyCompare(sizeAtPreferred, minSize)); + Q_ASSERT(sizeAtPreferred < maxSize || qFuzzyCompare(sizeAtPreferred, maxSize)); + Q_ASSERT(sizeAtMaximum > minSize || qFuzzyCompare(sizeAtMaximum, minSize)); + Q_ASSERT(sizeAtMaximum < maxSize || qFuzzyCompare(sizeAtMaximum, maxSize)); + + // Band here refers if the value is in the Minimum To Preferred + // band (the lower band) or the Preferred To Maximum (the upper band). + + bool minLowerBand; + qreal minFactor = getFactor(sizeAtMinimum, minSize, prefSize, maxSize, &minLowerBand); + + bool prefLowerBand; + qreal prefFactor = getFactor(sizeAtPreferred, minSize, prefSize, maxSize, &prefLowerBand); + + bool maxLowerBand; + qreal maxFactor = getFactor(sizeAtMaximum, minSize, prefSize, maxSize, &maxLowerBand); for (int i = 0; i < m_edges.count(); ++i) { - m_edges[i]->sizeAtMinimum = m_edges[i]->minSize * minFactor; - m_edges[i]->sizeAtPreferred = m_edges[i]->prefSize * prefFactor; - m_edges[i]->sizeAtMaximum = m_edges[i]->maxSize * maxFactor; - m_edges[i]->updateChildrenSizes(); + AnchorData *e = m_edges.at(i); + + if (minLowerBand) + e->sizeAtMinimum = e->minSize + ((e->prefSize - e->minSize) * minFactor); + else + e->sizeAtMinimum = e->prefSize + ((e->maxSize - e->prefSize) * minFactor); + + if (prefLowerBand) + e->sizeAtPreferred = e->minSize + ((e->prefSize - e->minSize) * prefFactor); + else + e->sizeAtPreferred = e->prefSize + ((e->maxSize - e->prefSize) * prefFactor); + + if (maxLowerBand) + e->sizeAtMaximum = e->minSize + ((e->prefSize - e->minSize) * maxFactor); + else + e->sizeAtMaximum = e->prefSize + ((e->maxSize - e->prefSize) * maxFactor); + + e->updateChildrenSizes(); } } diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 3bab0ce..dcda12d 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -234,7 +234,6 @@ void tst_QGraphicsAnchorLayout::parallel() // solution found by the solver at runtime. p.resize(layoutMaximumSize); QCOMPARE(a->geometry(), QRectF(0, 0, 200, 100)); - QEXPECT_FAIL("", "see commit 23441f49a23cbf936b60140c5c8a6d5cb3ca00a7 for explanation", Abort); QCOMPARE(b->geometry(), QRectF(200, 100, 175, 100)); QCOMPARE(c->geometry(), QRectF(200, 200, 350, 100)); QCOMPARE(d->geometry(), QRectF(375, 300, 175, 100)); @@ -609,9 +608,8 @@ void tst_QGraphicsAnchorLayout::proportionalPreferred() qreal factor = 12.0 / 24.0; QCOMPARE(c->size().width(), d->size().width()); - QEXPECT_FAIL("", "see commit 23441f49a23cbf936b60140c5c8a6d5cb3ca00a7 for explanation", Abort); - QCOMPARE(a->size().width() * factor, 10 * factor); - QCOMPARE(c->size().width() * factor, 14 * factor); + QCOMPARE(a->size().width(), 10 * factor); + QCOMPARE(c->size().width(), 14 * factor); QCOMPARE(p.size(), QSizeF(12, 400)); } -- cgit v0.12 From b089427c211d1da017941489fc90f37cca2f2cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Tue, 18 Aug 2009 11:07:26 +0200 Subject: Fix a potential crash in AnchorVertex::toString(). Don't assume that m_item is always a QGraphicsWidget --- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index b6cef4e..be8e32f 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -106,15 +106,16 @@ inline QString AnchorVertex::toString() const edge = QLatin1String("None"); break; } - QString item; + QString itemName; if (m_item->isLayout()) { - item = QLatin1String("layout"); + itemName = QLatin1String("layout"); } else { - QGraphicsWidget *w = static_cast(m_item); - item = w->data(0).toString(); + if (QGraphicsItem *item = m_item->graphicsItem()) { + itemName = item->data(0).toString(); + } } edge.insert(0, QLatin1String("%1_")); - return edge.arg(item); + return edge.arg(itemName); } -- cgit v0.12 From 6f4e44018389bd654cda3cc073039ae1143d0bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Tue, 18 Aug 2009 11:13:36 +0200 Subject: Just a small improvement to SequentialAnchorData::updateChildrenSizes() Functionality-wise it should be the same, its just a bit less code. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 53 +++++++++--------------- 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index d54ea8d..b5b4fb3 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -127,33 +127,31 @@ void ParallelAnchorData::refreshSizeHints(qreal effectiveSpacing) sizeAtMaximum = prefSize; } -static qreal getFactor(qreal value, qreal min, qreal pref, qreal max, bool *minToPref) +/*! + \internal + returns the factor in the interval [-1, 1]. + -1 is at Minimum + 0 is at Preferred + 1 is at Maximum +*/ +static qreal getFactor(qreal value, qreal min, qreal pref, qreal max) { // ### Maybe remove some of the assertions? (since outside is asserting us) Q_ASSERT(value > min || qFuzzyCompare(value, min)); Q_ASSERT(value < max || qFuzzyCompare(value, max)); if (qFuzzyCompare(value, min)) { - *minToPref = true; - return 0.0; - + return -1.0; } else if (qFuzzyCompare(value, pref)) { - *minToPref = true; - return 1.0; - + return 0.0; } else if (qFuzzyCompare(value, max)) { - *minToPref = false; return 1.0; - } else if (value < pref) { - *minToPref = true; // Since value < pref and value != pref and min <= value, // we can assert that min < pref. Q_ASSERT(min < pref); - return (value - min) / (pref - min); - + return (value - min) / (pref - min) - 1; } else { - *minToPref = false; // Since value > pref and value != pref and max >= value, // we can assert that max > pref. Q_ASSERT(max > pref); @@ -176,32 +174,21 @@ void SequentialAnchorData::updateChildrenSizes() // Band here refers if the value is in the Minimum To Preferred // band (the lower band) or the Preferred To Maximum (the upper band). - bool minLowerBand; - qreal minFactor = getFactor(sizeAtMinimum, minSize, prefSize, maxSize, &minLowerBand); - - bool prefLowerBand; - qreal prefFactor = getFactor(sizeAtPreferred, minSize, prefSize, maxSize, &prefLowerBand); - - bool maxLowerBand; - qreal maxFactor = getFactor(sizeAtMaximum, minSize, prefSize, maxSize, &maxLowerBand); + qreal minFactor = getFactor(sizeAtMinimum, minSize, prefSize, maxSize); + qreal prefFactor = getFactor(sizeAtPreferred, minSize, prefSize, maxSize); + qreal maxFactor = getFactor(sizeAtMaximum, minSize, prefSize, maxSize); for (int i = 0; i < m_edges.count(); ++i) { AnchorData *e = m_edges.at(i); - if (minLowerBand) - e->sizeAtMinimum = e->minSize + ((e->prefSize - e->minSize) * minFactor); - else - e->sizeAtMinimum = e->prefSize + ((e->maxSize - e->prefSize) * minFactor); + qreal bandSize = minFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; + e->sizeAtMinimum = e->prefSize + bandSize * minFactor; - if (prefLowerBand) - e->sizeAtPreferred = e->minSize + ((e->prefSize - e->minSize) * prefFactor); - else - e->sizeAtPreferred = e->prefSize + ((e->maxSize - e->prefSize) * prefFactor); + bandSize = prefFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; + e->sizeAtPreferred = e->prefSize + bandSize * prefFactor; - if (maxLowerBand) - e->sizeAtMaximum = e->minSize + ((e->prefSize - e->minSize) * maxFactor); - else - e->sizeAtMaximum = e->prefSize + ((e->maxSize - e->prefSize) * maxFactor); + bandSize = maxFactor > 0 ? e->maxSize - e->prefSize : e->prefSize - e->minSize; + e->sizeAtMaximum = e->prefSize + bandSize * maxFactor; e->updateChildrenSizes(); } -- cgit v0.12 From 825ca69c171c788e71359bf1dd94510f74f80b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Tue, 18 Aug 2009 12:26:03 +0200 Subject: Remove some warnings --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index b5b4fb3..cdb23dd 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -438,7 +438,6 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) return; bool dirty; - int count = 0; do { dirty = simplifyGraphIteration(orientation); } while (dirty); @@ -676,7 +675,6 @@ void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientatio orientation == Horizontal ? "Horizontal" : "Vertical"); #endif - Q_Q(QGraphicsAnchorLayout); Graph &g = graph[orientation]; QList > connections = g.connections(); -- cgit v0.12 From 2b53dac40715c0074fdcd64a0bf75c43322cb262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Tue, 18 Aug 2009 12:56:57 +0200 Subject: Remove some warnings --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 2 -- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index cdb23dd..f248ffa 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -873,11 +873,9 @@ void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( if (!center) return; AnchorVertex *first = internalVertex(item, firstEdge); - AnchorVertex *last = internalVertex(item, lastEdge); Q_ASSERT(first); Q_ASSERT(center); - Q_ASSERT(last); Graph &g = graph[orientation]; diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index be8e32f..144465a 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -155,6 +155,8 @@ struct AnchorData : public QSimplexVariable { virtual void updateChildrenSizes() { }; virtual void refreshSizeHints(qreal effectiveSpacing); + virtual ~AnchorData() {} + void dump(int indent = 2); inline QString toString() const; -- cgit v0.12 From 8e6c5ceb5b30bf3e9c50a9bf0710b7a0919068b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Tue, 18 Aug 2009 13:58:33 +0200 Subject: Improved internal docs for simplification. --- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 73 ++++++++++++++---------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index f248ffa..ba2aadf 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -417,6 +417,50 @@ static bool simplifySequentialChunk(Graph *graph, return newAnchor != sequence; } +/*! + \internal + + The purpose of this function is to simplify the graph. + Simplification serves two purposes: + 1. Reduce the number of edges in the graph, (thus the number of variables to the equation + solver is reduced, and the solver performs better). + 2. Be able to do distribution of sequences of edges more intelligently (esp. with sequential + anchors) + + It is essential that it must be possible to restore simplified anchors back to their "original" + form. This is done by restoreSimplifiedAnchor(). + + There are two types of simplification that can be done: + 1. Sequential simplification + Sequential simplification means that all sequences of anchors will be merged into one single + anchor. Only anhcors that points in the same direction will be merged. + 2. Parallel simplification + If a simplified sequential anchor is about to be inserted between two vertices in the graph + and there already exist an anchor between those two vertices, a parallel anchor will be + created that serves as a placeholder for the sequential anchor and the anchor that was + already between the two vertices. + + The process of simplification can be described as: + + 1. Simplify all sequences of anchors into one anchor. + If no further simplification was done, go to (3) + - If there already exist an anchor where the sequential anchor is supposed to be inserted, + take that anchor out of the graph + - Then create a parallel anchor that holds the sequential anchor and the anchor just taken + out of the graph. + 2. Go to (1) + 3. Done + + + * Gathering sequential anchors * + The algorithm walks the graph in depth-first order, and only collects vertices that has two + edges connected to it. If the vertex does not have two edges or if it is a layout edge, + it will take all the previously collected vertices and try to create a simplified sequential + anchor representing all the previously collected vertices. + Once the simplified anchor is inserted, the collected list is cleared in order to find the next + sequence to simplify. + Note that there are some catches to this that are not covered by the above explanation. +*/ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) { static bool noSimplification = !qgetenv("QT_ANCHORLAYOUT_NO_SIMPLIFICATION").isEmpty(); @@ -443,35 +487,6 @@ void QGraphicsAnchorLayoutPrivate::simplifyGraph(Orientation orientation) } while (dirty); } -/*! - * \internal - * - * The purpose of this function is to simplify the graph. The process of simplification can be - * described as: - * - * 1. Simplify all sequences of anchors into one anchor. - * If not first iteration and no further simplification was done, go to (3) - * 2. Simplify two parallel anchors into one anchor. - * If any simplification was done, go to (1) - * 3. Done - * - * - * The algorithm walks the graph in depth-first order, and only collects vertices that has two - * edges connected to it. If the vertex does not have two edges or if it is a layout edge, - * it will take all the previously collected vertices and try to create a simplified sequential - * anchor representing all the previously collected vertices. - * Once the simplified anchor is inserted, the collected list is cleared in order to find the next - * sequence to simplify. - * Note that there are some catches to this that are not covered by the above explanation. - * - * - * Notes: - * * The algorithm should not make a sequence of the layout edge anchors. - * => Make sure those edges are not traversed - * * A generic algorithm will make a sequential simplification node of a Left-HCenter-Right - * sequence. This is ok, but that sequence should not be affected by stretch factors. - * - */ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutPrivate::Orientation orientation) { Q_Q(QGraphicsAnchorLayout); -- cgit v0.12 From 14f85a02b7c13b0afc1195df815093640fd7b83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Tue, 18 Aug 2009 14:34:24 +0200 Subject: Only compile in toString() functions if we are compiling in debug mode. Also, only include the "name" member to AnchorData if we are compiling in debug mode. --- src/gui/graphicsview/qgraph_p.h | 10 +++++++--- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 13 +++++++++---- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 24 ++++++++++++++++-------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/gui/graphicsview/qgraph_p.h b/src/gui/graphicsview/qgraph_p.h index 6cb843f..c228902 100644 --- a/src/gui/graphicsview/qgraph_p.h +++ b/src/gui/graphicsview/qgraph_p.h @@ -91,14 +91,16 @@ public: void createEdge(Vertex *first, Vertex *second, EdgeData *data) { // Creates a bidirectional edge -#if 0 +#if defined(QT_DEBUG) && 0 qDebug("Graph::createEdge(): %s", qPrintable(QString::fromAscii("%1-%2") .arg(first->toString()).arg(second->toString()))); #endif if (edgeData(first, second)) { +#ifdef QT_DEBUG qWarning(qPrintable(QString::fromAscii("%1-%2 already has an edge") .arg(first->toString()).arg(second->toString()))); +#endif } createDirectedEdge(first, second, data); createDirectedEdge(second, first, data); @@ -107,7 +109,7 @@ public: void removeEdge(Vertex *first, Vertex *second) { // Removes a bidirectional edge -#if 0 +#if defined(QT_DEBUG) && 0 qDebug("Graph::removeEdge(): %s", qPrintable(QString::fromAscii("%1-%2") .arg(first->toString()).arg(second->toString()))); @@ -120,7 +122,7 @@ public: EdgeData *takeEdge(Vertex* first, Vertex* second) { -#if 0 +#if defined(QT_DEBUG) && 0 qDebug("Graph::takeEdge(): %s", qPrintable(QString::fromAscii("%1-%2") .arg(first->toString()).arg(second->toString()))); @@ -169,6 +171,7 @@ public: return conns; } +#if defined(QT_DEBUG) QString serializeToDot() { // traversal QString strVertices; QString edges; @@ -195,6 +198,7 @@ public: } return QString::fromAscii("%1\n%2\n").arg(strVertices).arg(edges); } +#endif Vertex *rootVertex() const { diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index ba2aadf..4e45fc7 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -217,6 +217,7 @@ void SequentialAnchorData::refreshSizeHints(qreal effectiveSpacing) sizeAtMaximum = prefSize; } +#ifdef QT_DEBUG void AnchorData::dump(int indent) { if (type == Parallel) { qDebug("%*s type: parallel:", indent, ""); @@ -235,6 +236,8 @@ void AnchorData::dump(int indent) { } } +#endif + QSimplexConstraint *GraphPath::constraint(const GraphPath &path) const { // Calculate @@ -262,6 +265,7 @@ QSimplexConstraint *GraphPath::constraint(const GraphPath &path) const return c; } +#ifdef QT_DEBUG QString GraphPath::toString() const { QString string(QLatin1String("Path: ")); @@ -273,7 +277,7 @@ QString GraphPath::toString() const return string; } - +#endif QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate() : calculateGraphCacheDirty(1) @@ -336,7 +340,7 @@ static bool simplifySequentialChunk(Graph *graph, AnchorVertex *after) { int i; -#if 0 +#if defined(QT_DEBUG) && 0 QString strVertices; for (i = 0; i < vertices.count(); ++i) strVertices += QString::fromAscii("%1 - ").arg(vertices.at(i)->toString()); @@ -536,7 +540,7 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP // The complete path of the sequence to simplify is: beforeSequence, , afterSequence // where beforeSequence and afterSequence are the endpoints where the anchor is inserted // between. -#if 0 +#if defined(QT_DEBUG) && 0 // ### DEBUG QString strCandidates; for (i = 0; i < candidates.count(); ++i) @@ -1086,8 +1090,9 @@ void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, // so we still know that the anchor direction is from 1 to 2. data->from = v1; data->to = v2; +#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); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 144465a..7e7cee8 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -67,8 +67,9 @@ struct AnchorVertex { AnchorVertex() : m_item(0), m_edge(QGraphicsAnchorLayout::Edge(0)) {} +#ifdef QT_DEBUG inline QString toString() const; - +#endif QGraphicsLayoutItem *m_item; QGraphicsAnchorLayout::Edge m_edge; @@ -77,6 +78,7 @@ struct AnchorVertex { qreal distance; }; +#ifdef QT_DEBUG inline QString AnchorVertex::toString() const { if (!this || !m_item) { @@ -117,7 +119,7 @@ inline QString AnchorVertex::toString() const edge.insert(0, QLatin1String("%1_")); return edge.arg(itemName); } - +#endif /*! \internal @@ -157,10 +159,11 @@ struct AnchorData : public QSimplexVariable { virtual ~AnchorData() {} +#ifdef QT_DEBUG void dump(int indent = 2); - inline QString toString() const; QString name; +#endif // Anchor is semantically directed AnchorVertex *from; @@ -195,19 +198,20 @@ protected: isLayoutAnchor(false) {} }; +#ifdef QT_DEBUG inline QString AnchorData::toString() const { return QString::fromAscii("Anchor(%1)").arg(name); - //return QString().sprintf("Anchor %%1 ", - // minSize, prefSize, maxSize).arg(name); } - +#endif struct SequentialAnchorData : public AnchorData { SequentialAnchorData() : AnchorData(AnchorData::Sequential) { +#ifdef QT_DEBUG name = QLatin1String("SequentialAnchorData"); +#endif } virtual void updateChildrenSizes(); @@ -216,7 +220,9 @@ struct SequentialAnchorData : public AnchorData void setVertices(const QVector &vertices) { m_children = vertices; +#ifdef QT_DEBUG name = QString::fromAscii("%1 -- %2").arg(vertices.first()->toString(), vertices.last()->toString()); +#endif } QVector m_children; // list of vertices in the sequence @@ -235,7 +241,9 @@ struct ParallelAnchorData : public AnchorData Q_ASSERT(first->to == second->to); from = first->from; to = first->to; +#ifdef QT_DEBUG name = QString::fromAscii("%1 | %2").arg(first->toString(), second->toString()); +#endif } virtual void updateChildrenSizes(); @@ -262,9 +270,9 @@ public: GraphPath() {}; QSimplexConstraint *constraint(const GraphPath &path) const; - +#ifdef QT_DEBUG QString toString() const; - +#endif QSet positives; QSet negatives; }; -- cgit v0.12 From 9da9e6f9bcead138297765b2782f73b68799827a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 19 Aug 2009 09:43:54 +0200 Subject: Fix one "failure" on the mac. This failed on Alexis' Mac Mini (Leopard) --- tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index dcda12d..624e80d 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -730,6 +730,9 @@ void tst_QGraphicsAnchorLayout::setSpacing() p->show(); QApplication::processEvents(); +#ifdef Q_WS_MAC + QTest::qWait(200); +#endif QCOMPARE(a->geometry(), QRectF(0, 0, 20, 20)); QCOMPARE(b->geometry(), QRectF(21, 0, 20, 20)); QCOMPARE(c->geometry(), QRectF(0, 21, 41, 20)); -- cgit v0.12 From 14c6433781fb946aab6e7f77fd190469e5fcfb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 19 Aug 2009 11:46:06 +0200 Subject: update API to what was agreed on the API review meeting yesterday: The changes are: * Move enums in QGraphicsAnchorLayout::Edge to Qt::AnchorPoint. Prefix them with Anchor since they are not edges in general. * Rename anchor() to addAnchor() * Rename anchorCorner() -> addCornerAnchors() * Rename anchorWidth() -> addLeftAndRightAnchors() * Rename anchorHeight() -> addTopAndBottomAnchors() * Rename anchorGeometry() -> addAllAnchors() * remove the overloads that take a spacing argument, and add setAnchorSpacing() to accommodate for that. * Added anchorSpacing() (implementation missing) * Added unsetAnchorSpacing(). (implementation missing) * made sizeHint() protected. Updated all examples and autotest to reflect this API change. --- examples/graphicsview/anchorlayout/main.cpp | 81 +++-- examples/layouts/anchorlayout/window.cpp | 35 +- examples/layouts/anchorlayout/window.h | 4 +- src/corelib/global/qnamespace.h | 11 + src/gui/graphicsview/qgraphicsanchorlayout.cpp | 140 +++----- src/gui/graphicsview/qgraphicsanchorlayout.h | 109 ++---- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 238 +++++++------ src/gui/graphicsview/qgraphicsanchorlayout_p.h | 83 +++-- .../tst_qgraphicsanchorlayout.cpp | 379 +++++++++++++-------- 9 files changed, 584 insertions(+), 496 deletions(-) diff --git a/examples/graphicsview/anchorlayout/main.cpp b/examples/graphicsview/anchorlayout/main.cpp index 1c87fa0..5427bbf 100644 --- a/examples/graphicsview/anchorlayout/main.cpp +++ b/examples/graphicsview/anchorlayout/main.cpp @@ -44,37 +44,60 @@ int main(int argc, char **argv) w->setLayout(l); // vertical - l->anchor(a, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top, 0); - l->anchor(b, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top, 0); - - l->anchor(c, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(c, QGraphicsAnchorLayout::Top, b, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top, 0); - - l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(e, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); - - l->anchor(c, QGraphicsAnchorLayout::Top, f, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::VCenter, f, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(f, QGraphicsAnchorLayout::Bottom, g, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, g, QGraphicsAnchorLayout::Bottom, 0); + l->addAnchor(a, Qt::AnchorTop, l, Qt::AnchorTop); + l->setAnchorSpacing(a, Qt::AnchorTop, l, Qt::AnchorTop, 0); + l->addAnchor(b, Qt::AnchorTop, l, Qt::AnchorTop); + l->setAnchorSpacing(b, Qt::AnchorTop, l, Qt::AnchorTop, 0); + + l->addAnchor(c, Qt::AnchorTop, a, Qt::AnchorBottom); + l->setAnchorSpacing(c, Qt::AnchorTop, a, Qt::AnchorBottom, 0); + l->addAnchor(c, Qt::AnchorTop, b, Qt::AnchorBottom); + l->setAnchorSpacing(c, Qt::AnchorTop, b, Qt::AnchorBottom, 0); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->setAnchorSpacing(c, Qt::AnchorBottom, d, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorBottom, e, Qt::AnchorTop); + l->setAnchorSpacing(c, Qt::AnchorBottom, e, Qt::AnchorTop, 0); + + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(d, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + l->addAnchor(e, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(e, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + l->addAnchor(c, Qt::AnchorTop, f, Qt::AnchorTop); + l->setAnchorSpacing(c, Qt::AnchorTop, f, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorVerticalCenter, f, Qt::AnchorBottom); + l->setAnchorSpacing(c, Qt::AnchorVerticalCenter, f, Qt::AnchorBottom, 0); + l->addAnchor(f, Qt::AnchorBottom, g, Qt::AnchorTop); + l->setAnchorSpacing(f, Qt::AnchorBottom, g, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorBottom, g, Qt::AnchorBottom); + l->setAnchorSpacing(c, Qt::AnchorBottom, g, Qt::AnchorBottom, 0); // horizontal - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); - l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left, 0); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left, 0); - - l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left, 0); - l->anchor(c, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); - - l->anchor(b, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); - l->anchor(e, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); - l->anchor(d, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); - - l->anchor(l, QGraphicsAnchorLayout::Left, f, QGraphicsAnchorLayout::Left, 0); - l->anchor(l, QGraphicsAnchorLayout::Left, g, QGraphicsAnchorLayout::Left, 0); - l->anchor(f, QGraphicsAnchorLayout::Right, g, QGraphicsAnchorLayout::Right, 0); + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + l->addAnchor(l, Qt::AnchorLeft, d, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, d, Qt::AnchorLeft, 0); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + l->setAnchorSpacing(a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->setAnchorSpacing(a, Qt::AnchorRight, c, Qt::AnchorLeft, 0); + l->addAnchor(c, Qt::AnchorRight, e, Qt::AnchorLeft); + l->setAnchorSpacing(c, Qt::AnchorRight, e, Qt::AnchorLeft, 0); + + l->addAnchor(b, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(b, Qt::AnchorRight, l, Qt::AnchorRight, 0); + l->addAnchor(e, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(e, Qt::AnchorRight, l, Qt::AnchorRight, 0); + l->addAnchor(d, Qt::AnchorRight, e, Qt::AnchorLeft); + l->setAnchorSpacing(d, Qt::AnchorRight, e, Qt::AnchorLeft, 0); + + l->addAnchor(l, Qt::AnchorLeft, f, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, f, Qt::AnchorLeft, 0); + l->addAnchor(l, Qt::AnchorLeft, g, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, g, Qt::AnchorLeft, 0); + l->addAnchor(f, Qt::AnchorRight, g, Qt::AnchorRight); + l->setAnchorSpacing(f, Qt::AnchorRight, g, Qt::AnchorRight, 0); scene.addItem(w); scene.setBackgroundBrush(Qt::darkGreen); diff --git a/examples/layouts/anchorlayout/window.cpp b/examples/layouts/anchorlayout/window.cpp index d8d68e2..cab1b60 100644 --- a/examples/layouts/anchorlayout/window.cpp +++ b/examples/layouts/anchorlayout/window.cpp @@ -190,17 +190,17 @@ void Window::rebuildLayout() QGraphicsLayoutItem *startItem = layoutItemAt(m_ui.anchors->model(), i, 0); if (!startItem) continue; - QGraphicsAnchorLayout::Edge startEdge = (QGraphicsAnchorLayout::Edge)(m_ui.anchors->item(i, 1)->data(Qt::UserRole).toInt(&ok)); + Qt::AnchorPoint startEdge = (Qt::AnchorPoint)(m_ui.anchors->item(i, 1)->data(Qt::UserRole).toInt(&ok)); if (!ok) continue; QGraphicsLayoutItem *endItem = layoutItemAt(m_ui.anchors->model(), i, 2); if (!endItem) continue; - QGraphicsAnchorLayout::Edge endEdge = (QGraphicsAnchorLayout::Edge)(m_ui.anchors->item(i, 3)->data(Qt::UserRole).toInt(&ok)); + Qt::AnchorPoint endEdge = (Qt::AnchorPoint)(m_ui.anchors->item(i, 3)->data(Qt::UserRole).toInt(&ok)); if (!ok) continue; - m_layout->anchor(startItem, startEdge, endItem, endEdge); + m_layout->addAnchor(startItem, startEdge, endItem, endEdge); } } @@ -232,8 +232,8 @@ static const char *strEdges[] = {"Left", "VCenter", "Bottom"}; -void Window::setAnchorData(QGraphicsLayoutItem *startItem, const QString &startName, QGraphicsAnchorLayout::Edge startEdge, - QGraphicsLayoutItem *endItem, const QString &endName, QGraphicsAnchorLayout::Edge endEdge, int row /*= -1*/) +void Window::setAnchorData(QGraphicsLayoutItem *startItem, const QString &startName, Qt::AnchorPoint startEdge, + QGraphicsLayoutItem *endItem, const QString &endName, Qt::AnchorPoint endEdge, int row /*= -1*/) { if (row == -1) { row = m_ui.anchors->rowCount(); @@ -286,7 +286,7 @@ void Window::addAnchorRow() QString defaultName = defaultLayoutItem->isLayout() ? QLatin1String("layout") : defaultLayoutItem->graphicsItem()->data(0).toString(); - setAnchorData(defaultLayoutItem, defaultName, QGraphicsAnchorLayout::Right, defaultLayoutItem, defaultName, QGraphicsAnchorLayout::Left, rc); + setAnchorData(defaultLayoutItem, defaultName, Qt::AnchorRight, defaultLayoutItem, defaultName, Qt::AnchorLeft, rc); rebuildLayout(); } } @@ -324,7 +324,6 @@ bool Window::saveLayout(const QString& fileName) xml.writeAttribute(QLatin1String("id"), name); for (int p = 0; p < 3; ++p) { const char *propertyNames[] = {"minimumSize", "preferredSize", "maximumSize"}; - int b; typedef QSizeF (QGraphicsLayoutItem::*QGLISizeGetter)(void) const; QGLISizeGetter sizeGetters[] = { &QGraphicsLayoutItem::minimumSize, &QGraphicsLayoutItem::preferredSize, @@ -353,13 +352,13 @@ bool Window::saveLayout(const QString& fileName) QGraphicsLayoutItem *startItem = layoutItemAt(m_ui.anchors->model(), i, 0); if (!startItem) continue; - QGraphicsAnchorLayout::Edge startEdge = (QGraphicsAnchorLayout::Edge)(m_ui.anchors->item(i, 1)->data(Qt::UserRole).toInt(&ok)); + Qt::AnchorPoint startEdge = (Qt::AnchorPoint)(m_ui.anchors->item(i, 1)->data(Qt::UserRole).toInt(&ok)); if (!ok) continue; QGraphicsLayoutItem *endItem = layoutItemAt(m_ui.anchors->model(), i, 2); if (!endItem) continue; - QGraphicsAnchorLayout::Edge endEdge = (QGraphicsAnchorLayout::Edge)(m_ui.anchors->item(i, 3)->data(Qt::UserRole).toInt(&ok)); + Qt::AnchorPoint endEdge = (Qt::AnchorPoint)(m_ui.anchors->item(i, 3)->data(Qt::UserRole).toInt(&ok)); if (!ok) continue; @@ -416,24 +415,24 @@ static bool parseProperty(QXmlStreamReader *xml, QString *name, QSizeF *size) return false; } -static bool parseEdge(const QString &itemEdge, QByteArray *id, QGraphicsAnchorLayout::Edge *edge) +static bool parseEdge(const QString &itemEdge, QByteArray *id, Qt::AnchorPoint *edge) { QStringList item_edge = itemEdge.split(QLatin1Char('.')); bool ok = item_edge.count() == 2; if (ok) { QByteArray strEdge = item_edge.at(1).toAscii().toLower(); if (strEdge == "left") { - *edge = QGraphicsAnchorLayout::Left; + *edge = Qt::AnchorLeft; } else if (strEdge == "hcenter") { - *edge = QGraphicsAnchorLayout::HCenter; + *edge = Qt::AnchorHorizontalCenter; } else if (strEdge == "right") { - *edge = QGraphicsAnchorLayout::Right; + *edge = Qt::AnchorRight; } else if (strEdge == "top") { - *edge = QGraphicsAnchorLayout::Top; + *edge = Qt::AnchorTop; } else if (strEdge == "vcenter") { - *edge = QGraphicsAnchorLayout::VCenter; + *edge = Qt::AnchorVerticalCenter; } else if (strEdge == "bottom") { - *edge = QGraphicsAnchorLayout::Bottom; + *edge = Qt::AnchorBottom; } else { ok = false; } @@ -482,7 +481,7 @@ bool Window::loadLayout(const QString& fileName, QGraphicsAnchorLayout *layout) QString first = attrs.value("first").toString(); QString second = attrs.value("second").toString(); QByteArray startID; - QGraphicsAnchorLayout::Edge startEdge; + Qt::AnchorPoint startEdge; QGraphicsLayoutItem *startItem = 0; if (parseEdge(first, &startID, &startEdge)) { if (startID == "this") { @@ -497,7 +496,7 @@ bool Window::loadLayout(const QString& fileName, QGraphicsAnchorLayout *layout) } QByteArray endID; - QGraphicsAnchorLayout::Edge endEdge; + Qt::AnchorPoint endEdge; QGraphicsLayoutItem *endItem = 0; if (parseEdge(second, &endID, &endEdge)) { if (endID == "this") { diff --git a/examples/layouts/anchorlayout/window.h b/examples/layouts/anchorlayout/window.h index c81dbac..f87b2c9 100644 --- a/examples/layouts/anchorlayout/window.h +++ b/examples/layouts/anchorlayout/window.h @@ -36,8 +36,8 @@ private: void setItemData(const QString &name, const QSizeF &min, const QSizeF &pref, const QSizeF &max); QGraphicsLayoutItem *addItem(const QString &name = QString()); void addAnchorRow(); - void setAnchorData(QGraphicsLayoutItem *startItem, const QString &startName, QGraphicsAnchorLayout::Edge startEdge, - QGraphicsLayoutItem *endItem, const QString &endName, QGraphicsAnchorLayout::Edge endEdge, int row = -1); + void setAnchorData(QGraphicsLayoutItem *startItem, const QString &startName, Qt::AnchorPoint startEdge, + QGraphicsLayoutItem *endItem, const QString &endName, Qt::AnchorPoint endEdge, int row = -1); bool saveLayout(const QString& fileName); bool loadLayout(const QString& fileName, QGraphicsAnchorLayout *layout); diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 7770fd6..5b63fe1 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -1419,6 +1419,17 @@ public: RightToLeft }; + enum AnchorPoint { + AnchorLeft = 0, + AnchorHorizontalCenter, + AnchorRight, + AnchorTop, + AnchorVerticalCenter, + AnchorBottom + }; + + + enum DropAction { CopyAction = 0x1, MoveAction = 0x2, diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index a0e1101..db79dae 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -56,7 +56,7 @@ QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; QGraphicsWidget *a = new QGraphicsWidget; QGraphicsWidget *b = new QGraphicsWidget; - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left); + 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 @@ -114,7 +114,7 @@ QGraphicsAnchorLayout::~QGraphicsAnchorLayout() /*! * Creates an anchor between the edge \a firstEdge of item \a firstItem and the edge \a secondEdge * of item \a secondItem. The magnitude of the anchor is picked up from the style. Anchors - * between a layout edge and an item edge will have a size of 0. + * between a layout edge and an item edge will have a size of 0. * 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 @@ -126,31 +126,27 @@ QGraphicsAnchorLayout::~QGraphicsAnchorLayout() * the default vertical spacing). For all other anchor combinations, the spacing will be 0. * All anchoring functions will follow this rule. * - * \sa removeAnchor, anchorCorner, anchorWidth, anchorHeight, anchorGeometry + * The spacing can also be set manually by using setAnchorSpacing() method. + * + * \sa removeAnchor, addCornerAnchors, addLeftAndRightAnchors, addTopAndBottomAnchors, addAllAnchors */ -void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, - Edge firstEdge, - QGraphicsLayoutItem *secondItem, - Edge secondEdge) +void QGraphicsAnchorLayout::addAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) { Q_D(QGraphicsAnchorLayout); d->anchor(firstItem, firstEdge, secondItem, secondEdge); invalidate(); } -/*! - * \overload - * - * By calling this function the caller can specify the magnitude of the anchor with \a spacing. - * - */ -void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, - Edge firstEdge, - QGraphicsLayoutItem *secondItem, - Edge secondEdge, qreal spacing) +void QGraphicsAnchorLayout::setAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge, + qreal spacing) { Q_D(QGraphicsAnchorLayout); - d->anchor(firstItem, firstEdge, secondItem, secondEdge, &spacing); + + if (!d->setAnchorSize(firstItem, firstEdge, secondItem, secondEdge, spacing)) { + qWarning("setAnchorSpacing: The anchor does not exist."); + } invalidate(); } @@ -163,14 +159,14 @@ void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, * This is a convenience function, since anchoring corners can be expressed as anchoring two edges. * For instance, * \code - * layout->anchor(layout, QGraphicsAnchorLayout::Top, b, QGraphicsAnchorLayout::Top); - * layout->anchor(layout, QGraphicsAnchorLayout::Left, b, QGraphicsAnchorLayout::Left); + * layout->addAnchor(layout, Qt::AnchorTop, b, Qt::AnchorTop); + * layout->addAnchor(layout, Qt::AnchorLeft, b, Qt::AnchorLeft); * \endcode * * has the same effect as * * \code - * layout->anchorCorner(layout, Qt::TopLeft, b, Qt::TopLeft); + * layout->addCornerAnchors(layout, Qt::TopLeft, b, Qt::TopLeft); * \endcode * * If there is already an anchor between the edge pairs, it will be replaced by the anchors that @@ -179,116 +175,76 @@ void QGraphicsAnchorLayout::anchor(QGraphicsLayoutItem *firstItem, * \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. */ -void QGraphicsAnchorLayout::anchorCorner(QGraphicsLayoutItem *firstItem, - Qt::Corner firstCorner, - QGraphicsLayoutItem *secondItem, - Qt::Corner secondCorner) +void QGraphicsAnchorLayout::addCornerAnchors(QGraphicsLayoutItem *firstItem, + Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, + Qt::Corner secondCorner) { Q_D(QGraphicsAnchorLayout); // Horizontal anchor - QGraphicsAnchorLayout::Edge firstEdge = (firstCorner & 1 ? QGraphicsAnchorLayout::Right: QGraphicsAnchorLayout::Left); - QGraphicsAnchorLayout::Edge secondEdge = (secondCorner & 1 ? QGraphicsAnchorLayout::Right: QGraphicsAnchorLayout::Left); + Qt::AnchorPoint firstEdge = (firstCorner & 1 ? Qt::AnchorRight: Qt::AnchorLeft); + Qt::AnchorPoint secondEdge = (secondCorner & 1 ? Qt::AnchorRight: Qt::AnchorLeft); d->anchor(firstItem, firstEdge, secondItem, secondEdge); // Vertical anchor - firstEdge = (firstCorner & 2 ? QGraphicsAnchorLayout::Bottom: QGraphicsAnchorLayout::Top); - secondEdge = (secondCorner & 2 ? QGraphicsAnchorLayout::Bottom: QGraphicsAnchorLayout::Top); + firstEdge = (firstCorner & 2 ? Qt::AnchorBottom: Qt::AnchorTop); + secondEdge = (secondCorner & 2 ? Qt::AnchorBottom: Qt::AnchorTop); d->anchor(firstItem, firstEdge, secondItem, secondEdge); invalidate(); } /*! - * \overload - * - * By calling this function the caller can specify the magnitude of the anchor with \a spacing. - * - */ -void QGraphicsAnchorLayout::anchorCorner(QGraphicsLayoutItem *firstItem, - Qt::Corner firstCorner, - QGraphicsLayoutItem *secondItem, - Qt::Corner secondCorner, qreal spacing) -{ - Q_D(QGraphicsAnchorLayout); - - // Horizontal anchor - QGraphicsAnchorLayout::Edge firstEdge = (firstCorner & 1 ? QGraphicsAnchorLayout::Right: QGraphicsAnchorLayout::Left); - QGraphicsAnchorLayout::Edge secondEdge = (secondCorner & 1 ? QGraphicsAnchorLayout::Right: QGraphicsAnchorLayout::Left); - d->anchor(firstItem, firstEdge, secondItem, secondEdge, &spacing); - - // Vertical anchor - firstEdge = (firstCorner & 2 ? QGraphicsAnchorLayout::Bottom: QGraphicsAnchorLayout::Top); - secondEdge = (secondCorner & 2 ? QGraphicsAnchorLayout::Bottom: QGraphicsAnchorLayout::Top); - d->anchor(firstItem, firstEdge, secondItem, secondEdge, &spacing); - - invalidate(); -} - -/*! - \fn QGraphicsAnchorLayout::anchorWidth(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo = 0) + \fn QGraphicsAnchorLayout::addLeftAndRightAnchors(QGraphicsLayoutItem *firstEdge, QGraphicsLayoutItem *secondEdge) This convenience function is equivalent to calling \code - if (!relativeTo) - relativeTo = lay; - lay->anchor(relativeTo, QGraphicsAnchorLayout::Left, item, QGraphicsAnchorLayout::Left); - lay->anchor(relativeTo, QGraphicsAnchorLayout::Right, item, QGraphicsAnchorLayout::Right); + l->addAnchor(firstEdge, Qt::AnchorLeft, secondEdge, Qt::AnchorLeft); + l->addAnchor(firstEdge, Qt::AnchorRight, secondEdge, Qt::AnchorRight); \endcode */ /*! - \fn QGraphicsAnchorLayout::anchorWidth(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo, qreal spacing) - - \overload - - By calling this function the caller can specify the magnitude of the anchor with \a spacing. -*/ - -/*! - \fn QGraphicsAnchorLayout::anchorHeight(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo = 0) + \fn QGraphicsAnchorLayout::addTopAndBottomAnchors(QGraphicsLayoutItem *firstEdge, QGraphicsLayoutItem *secondEdge) This convenience function is equivalent to calling \code - if (!relativeTo) - relativeTo = lay; - lay->anchor(relativeTo, QGraphicsAnchorLayout::Top, item, QGraphicsAnchorLayout::Top); - lay->anchor(relativeTo, QGraphicsAnchorLayout::Bottom, item, QGraphicsAnchorLayout::Bottom); + l->addAnchor(firstEdge, Qt::AnchorTop, secondEdge, Qt::AnchorTop); + l->addAnchor(firstEdge, Qt::AnchorBottom, secondEdge, Qt::AnchorBottom); \endcode */ /*! - \fn QGraphicsAnchorLayout::anchorHeight(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo, qreal spacing) - - \overload - - By calling this function the caller can specify the magnitude of the anchor with \a spacing. -*/ - -/*! - \fn QGraphicsAnchorLayout::anchorGeometry(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo = 0) + \fn QGraphicsAnchorLayout::addAllAnchors(QGraphicsLayoutItem *firstEdge, QGraphicsLayoutItem *secondEdge) This convenience function is equivalent to calling \code - anchorWidth(item, relativeTo); - anchorHeight(item, relativeTo); + l->addLeftAndRightAnchors(firstEdge, secondEdge); + l->addTopAndBottomAnchors(firstEdge, secondEdge); \endcode */ -/*! - \fn QGraphicsAnchorLayout::anchorGeometry(QGraphicsLayoutItem *item, QGraphicsLayoutItem *relativeTo, qreal spacing) - - \overload +qreal QGraphicsAnchorLayout::anchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) const +{ + qWarning("// ### TO BE IMPLEMENTED"); + return 0; +} - By calling this function the caller can specify the magnitude of the anchor with \a spacing. -*/ +void QGraphicsAnchorLayout::unsetAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) +{ + qWarning("// ### TO BE IMPLEMENTED"); + invalidate(); +} /*! Removes the anchor between the edge \a firstEdge of item \a firstItem and the edge \a secondEdge of item \a secondItem. If such an anchor does not exist, the layout will be left unchanged. */ -void QGraphicsAnchorLayout::removeAnchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, - QGraphicsLayoutItem *secondItem, Edge secondEdge) +void QGraphicsAnchorLayout::removeAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) { Q_D(QGraphicsAnchorLayout); if ((firstItem == 0) || (secondItem == 0)) { diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h index f0ffcbd..f2335a8 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -59,49 +59,35 @@ class QGraphicsAnchorLayoutPrivate; class Q_GUI_EXPORT QGraphicsAnchorLayout : public QGraphicsLayout { public: - enum Edge { - Left = 0, - HCenter, - Right, - Top, - VCenter, - Bottom - }; - QGraphicsAnchorLayout(QGraphicsLayoutItem *parent = 0); virtual ~QGraphicsAnchorLayout(); - void anchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, - QGraphicsLayoutItem *secondItem, Edge secondEdge); + void addAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge); - void anchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, - QGraphicsLayoutItem *secondItem, Edge secondEdge, - qreal spacing); + void setAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge, + qreal spacing); - void anchorCorner(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner, - QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner); + void addCornerAnchors(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner, + QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner); - void anchorCorner(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner, - QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner, - qreal spacing); + inline void addLeftAndRightAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem); - void removeAnchor(QGraphicsLayoutItem *firstItem, Edge firstEdge, - QGraphicsLayoutItem *secondItem, Edge secondEdge); + inline void addTopAndBottomAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem); - inline void anchorWidth(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo = 0); - inline void anchorWidth(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo, qreal spacing); + inline void addAllAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem); - inline void anchorHeight(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo = 0); - inline void anchorHeight(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo, qreal spacing); + qreal anchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) const; + void unsetAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge); - inline void anchorGeometry(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo = 0); - inline void anchorGeometry(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo, qreal spacing); + void removeAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge); void setHorizontalSpacing(qreal spacing); void setVerticalSpacing(qreal spacing); @@ -115,6 +101,7 @@ public: QGraphicsLayoutItem *itemAt(int index) const; void invalidate(); +protected: QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; private: @@ -122,58 +109,26 @@ private: Q_DECLARE_PRIVATE(QGraphicsAnchorLayout) }; -void QGraphicsAnchorLayout::anchorWidth(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo) -{ - if (!relativeTo) - relativeTo = this; - - anchor(relativeTo, Left, item, Left); - anchor(item, Right, relativeTo, Right); -} - -void QGraphicsAnchorLayout::anchorWidth(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo, - qreal spacing) -{ - anchor(relativeTo, Left, item, Left, spacing); - anchor(item, Right, relativeTo, Right, spacing); -} - -void QGraphicsAnchorLayout::anchorHeight(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo) -{ - if (!relativeTo) - relativeTo = this; - - anchor(relativeTo, Top, item, Top); - anchor(item, Bottom, relativeTo, Bottom); -} -void QGraphicsAnchorLayout::anchorHeight(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo, - qreal spacing) +void QGraphicsAnchorLayout::addLeftAndRightAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem) { - anchor(relativeTo, Top, item, Top, spacing); - anchor(item, Bottom, relativeTo, Bottom, spacing); + addAnchor(secondItem, Qt::AnchorLeft, firstItem, Qt::AnchorLeft); + addAnchor(firstItem, Qt::AnchorRight, secondItem, Qt::AnchorRight); } -void QGraphicsAnchorLayout::anchorGeometry(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo) +void QGraphicsAnchorLayout::addTopAndBottomAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem) { - if (!relativeTo) - relativeTo = this; - - anchorWidth(item, relativeTo); - anchorHeight(item, relativeTo); + addAnchor(secondItem, Qt::AnchorTop, firstItem, Qt::AnchorTop); + addAnchor(firstItem, Qt::AnchorBottom, secondItem, Qt::AnchorBottom); } -void QGraphicsAnchorLayout::anchorGeometry(QGraphicsLayoutItem *item, - QGraphicsLayoutItem *relativeTo, - qreal spacing) +void QGraphicsAnchorLayout::addAllAnchors(QGraphicsLayoutItem *firstItem, + QGraphicsLayoutItem *secondItem) { - anchorWidth(item, relativeTo, spacing); - anchorHeight(item, relativeTo, spacing); + addLeftAndRightAnchors(firstItem, secondItem); + addTopAndBottomAnchors(firstItem, secondItem); } #endif diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 4e45fc7..436ce2c 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -56,14 +56,14 @@ void AnchorData::refreshSizeHints(qreal effectiveSpacing) minSize = item->minimumWidth(); prefSize = item->preferredWidth(); maxSize = item->maximumWidth(); - hasCenter = (from->m_edge == QGraphicsAnchorLayout::HCenter - || to->m_edge == QGraphicsAnchorLayout::HCenter); + hasCenter = (from->m_edge == Qt::AnchorHorizontalCenter + || to->m_edge == Qt::AnchorHorizontalCenter); } else { minSize = item->minimumHeight(); prefSize = item->preferredHeight(); maxSize = item->maximumHeight(); - hasCenter = (from->m_edge == QGraphicsAnchorLayout::VCenter - || to->m_edge == QGraphicsAnchorLayout::VCenter); + hasCenter = (from->m_edge == Qt::AnchorVerticalCenter + || to->m_edge == Qt::AnchorVerticalCenter); } if (hasCenter) { @@ -288,21 +288,20 @@ QGraphicsAnchorLayoutPrivate::QGraphicsAnchorLayoutPrivate() } } -QGraphicsAnchorLayout::Edge QGraphicsAnchorLayoutPrivate::oppositeEdge( - QGraphicsAnchorLayout::Edge edge) +Qt::AnchorPoint QGraphicsAnchorLayoutPrivate::oppositeEdge(Qt::AnchorPoint edge) { switch (edge) { - case QGraphicsAnchorLayout::Left: - edge = QGraphicsAnchorLayout::Right; + case Qt::AnchorLeft: + edge = Qt::AnchorRight; break; - case QGraphicsAnchorLayout::Right: - edge = QGraphicsAnchorLayout::Left; + case Qt::AnchorRight: + edge = Qt::AnchorLeft; break; - case QGraphicsAnchorLayout::Top: - edge = QGraphicsAnchorLayout::Bottom; + case Qt::AnchorTop: + edge = Qt::AnchorBottom; break; - case QGraphicsAnchorLayout::Bottom: - edge = QGraphicsAnchorLayout::Top; + case Qt::AnchorBottom: + edge = Qt::AnchorTop; break; default: break; @@ -502,8 +501,8 @@ bool QGraphicsAnchorLayoutPrivate::simplifyGraphIteration(QGraphicsAnchorLayoutP stack.push(v); QVector candidates; - const QGraphicsAnchorLayout::Edge centerEdge = pickEdge(QGraphicsAnchorLayout::HCenter, orientation); - const QGraphicsAnchorLayout::Edge layoutEdge = oppositeEdge(v->m_edge); + const Qt::AnchorPoint centerEdge = pickEdge(Qt::AnchorHorizontalCenter, orientation); + const Qt::AnchorPoint layoutEdge = oppositeEdge(v->m_edge); bool dirty = false; @@ -710,9 +709,9 @@ void QGraphicsAnchorLayoutPrivate::restoreSimplifiedGraph(Orientation orientatio } QGraphicsAnchorLayoutPrivate::Orientation -QGraphicsAnchorLayoutPrivate::edgeOrientation(QGraphicsAnchorLayout::Edge edge) +QGraphicsAnchorLayoutPrivate::edgeOrientation(Qt::AnchorPoint edge) { - return edge > QGraphicsAnchorLayout::Right ? Vertical : Horizontal; + return edge > Qt::AnchorRight ? Vertical : Horizontal; } /*! @@ -731,22 +730,22 @@ void QGraphicsAnchorLayoutPrivate::createLayoutEdges() // Horizontal AnchorData *data = new AnchorData(0, 0, QWIDGETSIZE_MAX); - addAnchor(layout, QGraphicsAnchorLayout::Left, layout, - QGraphicsAnchorLayout::Right, data); + addAnchor(layout, Qt::AnchorLeft, layout, + Qt::AnchorRight, data); data->skipInPreferred = 1; // Set the Layout Left edge as the root of the horizontal graph. - AnchorVertex *v = internalVertex(layout, QGraphicsAnchorLayout::Left); + AnchorVertex *v = internalVertex(layout, Qt::AnchorLeft); graph[Horizontal].setRootVertex(v); // Vertical data = new AnchorData(0, 0, QWIDGETSIZE_MAX); - addAnchor(layout, QGraphicsAnchorLayout::Top, layout, - QGraphicsAnchorLayout::Bottom, data); + addAnchor(layout, Qt::AnchorTop, layout, + Qt::AnchorBottom, data); data->skipInPreferred = 1; // Set the Layout Top edge as the root of the vertical graph. - v = internalVertex(layout, QGraphicsAnchorLayout::Top); + v = internalVertex(layout, Qt::AnchorTop); graph[Vertical].setRootVertex(v); } @@ -754,11 +753,11 @@ void QGraphicsAnchorLayoutPrivate::deleteLayoutEdges() { Q_Q(QGraphicsAnchorLayout); - Q_ASSERT(internalVertex(q, QGraphicsAnchorLayout::HCenter) == NULL); - Q_ASSERT(internalVertex(q, QGraphicsAnchorLayout::VCenter) == NULL); + Q_ASSERT(internalVertex(q, Qt::AnchorHorizontalCenter) == NULL); + Q_ASSERT(internalVertex(q, Qt::AnchorVerticalCenter) == NULL); - removeAnchor(q, QGraphicsAnchorLayout::Left, q, QGraphicsAnchorLayout::Right); - removeAnchor(q, QGraphicsAnchorLayout::Top, q, QGraphicsAnchorLayout::Bottom); + removeAnchor(q, Qt::AnchorLeft, q, Qt::AnchorRight); + removeAnchor(q, Qt::AnchorTop, q, Qt::AnchorBottom); } void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) @@ -773,8 +772,8 @@ void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) int maximumSize = item->maximumWidth(); AnchorData *data = new AnchorData(minimumSize, preferredSize, maximumSize); - addAnchor(item, QGraphicsAnchorLayout::Left, item, - QGraphicsAnchorLayout::Right, data); + addAnchor(item, Qt::AnchorLeft, item, + Qt::AnchorRight, data); // Vertical minimumSize = item->minimumHeight(); @@ -782,8 +781,8 @@ void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) maximumSize = item->maximumHeight(); data = new AnchorData(minimumSize, preferredSize, maximumSize); - addAnchor(item, QGraphicsAnchorLayout::Top, item, - QGraphicsAnchorLayout::Bottom, data); + addAnchor(item, Qt::AnchorTop, item, + Qt::AnchorBottom, data); } /*! @@ -798,14 +797,14 @@ void QGraphicsAnchorLayoutPrivate::createItemEdges(QGraphicsLayoutItem *item) these anchors must have the same time at all times. */ void QGraphicsAnchorLayoutPrivate::createCenterAnchors( - QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge) + QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge) { Orientation orientation; switch (centerEdge) { - case QGraphicsAnchorLayout::HCenter: + case Qt::AnchorHorizontalCenter: orientation = Horizontal; break; - case QGraphicsAnchorLayout::VCenter: + case Qt::AnchorVerticalCenter: orientation = Vertical; break; default: @@ -820,15 +819,15 @@ void QGraphicsAnchorLayoutPrivate::createCenterAnchors( return; // Orientation code - QGraphicsAnchorLayout::Edge firstEdge; - QGraphicsAnchorLayout::Edge lastEdge; + Qt::AnchorPoint firstEdge; + Qt::AnchorPoint lastEdge; if (orientation == Horizontal) { - firstEdge = QGraphicsAnchorLayout::Left; - lastEdge = QGraphicsAnchorLayout::Right; + firstEdge = Qt::AnchorLeft; + lastEdge = Qt::AnchorRight; } else { - firstEdge = QGraphicsAnchorLayout::Top; - lastEdge = QGraphicsAnchorLayout::Bottom; + firstEdge = Qt::AnchorTop; + lastEdge = Qt::AnchorBottom; } AnchorVertex *first = internalVertex(item, firstEdge); @@ -858,15 +857,15 @@ void QGraphicsAnchorLayoutPrivate::createCenterAnchors( } void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( - QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge, + QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge, bool substitute) { Orientation orientation; switch (centerEdge) { - case QGraphicsAnchorLayout::HCenter: + case Qt::AnchorHorizontalCenter: orientation = Horizontal; break; - case QGraphicsAnchorLayout::VCenter: + case Qt::AnchorVerticalCenter: orientation = Vertical; break; default: @@ -877,15 +876,15 @@ void QGraphicsAnchorLayoutPrivate::removeCenterAnchors( Q_ASSERT(!graphSimplified[orientation]); // Orientation code - QGraphicsAnchorLayout::Edge firstEdge; - QGraphicsAnchorLayout::Edge lastEdge; + Qt::AnchorPoint firstEdge; + Qt::AnchorPoint lastEdge; if (orientation == Horizontal) { - firstEdge = QGraphicsAnchorLayout::Left; - lastEdge = QGraphicsAnchorLayout::Right; + firstEdge = Qt::AnchorLeft; + lastEdge = Qt::AnchorRight; } else { - firstEdge = QGraphicsAnchorLayout::Top; - lastEdge = QGraphicsAnchorLayout::Bottom; + firstEdge = Qt::AnchorTop; + lastEdge = Qt::AnchorBottom; } AnchorVertex *center = internalVertex(item, centerEdge); @@ -952,11 +951,11 @@ void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem * // so that we can remove those easily AnchorVertex *first = internalVertex(item, orientation == Horizontal ? - QGraphicsAnchorLayout::Left : - QGraphicsAnchorLayout::Top); + Qt::AnchorLeft : + Qt::AnchorTop); AnchorVertex *center = internalVertex(item, orientation == Horizontal ? - QGraphicsAnchorLayout::HCenter : - QGraphicsAnchorLayout::VCenter); + Qt::AnchorHorizontalCenter : + Qt::AnchorVerticalCenter); // Skip if no center constraints exist if (!center) @@ -981,26 +980,26 @@ void QGraphicsAnchorLayoutPrivate::removeCenterConstraints(QGraphicsLayoutItem * * If \a spacing is 0, it will pick up the spacing defined by the style. */ void QGraphicsAnchorLayoutPrivate::anchor(QGraphicsLayoutItem *firstItem, - QGraphicsAnchorLayout::Edge firstEdge, + Qt::AnchorPoint firstEdge, QGraphicsLayoutItem *secondItem, - QGraphicsAnchorLayout::Edge secondEdge, + Qt::AnchorPoint secondEdge, qreal *spacing) { Q_Q(QGraphicsAnchorLayout); if ((firstItem == 0) || (secondItem == 0)) { - qWarning("QGraphicsAnchorLayout::anchor(): " + qWarning("QGraphicsAnchorLayout::addAnchor(): " "Cannot anchor NULL items"); return; } if (firstItem == secondItem) { - qWarning("QGraphicsAnchorLayout::anchor(): " + qWarning("QGraphicsAnchorLayout::addAnchor(): " "Cannot anchor the item to itself"); return; } if (edgeOrientation(secondEdge) != edgeOrientation(firstEdge)) { - qWarning("QGraphicsAnchorLayout::anchor(): " + qWarning("QGraphicsAnchorLayout::addAnchor(): " "Cannot anchor edges of different orientations"); return; } @@ -1047,7 +1046,7 @@ void QGraphicsAnchorLayoutPrivate::anchor(QGraphicsLayoutItem *firstItem, // Right ? 0 0 if (firstItem != q && secondItem != q - && pickEdge(firstEdge, Horizontal) != QGraphicsAnchorLayout::HCenter + && pickEdge(firstEdge, Horizontal) != Qt::AnchorHorizontalCenter && oppositeEdge(firstEdge) == secondEdge) { data = new AnchorData; // ask the style later } else { @@ -1064,9 +1063,9 @@ void QGraphicsAnchorLayoutPrivate::anchor(QGraphicsLayoutItem *firstItem, } void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, - QGraphicsAnchorLayout::Edge firstEdge, + Qt::AnchorPoint firstEdge, QGraphicsLayoutItem *secondItem, - QGraphicsAnchorLayout::Edge secondEdge, + Qt::AnchorPoint secondEdge, AnchorData *data) { Q_Q(QGraphicsAnchorLayout); @@ -1099,10 +1098,51 @@ void QGraphicsAnchorLayoutPrivate::addAnchor(QGraphicsLayoutItem *firstItem, graph[edgeOrientation(firstEdge)].createEdge(v1, v2, data); } +void QGraphicsAnchorLayoutPrivate::removeAnchor(QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge) +{ + // Guarantee that the graph is no simplified when adding this anchor, + // anchor manipulation always happen in the full graph + restoreSimplifiedGraph(edgeOrientation(firstEdge)); + + // Look for both vertices + AnchorVertex *v1 = internalVertex(firstItem, firstEdge); + AnchorVertex *v2 = internalVertex(secondItem, secondEdge); + + Q_ASSERT(v1 && v2); + + // Remove edge from graph + graph[edgeOrientation(firstEdge)].removeEdge(v1, v2); + + // Decrease vertices reference count (may trigger a deletion) + removeInternalVertex(firstItem, firstEdge); + removeInternalVertex(secondItem, secondEdge); +} + +bool QGraphicsAnchorLayoutPrivate::setAnchorSize(const QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + qreal anchorSize) +{ + // ### we can avoid restoration if we really want to + restoreSimplifiedGraph(edgeOrientation(firstEdge)); + AnchorVertex *v1 = internalVertex(firstItem, firstEdge); + AnchorVertex *v2 = internalVertex(secondItem, secondEdge); + + AnchorData *data = graph[edgeOrientation(firstEdge)].edgeData(v1, v2); + if (data) + data->setFixedSize(anchorSize); + + return data; +} + AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutItem *item, - QGraphicsAnchorLayout::Edge edge) + Qt::AnchorPoint edge) { - QPair pair(item, edge); + QPair pair(item, edge); QPair v = m_vertexList.value(pair); if (!v.first) { @@ -1121,9 +1161,9 @@ AnchorVertex *QGraphicsAnchorLayoutPrivate::addInternalVertex(QGraphicsLayoutIte * returns 0 if it did not exist. */ void QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *item, - QGraphicsAnchorLayout::Edge edge) + Qt::AnchorPoint edge) { - QPair pair(item, edge); + QPair pair(item, edge); QPair v = m_vertexList.value(pair); if (!v.first) { @@ -1141,37 +1181,14 @@ void QGraphicsAnchorLayoutPrivate::removeInternalVertex(QGraphicsLayoutItem *ite m_vertexList.insert(pair, v); if ((v.second == 2) && - ((edge == QGraphicsAnchorLayout::HCenter) || - (edge == QGraphicsAnchorLayout::VCenter))) { + ((edge == Qt::AnchorHorizontalCenter) || + (edge == Qt::AnchorVerticalCenter))) { removeCenterAnchors(item, edge, true); } } } -void QGraphicsAnchorLayoutPrivate::removeAnchor(QGraphicsLayoutItem *firstItem, - QGraphicsAnchorLayout::Edge firstEdge, - QGraphicsLayoutItem *secondItem, - QGraphicsAnchorLayout::Edge secondEdge) -{ - // Guarantee that the graph is no simplified when adding this anchor, - // anchor manipulation always happen in the full graph - restoreSimplifiedGraph(edgeOrientation(firstEdge)); - - // Look for both vertices - AnchorVertex *v1 = internalVertex(firstItem, firstEdge); - AnchorVertex *v2 = internalVertex(secondItem, secondEdge); - - Q_ASSERT(v1 && v2); - - // Remove edge from graph - graph[edgeOrientation(firstEdge)].removeEdge(v1, v2); - - // Decrease vertices reference count (may trigger a deletion) - removeInternalVertex(firstItem, firstEdge); - removeInternalVertex(secondItem, secondEdge); -} - -void QGraphicsAnchorLayoutPrivate::removeVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge) +void QGraphicsAnchorLayoutPrivate::removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge) { if (AnchorVertex *v = internalVertex(item, edge)) { Graph &g = graph[edgeOrientation(edge)]; @@ -1190,14 +1207,13 @@ void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item) Q_ASSERT(!graphSimplified[Horizontal] && !graphSimplified[Vertical]); // remove the center anchor first!! - removeCenterAnchors(item, QGraphicsAnchorLayout::HCenter, false); - removeVertex(item, QGraphicsAnchorLayout::Left); - removeVertex(item, QGraphicsAnchorLayout::Right); - - removeCenterAnchors(item, QGraphicsAnchorLayout::VCenter, false); - removeVertex(item, QGraphicsAnchorLayout::Top); - removeVertex(item, QGraphicsAnchorLayout::Bottom); + removeCenterAnchors(item, Qt::AnchorHorizontalCenter, false); + removeVertex(item, Qt::AnchorLeft); + removeVertex(item, Qt::AnchorRight); + removeCenterAnchors(item, Qt::AnchorVerticalCenter, false); + removeVertex(item, Qt::AnchorTop); + removeVertex(item, Qt::AnchorBottom); } /*! @@ -1225,14 +1241,14 @@ void QGraphicsAnchorLayoutPrivate::removeAnchors(QGraphicsLayoutItem *item) and Layout Right as another item's Left. */ void QGraphicsAnchorLayoutPrivate::correctEdgeDirection(QGraphicsLayoutItem *&firstItem, - QGraphicsAnchorLayout::Edge &firstEdge, + Qt::AnchorPoint &firstEdge, QGraphicsLayoutItem *&secondItem, - QGraphicsAnchorLayout::Edge &secondEdge) + Qt::AnchorPoint &secondEdge) { Q_Q(QGraphicsAnchorLayout); - QGraphicsAnchorLayout::Edge effectiveFirst = firstEdge; - QGraphicsAnchorLayout::Edge effectiveSecond = secondEdge; + Qt::AnchorPoint effectiveFirst = firstEdge; + Qt::AnchorPoint effectiveSecond = secondEdge; if (firstItem == q) effectiveFirst = QGraphicsAnchorLayoutPrivate::oppositeEdge(firstEdge); @@ -1375,7 +1391,7 @@ void QGraphicsAnchorLayoutPrivate::calculateGraphs( // objective function. // Retrieve that path - AnchorVertex *v = internalVertex(q, pickEdge(QGraphicsAnchorLayout::Right, orientation)); + AnchorVertex *v = internalVertex(q, pickEdge(Qt::AnchorRight, orientation)); GraphPath trunkPath = graphPaths[orientation].value(v); if (!trunkConstraints.isEmpty()) { @@ -1607,13 +1623,13 @@ QGraphicsAnchorLayoutPrivate::getGraphParts(Orientation orientation) // Find layout vertices and edges for the current orientation. AnchorVertex *layoutFirstVertex = \ - internalVertex(q, pickEdge(QGraphicsAnchorLayout::Left, orientation)); + internalVertex(q, pickEdge(Qt::AnchorLeft, orientation)); AnchorVertex *layoutCentralVertex = \ - internalVertex(q, pickEdge(QGraphicsAnchorLayout::HCenter, orientation)); + internalVertex(q, pickEdge(Qt::AnchorHorizontalCenter, orientation)); AnchorVertex *layoutLastVertex = \ - internalVertex(q, pickEdge(QGraphicsAnchorLayout::Right, orientation)); + internalVertex(q, pickEdge(Qt::AnchorRight, orientation)); Q_ASSERT(layoutFirstVertex && layoutLastVertex); @@ -1710,10 +1726,10 @@ void QGraphicsAnchorLayoutPrivate::setItemsGeometries() AnchorVertex *firstH, *secondH, *firstV, *secondV; foreach (QGraphicsLayoutItem *item, items) { - firstH = internalVertex(item, QGraphicsAnchorLayout::Left); - secondH = internalVertex(item, QGraphicsAnchorLayout::Right); - firstV = internalVertex(item, QGraphicsAnchorLayout::Top); - secondV = internalVertex(item, QGraphicsAnchorLayout::Bottom); + firstH = internalVertex(item, Qt::AnchorLeft); + secondH = internalVertex(item, Qt::AnchorRight); + firstV = internalVertex(item, Qt::AnchorTop); + secondV = internalVertex(item, Qt::AnchorBottom); QPointF topLeft(firstH->distance, firstV->distance); QPointF bottomRight(secondH->distance, secondV->distance); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 7e7cee8..742108d 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -48,7 +48,7 @@ /* The public QGraphicsAnchorLayout interface represents an anchorage point - as a pair of a and a . + as a pair of a and a . Internally though, it has a graph of anchorage points (vertices) and anchors (edges), represented by the AnchorVertex and AnchorData structs @@ -61,17 +61,17 @@ Represents a vertex (anchorage point) in the internal graph */ struct AnchorVertex { - AnchorVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge) + AnchorVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge) : m_item(item), m_edge(edge) {} AnchorVertex() - : m_item(0), m_edge(QGraphicsAnchorLayout::Edge(0)) {} + : m_item(0), m_edge(Qt::AnchorPoint(0)) {} #ifdef QT_DEBUG inline QString toString() const; #endif QGraphicsLayoutItem *m_item; - QGraphicsAnchorLayout::Edge m_edge; + Qt::AnchorPoint m_edge; // Current distance from this vertex to the layout edge (Left or Top) // Value is calculated from the current anchors sizes. @@ -86,22 +86,22 @@ inline QString AnchorVertex::toString() const } QString edge; switch (m_edge) { - case QGraphicsAnchorLayout::Left: + case Qt::AnchorLeft: edge = QLatin1String("Left"); break; - case QGraphicsAnchorLayout::HCenter: + case Qt::AnchorHorizontalCenter: edge = QLatin1String("HorizontalCenter"); break; - case QGraphicsAnchorLayout::Right: + case Qt::AnchorRight: edge = QLatin1String("Right"); break; - case QGraphicsAnchorLayout::Top: + case Qt::AnchorTop: edge = QLatin1String("Top"); break; - case QGraphicsAnchorLayout::VCenter: + case Qt::AnchorVerticalCenter: edge = QLatin1String("VerticalCenter"); break; - case QGraphicsAnchorLayout::Bottom: + case Qt::AnchorBottom: edge = QLatin1String("Bottom"); break; default: @@ -165,6 +165,17 @@ struct AnchorData : public QSimplexVariable { QString name; #endif + inline void setFixedSize(qreal size) + { + minSize = size; + prefSize = size; + maxSize = size; + sizeAtMinimum = size; + sizeAtPreferred = size; + sizeAtMaximum = size; + hasSize = true; + } + // Anchor is semantically directed AnchorVertex *from; AnchorVertex *to; @@ -310,17 +321,17 @@ public: QGraphicsAnchorLayoutPrivate(); - static QGraphicsAnchorLayout::Edge oppositeEdge( - QGraphicsAnchorLayout::Edge edge); + static Qt::AnchorPoint oppositeEdge( + Qt::AnchorPoint edge); - static Orientation edgeOrientation(QGraphicsAnchorLayout::Edge edge); + static Orientation edgeOrientation(Qt::AnchorPoint edge); - static QGraphicsAnchorLayout::Edge pickEdge(QGraphicsAnchorLayout::Edge edge, Orientation orientation) + static Qt::AnchorPoint pickEdge(Qt::AnchorPoint edge, Orientation orientation) { if (orientation == Vertical && int(edge) <= 2) - return (QGraphicsAnchorLayout::Edge)(edge + 3); + return (Qt::AnchorPoint)(edge + 3); else if (orientation == Horizontal && int(edge) >= 3) { - return (QGraphicsAnchorLayout::Edge)(edge - 3); + return (Qt::AnchorPoint)(edge - 3); } return edge; } @@ -329,37 +340,43 @@ public: void createLayoutEdges(); void deleteLayoutEdges(); void createItemEdges(QGraphicsLayoutItem *item); - void createCenterAnchors(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge); - void removeCenterAnchors(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge centerEdge, bool substitute = true); + void createCenterAnchors(QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge); + void removeCenterAnchors(QGraphicsLayoutItem *item, Qt::AnchorPoint centerEdge, bool substitute = true); void removeCenterConstraints(QGraphicsLayoutItem *item, Orientation orientation); // helper function used by the 4 API functions void anchor(QGraphicsLayoutItem *firstItem, - QGraphicsAnchorLayout::Edge firstEdge, + Qt::AnchorPoint firstEdge, QGraphicsLayoutItem *secondItem, - QGraphicsAnchorLayout::Edge secondEdge, + Qt::AnchorPoint secondEdge, qreal *spacing = 0); // Anchor Manipulation methods void addAnchor(QGraphicsLayoutItem *firstItem, - QGraphicsAnchorLayout::Edge firstEdge, + Qt::AnchorPoint firstEdge, QGraphicsLayoutItem *secondItem, - QGraphicsAnchorLayout::Edge secondEdge, + Qt::AnchorPoint secondEdge, AnchorData *data); void removeAnchor(QGraphicsLayoutItem *firstItem, - QGraphicsAnchorLayout::Edge firstEdge, + Qt::AnchorPoint firstEdge, QGraphicsLayoutItem *secondItem, - QGraphicsAnchorLayout::Edge secondEdge); + Qt::AnchorPoint secondEdge); + + bool setAnchorSize(const QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + qreal anchorSize); void removeAnchors(QGraphicsLayoutItem *item); - void removeVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge); + void removeVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge); void correctEdgeDirection(QGraphicsLayoutItem *&firstItem, - QGraphicsAnchorLayout::Edge &firstEdge, + Qt::AnchorPoint &firstEdge, QGraphicsLayoutItem *&secondItem, - QGraphicsAnchorLayout::Edge &secondEdge); + Qt::AnchorPoint &secondEdge); // for getting the actual spacing (will query the style if the // spacing is not explicitly set). qreal effectiveSpacing(Orientation orientation) const; @@ -377,18 +394,18 @@ public: QList constraintsFromSizeHints(const QList &anchors); QList > getGraphParts(Orientation orientation); - inline AnchorVertex *internalVertex(const QPair &itemEdge) + inline AnchorVertex *internalVertex(const QPair &itemEdge) { return m_vertexList.value(itemEdge).first; } - inline AnchorVertex *internalVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge) + inline AnchorVertex *internalVertex(const QGraphicsLayoutItem *item, Qt::AnchorPoint edge) { - return internalVertex(qMakePair(item, edge)); + return internalVertex(qMakePair(const_cast(item), edge)); } - AnchorVertex *addInternalVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge); - void removeInternalVertex(QGraphicsLayoutItem *item, QGraphicsAnchorLayout::Edge edge); + AnchorVertex *addInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge); + void removeInternalVertex(QGraphicsLayoutItem *item, Qt::AnchorPoint edge); // Geometry interpolation methods void setItemsGeometries(); @@ -421,7 +438,7 @@ public: // Mapping between high level anchorage points (Item, Edge) to low level // ones (Graph Vertices) - QHash, QPair > m_vertexList; + QHash, QPair > m_vertexList; // Internal graph of anchorage points and anchors, for both orientations Graph graph[2]; diff --git a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp index 624e80d..a579ba5 100644 --- a/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp +++ b/tests/auto/qgraphicsanchorlayout/tst_qgraphicsanchorlayout.cpp @@ -57,7 +57,7 @@ void tst_QGraphicsAnchorLayout::simple() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); - l->anchor(w1, QGraphicsAnchorLayout::Right, w2, QGraphicsAnchorLayout::Left); + l->addAnchor(w1, Qt::AnchorRight, w2, Qt::AnchorLeft); QGraphicsWidget p; p.setLayout(l); @@ -81,28 +81,45 @@ void tst_QGraphicsAnchorLayout::diagonal() l->setContentsMargins(0, 0, 0, 0); // vertical - l->anchor(a, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top, 0); - l->anchor(b, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top, 0); - - l->anchor(c, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(c, QGraphicsAnchorLayout::Top, b, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top, 0); - - l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(e, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + l->addAnchor(a, Qt::AnchorTop, l, Qt::AnchorTop); + l->setAnchorSpacing(a, Qt::AnchorTop, l, Qt::AnchorTop, 0); + + l->addAnchor(b, Qt::AnchorTop, l, Qt::AnchorTop); + l->setAnchorSpacing(b, Qt::AnchorTop, l, Qt::AnchorTop, 0); + + l->addAnchor(c, Qt::AnchorTop, a, Qt::AnchorBottom); + l->setAnchorSpacing(c, Qt::AnchorTop, a, Qt::AnchorBottom, 0); + l->addAnchor(c, Qt::AnchorTop, b, Qt::AnchorBottom); + l->setAnchorSpacing(c, Qt::AnchorTop, b, Qt::AnchorBottom, 0); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->setAnchorSpacing(c, Qt::AnchorBottom, d, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorBottom, e, Qt::AnchorTop); + l->setAnchorSpacing(c, Qt::AnchorBottom, e, Qt::AnchorTop, 0); + + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(d, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + l->addAnchor(e, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(e, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); // horizontal - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); - l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left, 0); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left, 0); - - l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left, 0); - l->anchor(c, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); - - l->anchor(b, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); - l->anchor(e, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); - l->anchor(d, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + l->addAnchor(l, Qt::AnchorLeft, d, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, d, Qt::AnchorLeft, 0); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + l->setAnchorSpacing(a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->setAnchorSpacing(a, Qt::AnchorRight, c, Qt::AnchorLeft, 0); + l->addAnchor(c, Qt::AnchorRight, e, Qt::AnchorLeft); + l->setAnchorSpacing(c, Qt::AnchorRight, e, Qt::AnchorLeft, 0); + + l->addAnchor(b, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(b, Qt::AnchorRight, l, Qt::AnchorRight, 0); + l->addAnchor(e, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(e, Qt::AnchorRight, l, Qt::AnchorRight, 0); + l->addAnchor(d, Qt::AnchorRight, e, Qt::AnchorLeft); + l->setAnchorSpacing(d, Qt::AnchorRight, e, Qt::AnchorLeft, 0); QCOMPARE(l->count(), 5); @@ -180,23 +197,39 @@ void tst_QGraphicsAnchorLayout::parallel() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); - l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); - l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); - l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); - l->anchor(d, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top, 0); - l->anchor(e, QGraphicsAnchorLayout::Bottom, f, QGraphicsAnchorLayout::Top, 0); - l->anchor(f, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); - - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left, 0); - l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left, 0); - l->anchor(b, QGraphicsAnchorLayout::Right, d, QGraphicsAnchorLayout::Left, 0); - l->anchor(b, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); - l->anchor(c, QGraphicsAnchorLayout::Right, f, QGraphicsAnchorLayout::Left, 0); - l->anchor(d, QGraphicsAnchorLayout::Right, f, QGraphicsAnchorLayout::Left, 0); - l->anchor(e, QGraphicsAnchorLayout::Right, f, QGraphicsAnchorLayout::Left, 0); - l->anchor(f, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->setAnchorSpacing(l, Qt::AnchorTop, a, Qt::AnchorTop, 0); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->setAnchorSpacing(a, Qt::AnchorBottom, b, Qt::AnchorTop, 0); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->setAnchorSpacing(b, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->setAnchorSpacing(c, Qt::AnchorBottom, d, Qt::AnchorTop, 0); + l->addAnchor(d, Qt::AnchorBottom, e, Qt::AnchorTop); + l->setAnchorSpacing(d, Qt::AnchorBottom, e, Qt::AnchorTop, 0); + l->addAnchor(e, Qt::AnchorBottom, f, Qt::AnchorTop); + l->setAnchorSpacing(e, Qt::AnchorBottom, f, Qt::AnchorTop, 0); + l->addAnchor(f, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(f, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + l->setAnchorSpacing(a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->setAnchorSpacing(a, Qt::AnchorRight, c, Qt::AnchorLeft, 0); + l->addAnchor(b, Qt::AnchorRight, d, Qt::AnchorLeft); + l->setAnchorSpacing(b, Qt::AnchorRight, d, Qt::AnchorLeft, 0); + l->addAnchor(b, Qt::AnchorRight, e, Qt::AnchorLeft); + l->setAnchorSpacing(b, Qt::AnchorRight, e, Qt::AnchorLeft, 0); + l->addAnchor(c, Qt::AnchorRight, f, Qt::AnchorLeft); + l->setAnchorSpacing(c, Qt::AnchorRight, f, Qt::AnchorLeft, 0); + l->addAnchor(d, Qt::AnchorRight, f, Qt::AnchorLeft); + l->setAnchorSpacing(d, Qt::AnchorRight, f, Qt::AnchorLeft, 0); + l->addAnchor(e, Qt::AnchorRight, f, Qt::AnchorLeft); + l->setAnchorSpacing(e, Qt::AnchorRight, f, Qt::AnchorLeft, 0); + l->addAnchor(f, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(f, Qt::AnchorRight, l, Qt::AnchorRight, 0); QCOMPARE(l->count(), 6); @@ -255,13 +288,18 @@ void tst_QGraphicsAnchorLayout::parallel2() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); - l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); - l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); - l->anchor(b, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->setAnchorSpacing(l, Qt::AnchorTop, a, Qt::AnchorTop, 0); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->setAnchorSpacing(a, Qt::AnchorBottom, b, Qt::AnchorTop, 0); + l->addAnchor(b, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(b, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); - l->anchorWidth(l, a); - l->anchor(l, QGraphicsAnchorLayout::Left, b, QGraphicsAnchorLayout::Left, 0); - l->anchor(b, QGraphicsAnchorLayout::Right, a, QGraphicsAnchorLayout::Right, 0); + l->addLeftAndRightAnchors(l, a); + l->addAnchor(l, Qt::AnchorLeft, b, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, b, Qt::AnchorLeft, 0); + l->addAnchor(b, Qt::AnchorRight, a, Qt::AnchorRight); + l->setAnchorSpacing(b, Qt::AnchorRight, a, Qt::AnchorRight, 0); QCOMPARE(l->count(), 2); @@ -303,15 +341,23 @@ void tst_QGraphicsAnchorLayout::snake() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); - l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); - l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); - l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); - - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Right, 0); - l->anchor(b, QGraphicsAnchorLayout::Left, c, QGraphicsAnchorLayout::Left, 0); - l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->setAnchorSpacing(l, Qt::AnchorTop, a, Qt::AnchorTop, 0); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->setAnchorSpacing(a, Qt::AnchorBottom, b, Qt::AnchorTop, 0); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->setAnchorSpacing(b, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(c, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorRight); + l->setAnchorSpacing(a, Qt::AnchorRight, b, Qt::AnchorRight, 0); + l->addAnchor(b, Qt::AnchorLeft, c, Qt::AnchorLeft); + l->setAnchorSpacing(b, Qt::AnchorLeft, c, Qt::AnchorLeft, 0); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(c, Qt::AnchorRight, l, Qt::AnchorRight, 0); QCOMPARE(l->count(), 3); @@ -362,18 +408,26 @@ void tst_QGraphicsAnchorLayout::snakeOppositeDirections() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); - l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); - l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); - l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->setAnchorSpacing(l, Qt::AnchorTop, a, Qt::AnchorTop, 0); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->setAnchorSpacing(a, Qt::AnchorBottom, b, Qt::AnchorTop, 0); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->setAnchorSpacing(b, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(c, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); // Both a and c are 'pointing' to b - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Right, 0); - l->anchor(c, QGraphicsAnchorLayout::Left, b, QGraphicsAnchorLayout::Left, 0); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorRight); + l->setAnchorSpacing(a, Qt::AnchorRight, b, Qt::AnchorRight, 0); + l->addAnchor(c, Qt::AnchorLeft, b, Qt::AnchorLeft); + l->setAnchorSpacing(c, Qt::AnchorLeft, b, Qt::AnchorLeft, 0); - l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(c, Qt::AnchorRight, l, Qt::AnchorRight, 0); QCOMPARE(l->count(), 3); @@ -429,18 +483,29 @@ void tst_QGraphicsAnchorLayout::fairDistribution() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); - l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); - l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); - l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); - l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); - - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left, 0); - l->anchor(b, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left, 0); - l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); - l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left, 0); - l->anchor(d, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->setAnchorSpacing(l, Qt::AnchorTop, a, Qt::AnchorTop, 0); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->setAnchorSpacing(a, Qt::AnchorBottom, b, Qt::AnchorTop, 0); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->setAnchorSpacing(b, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->setAnchorSpacing(c, Qt::AnchorBottom, d, Qt::AnchorTop, 0); + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(d, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + l->setAnchorSpacing(a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + l->addAnchor(b, Qt::AnchorRight, c, Qt::AnchorLeft); + l->setAnchorSpacing(b, Qt::AnchorRight, c, Qt::AnchorLeft, 0); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(c, Qt::AnchorRight, l, Qt::AnchorRight, 0); + l->addAnchor(l, Qt::AnchorLeft, d, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, d, Qt::AnchorLeft, 0); + l->addAnchor(d, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(d, Qt::AnchorRight, l, Qt::AnchorRight, 0); QCOMPARE(l->count(), 4); @@ -503,19 +568,30 @@ void tst_QGraphicsAnchorLayout::fairDistributionOppositeDirections() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); - l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); - l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); - l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); - l->anchor(d, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top, 0); - l->anchor(e, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); - - l->anchor(a, QGraphicsAnchorLayout::Left, l, QGraphicsAnchorLayout::Left, 0); - l->anchor(b, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Right, 0); - l->anchor(c, QGraphicsAnchorLayout::Left, b, QGraphicsAnchorLayout::Right, 0); - l->anchor(d, QGraphicsAnchorLayout::Left, c, QGraphicsAnchorLayout::Right, 0); - l->anchor(d, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); - l->anchorWidth(l, e, 0); + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->setAnchorSpacing(l, Qt::AnchorTop, a, Qt::AnchorTop, 0); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->setAnchorSpacing(a, Qt::AnchorBottom, b, Qt::AnchorTop, 0); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->setAnchorSpacing(b, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->setAnchorSpacing(c, Qt::AnchorBottom, d, Qt::AnchorTop, 0); + l->addAnchor(d, Qt::AnchorBottom, e, Qt::AnchorTop); + l->setAnchorSpacing(d, Qt::AnchorBottom, e, Qt::AnchorTop, 0); + l->addAnchor(e, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(e, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + l->addAnchor(a, Qt::AnchorLeft, l, Qt::AnchorLeft); + l->setAnchorSpacing(a, Qt::AnchorLeft, l, Qt::AnchorLeft, 0); + l->addAnchor(b, Qt::AnchorLeft, a, Qt::AnchorRight); + l->setAnchorSpacing(b, Qt::AnchorLeft, a, Qt::AnchorRight, 0); + l->addAnchor(c, Qt::AnchorLeft, b, Qt::AnchorRight); + l->setAnchorSpacing(c, Qt::AnchorLeft, b, Qt::AnchorRight, 0); + l->addAnchor(d, Qt::AnchorLeft, c, Qt::AnchorRight); + l->setAnchorSpacing(d, Qt::AnchorLeft, c, Qt::AnchorRight, 0); + l->addAnchor(d, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(d, Qt::AnchorRight, l, Qt::AnchorRight, 0); + l->addLeftAndRightAnchors(l, e); QCOMPARE(l->count(), 5); @@ -564,19 +640,31 @@ void tst_QGraphicsAnchorLayout::proportionalPreferred() QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; l->setContentsMargins(0, 0, 0, 0); - l->anchor(l, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Top, 0); - l->anchor(a, QGraphicsAnchorLayout::Bottom, b, QGraphicsAnchorLayout::Top, 0); - l->anchor(b, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); - l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); - - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); - l->anchor(l, QGraphicsAnchorLayout::Left, b, QGraphicsAnchorLayout::Left, 0); - l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left, 0); - l->anchor(a, QGraphicsAnchorLayout::Right, d, QGraphicsAnchorLayout::Left, 0); - l->anchor(b, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); - l->anchor(c, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); - l->anchor(d, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); + l->addAnchor(l, Qt::AnchorTop, a, Qt::AnchorTop); + l->setAnchorSpacing(l, Qt::AnchorTop, a, Qt::AnchorTop, 0); + l->addAnchor(a, Qt::AnchorBottom, b, Qt::AnchorTop); + l->setAnchorSpacing(a, Qt::AnchorBottom, b, Qt::AnchorTop, 0); + l->addAnchor(b, Qt::AnchorBottom, c, Qt::AnchorTop); + l->setAnchorSpacing(b, Qt::AnchorBottom, c, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->setAnchorSpacing(c, Qt::AnchorBottom, d, Qt::AnchorTop, 0); + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(d, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + l->addAnchor(l, Qt::AnchorLeft, b, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, b, Qt::AnchorLeft, 0); + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->setAnchorSpacing(a, Qt::AnchorRight, c, Qt::AnchorLeft, 0); + l->addAnchor(a, Qt::AnchorRight, d, Qt::AnchorLeft); + l->setAnchorSpacing(a, Qt::AnchorRight, d, Qt::AnchorLeft, 0); + l->addAnchor(b, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(b, Qt::AnchorRight, l, Qt::AnchorRight, 0); + l->addAnchor(c, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(c, Qt::AnchorRight, l, Qt::AnchorRight, 0); + l->addAnchor(d, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(d, Qt::AnchorRight, l, Qt::AnchorRight, 0); QCOMPARE(l->count(), 4); @@ -631,37 +719,60 @@ void tst_QGraphicsAnchorLayout::example() l->setContentsMargins(0, 0, 0, 0); // vertical - l->anchor(a, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top, 0); - l->anchor(b, QGraphicsAnchorLayout::Top, l, QGraphicsAnchorLayout::Top, 0); - - l->anchor(c, QGraphicsAnchorLayout::Top, a, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(c, QGraphicsAnchorLayout::Top, b, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, d, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, e, QGraphicsAnchorLayout::Top, 0); - - l->anchor(d, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(e, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom, 0); - - l->anchor(c, QGraphicsAnchorLayout::Top, f, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::VCenter, f, QGraphicsAnchorLayout::Bottom, 0); - l->anchor(f, QGraphicsAnchorLayout::Bottom, g, QGraphicsAnchorLayout::Top, 0); - l->anchor(c, QGraphicsAnchorLayout::Bottom, g, QGraphicsAnchorLayout::Bottom, 0); + l->addAnchor(a, Qt::AnchorTop, l, Qt::AnchorTop); + l->setAnchorSpacing(a, Qt::AnchorTop, l, Qt::AnchorTop, 0); + l->addAnchor(b, Qt::AnchorTop, l, Qt::AnchorTop); + l->setAnchorSpacing(b, Qt::AnchorTop, l, Qt::AnchorTop, 0); + + l->addAnchor(c, Qt::AnchorTop, a, Qt::AnchorBottom); + l->setAnchorSpacing(c, Qt::AnchorTop, a, Qt::AnchorBottom, 0); + l->addAnchor(c, Qt::AnchorTop, b, Qt::AnchorBottom); + l->setAnchorSpacing(c, Qt::AnchorTop, b, Qt::AnchorBottom, 0); + l->addAnchor(c, Qt::AnchorBottom, d, Qt::AnchorTop); + l->setAnchorSpacing(c, Qt::AnchorBottom, d, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorBottom, e, Qt::AnchorTop); + l->setAnchorSpacing(c, Qt::AnchorBottom, e, Qt::AnchorTop, 0); + + l->addAnchor(d, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(d, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + l->addAnchor(e, Qt::AnchorBottom, l, Qt::AnchorBottom); + l->setAnchorSpacing(e, Qt::AnchorBottom, l, Qt::AnchorBottom, 0); + + l->addAnchor(c, Qt::AnchorTop, f, Qt::AnchorTop); + l->setAnchorSpacing(c, Qt::AnchorTop, f, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorVerticalCenter, f, Qt::AnchorBottom); + l->setAnchorSpacing(c, Qt::AnchorVerticalCenter, f, Qt::AnchorBottom, 0); + l->addAnchor(f, Qt::AnchorBottom, g, Qt::AnchorTop); + l->setAnchorSpacing(f, Qt::AnchorBottom, g, Qt::AnchorTop, 0); + l->addAnchor(c, Qt::AnchorBottom, g, Qt::AnchorBottom); + l->setAnchorSpacing(c, Qt::AnchorBottom, g, Qt::AnchorBottom, 0); // horizontal - l->anchor(l, QGraphicsAnchorLayout::Left, a, QGraphicsAnchorLayout::Left, 0); - l->anchor(l, QGraphicsAnchorLayout::Left, d, QGraphicsAnchorLayout::Left, 0); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left, 0); - - l->anchor(a, QGraphicsAnchorLayout::Right, c, QGraphicsAnchorLayout::Left, 0); - l->anchor(c, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); - - l->anchor(b, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); - l->anchor(e, QGraphicsAnchorLayout::Right, l, QGraphicsAnchorLayout::Right, 0); - l->anchor(d, QGraphicsAnchorLayout::Right, e, QGraphicsAnchorLayout::Left, 0); - - l->anchor(l, QGraphicsAnchorLayout::Left, f, QGraphicsAnchorLayout::Left, 0); - l->anchor(l, QGraphicsAnchorLayout::Left, g, QGraphicsAnchorLayout::Left, 0); - l->anchor(f, QGraphicsAnchorLayout::Right, g, QGraphicsAnchorLayout::Right, 0); + l->addAnchor(l, Qt::AnchorLeft, a, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, a, Qt::AnchorLeft, 0); + l->addAnchor(l, Qt::AnchorLeft, d, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, d, Qt::AnchorLeft, 0); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); + l->setAnchorSpacing(a, Qt::AnchorRight, b, Qt::AnchorLeft, 0); + + l->addAnchor(a, Qt::AnchorRight, c, Qt::AnchorLeft); + l->setAnchorSpacing(a, Qt::AnchorRight, c, Qt::AnchorLeft, 0); + l->addAnchor(c, Qt::AnchorRight, e, Qt::AnchorLeft); + l->setAnchorSpacing(c, Qt::AnchorRight, e, Qt::AnchorLeft, 0); + + l->addAnchor(b, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(b, Qt::AnchorRight, l, Qt::AnchorRight, 0); + l->addAnchor(e, Qt::AnchorRight, l, Qt::AnchorRight); + l->setAnchorSpacing(e, Qt::AnchorRight, l, Qt::AnchorRight, 0); + l->addAnchor(d, Qt::AnchorRight, e, Qt::AnchorLeft); + l->setAnchorSpacing(d, Qt::AnchorRight, e, Qt::AnchorLeft, 0); + + l->addAnchor(l, Qt::AnchorLeft, f, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, f, Qt::AnchorLeft, 0); + l->addAnchor(l, Qt::AnchorLeft, g, Qt::AnchorLeft); + l->setAnchorSpacing(l, Qt::AnchorLeft, g, Qt::AnchorLeft, 0); + l->addAnchor(f, Qt::AnchorRight, g, Qt::AnchorRight); + l->setAnchorSpacing(f, Qt::AnchorRight, g, Qt::AnchorRight, 0); QCOMPARE(l->count(), 7); @@ -706,14 +817,14 @@ void tst_QGraphicsAnchorLayout::setSpacing() QGraphicsWidget *c = createItem(min, pref, max); QGraphicsAnchorLayout *l = new QGraphicsAnchorLayout; - l->anchorCorner(l, Qt::TopLeftCorner, a, Qt::TopLeftCorner); - l->anchorCorner(b, Qt::TopRightCorner, l, Qt::TopRightCorner); - l->anchor(a, QGraphicsAnchorLayout::Right, b, QGraphicsAnchorLayout::Left); + l->addCornerAnchors(l, Qt::TopLeftCorner, a, Qt::TopLeftCorner); + l->addCornerAnchors(b, Qt::TopRightCorner, l, Qt::TopRightCorner); + l->addAnchor(a, Qt::AnchorRight, b, Qt::AnchorLeft); - l->anchorWidth(c); + l->addLeftAndRightAnchors(l, c); - l->anchor(a, QGraphicsAnchorLayout::Bottom, c, QGraphicsAnchorLayout::Top); - l->anchor(c, QGraphicsAnchorLayout::Bottom, l, QGraphicsAnchorLayout::Bottom); + l->addAnchor(a, Qt::AnchorBottom, c, Qt::AnchorTop); + l->addAnchor(c, Qt::AnchorBottom, l, Qt::AnchorBottom); QGraphicsWidget *p = new QGraphicsWidget(0, Qt::Window); -- cgit v0.12 From 784bef39ca505e1e4a6f2ec6742183b7750b4f47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Wed, 19 Aug 2009 15:47:18 +0200 Subject: Implement the functions we added in the API review: Those are: * setAnchorSpacing() * anchorSpacing() * unsetAnchorSpacing() Autotests for the two last ones are missing though.. --- src/gui/graphicsview/qgraphicsanchorlayout.cpp | 53 +++++++++++++++++------- src/gui/graphicsview/qgraphicsanchorlayout.h | 9 ++-- src/gui/graphicsview/qgraphicsanchorlayout_p.cpp | 40 ++++++++++++++++-- src/gui/graphicsview/qgraphicsanchorlayout_p.h | 19 +++++++-- 4 files changed, 95 insertions(+), 26 deletions(-) diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.cpp b/src/gui/graphicsview/qgraphicsanchorlayout.cpp index db79dae..5032dc6 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout.cpp @@ -138,18 +138,6 @@ void QGraphicsAnchorLayout::addAnchor(QGraphicsLayoutItem *firstItem, Qt::Anchor invalidate(); } -void QGraphicsAnchorLayout::setAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, - const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge, - qreal spacing) -{ - Q_D(QGraphicsAnchorLayout); - - if (!d->setAnchorSize(firstItem, firstEdge, secondItem, secondEdge, spacing)) { - qWarning("setAnchorSpacing: The anchor does not exist."); - } - invalidate(); -} - /*! * 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 @@ -225,17 +213,52 @@ void QGraphicsAnchorLayout::addCornerAnchors(QGraphicsLayoutItem *firstItem, \endcode */ +/*! + Set the spacing between the anchor point defined by \a firstItem and \a firstEdge and + \a secondItem and \a secondEdge to be \a spacing. +*/ +void QGraphicsAnchorLayout::setAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge, + qreal spacing) +{ + Q_D(QGraphicsAnchorLayout); + + if (!d->setAnchorSize(firstItem, firstEdge, secondItem, secondEdge, &spacing)) { + qWarning("setAnchorSpacing: The anchor does not exist."); + } + invalidate(); +} + +/*! + Returns the spacing between the anchor point defined by \a firstItem and \a firstEdge and + \a secondItem and \a secondEdge. The anchor must exist. +*/ qreal QGraphicsAnchorLayout::anchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) const { - qWarning("// ### TO BE IMPLEMENTED"); - return 0; + Q_D(const QGraphicsAnchorLayout); + qreal size = 0; + if (!d->anchorSize(firstItem, firstEdge, secondItem, secondEdge, 0, &size)) { + qWarning("anchorSpacing: The anchor does not exist."); + } + return size; } +/*! + Resets the spacing between the anchor point defined by \a firstItem and \a firstEdge and + \a secondItem and \a secondEdge to be the default spacing. Depending on the anchor type, the + default spacing is either 0 or a value returned from the style. + + \sa setAnchorSpacing, anchorSpacing, addAnchor +*/ void QGraphicsAnchorLayout::unsetAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) { - qWarning("// ### TO BE IMPLEMENTED"); + Q_D(QGraphicsAnchorLayout); + + if (!d->setAnchorSize(firstItem, firstEdge, secondItem, secondEdge, 0)) { + qWarning("unsetAnchorSpacing: The anchor does not exist."); + } invalidate(); } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout.h b/src/gui/graphicsview/qgraphicsanchorlayout.h index f2335a8..3de9ae5 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout.h @@ -65,10 +65,6 @@ public: void addAnchor(QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge); - void setAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, - const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge, - qreal spacing); - void addCornerAnchors(QGraphicsLayoutItem *firstItem, Qt::Corner firstCorner, QGraphicsLayoutItem *secondItem, Qt::Corner secondCorner); @@ -81,8 +77,13 @@ public: inline void addAllAnchors(QGraphicsLayoutItem *firstItem, QGraphicsLayoutItem *secondItem); + void setAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge, + qreal spacing); + qreal anchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge) const; + void unsetAnchorSpacing(const QGraphicsLayoutItem *firstItem, Qt::AnchorPoint firstEdge, const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge); diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp index 436ce2c..ffece0d 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.cpp @@ -1125,17 +1125,49 @@ bool QGraphicsAnchorLayoutPrivate::setAnchorSize(const QGraphicsLayoutItem *firs Qt::AnchorPoint firstEdge, const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge, - qreal anchorSize) + const qreal *anchorSize) { - // ### we can avoid restoration if we really want to + // ### we can avoid restoration if we really want to, but we would have to + // search recursively through all composite anchors restoreSimplifiedGraph(edgeOrientation(firstEdge)); AnchorVertex *v1 = internalVertex(firstItem, firstEdge); AnchorVertex *v2 = internalVertex(secondItem, secondEdge); AnchorData *data = graph[edgeOrientation(firstEdge)].edgeData(v1, v2); - if (data) - data->setFixedSize(anchorSize); + if (data) { + if (anchorSize) { + data->setFixedSize(*anchorSize); + } else { + data->unsetSize(); + } + } + + return data; +} +bool QGraphicsAnchorLayoutPrivate::anchorSize(const QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + qreal *minSize, + qreal *prefSize, + qreal *maxSize) const +{ + Q_ASSERT(minSize || prefSize || maxSize); + QGraphicsAnchorLayoutPrivate *that = const_cast(this); + that->restoreSimplifiedGraph(edgeOrientation(firstEdge)); + AnchorVertex *v1 = internalVertex(firstItem, firstEdge); + AnchorVertex *v2 = internalVertex(secondItem, secondEdge); + + AnchorData *data = that->graph[edgeOrientation(firstEdge)].edgeData(v1, v2); + if (data) { + if (minSize) + *minSize = data->minSize; + if (prefSize) + *prefSize = data->prefSize; + if (maxSize) + *maxSize = data->maxSize; + } return data; } diff --git a/src/gui/graphicsview/qgraphicsanchorlayout_p.h b/src/gui/graphicsview/qgraphicsanchorlayout_p.h index 742108d..e17bd28 100644 --- a/src/gui/graphicsview/qgraphicsanchorlayout_p.h +++ b/src/gui/graphicsview/qgraphicsanchorlayout_p.h @@ -176,6 +176,11 @@ struct AnchorData : public QSimplexVariable { hasSize = true; } + inline void unsetSize() + { + hasSize = false; + } + // Anchor is semantically directed AnchorVertex *from; AnchorVertex *to; @@ -367,7 +372,15 @@ public: Qt::AnchorPoint firstEdge, const QGraphicsLayoutItem *secondItem, Qt::AnchorPoint secondEdge, - qreal anchorSize); + const qreal *anchorSize); + + bool anchorSize(const QGraphicsLayoutItem *firstItem, + Qt::AnchorPoint firstEdge, + const QGraphicsLayoutItem *secondItem, + Qt::AnchorPoint secondEdge, + qreal *minSize = 0, + qreal *prefSize = 0, + qreal *maxSize = 0) const; void removeAnchors(QGraphicsLayoutItem *item); @@ -394,12 +407,12 @@ public: QList constraintsFromSizeHints(const QList &anchors); QList > getGraphParts(Orientation orientation); - inline AnchorVertex *internalVertex(const QPair &itemEdge) + inline AnchorVertex *internalVertex(const QPair &itemEdge) const { return m_vertexList.value(itemEdge).first; } - inline AnchorVertex *internalVertex(const QGraphicsLayoutItem *item, Qt::AnchorPoint edge) + inline AnchorVertex *internalVertex(const QGraphicsLayoutItem *item, Qt::AnchorPoint edge) const { return internalVertex(qMakePair(const_cast(item), edge)); } -- cgit v0.12 From d98a73c75c91b535fb7a266b7d369861c2096b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Thu, 20 Aug 2009 16:45:18 +0200 Subject: Add the autotest to auto.pro --- tests/auto/auto.pro | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index b4a6600..17a1b35 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -142,6 +142,7 @@ SUBDIRS += _networkselftest \ qglobal \ qgraphicsitem \ qgraphicsitemanimation \ + qgraphicsanchorlayout \ qgraphicslayout \ qgraphicslayoutitem \ qgraphicslinearlayout \ @@ -190,7 +191,7 @@ SUBDIRS += _networkselftest \ qlibrary \ qline \ qlineedit \ - qlist \ + qlist \ q3listbox \ qlistview \ qlistwidget \ -- cgit v0.12 From 926f5ad207fb04fdeb09c0b696dce3c072e47e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Arve=20S=C3=A6ther?= Date: Thu, 20 Aug 2009 18:08:30 +0200 Subject: Remove the anchorlayoyt example that can load layouts. We decided to remove it because it does not serve its purpose as an example.(Too much code, does not show how to use the API in a nice way) --- examples/layouts/anchorlayout/anchorlayout.pro | 15 - examples/layouts/anchorlayout/anchorlayout.ui | 451 ----------------- examples/layouts/anchorlayout/layoutitem.cpp | 93 ---- examples/layouts/anchorlayout/layoutitem.h | 62 --- examples/layouts/anchorlayout/main.cpp | 10 - examples/layouts/anchorlayout/scene.cpp | 57 --- examples/layouts/anchorlayout/scene.h | 64 --- .../layouts/anchorlayout/widgetchooserdelegate.cpp | 134 ----- .../layouts/anchorlayout/widgetchooserdelegate.h | 75 --- examples/layouts/anchorlayout/window.cpp | 555 --------------------- examples/layouts/anchorlayout/window.h | 61 --- examples/layouts/anchorlayout/xml/center.xml | 57 --- examples/layouts/anchorlayout/xml/linear.xml | 46 -- examples/layouts/anchorlayout/xml/snake.xml | 79 --- 14 files changed, 1759 deletions(-) delete mode 100644 examples/layouts/anchorlayout/anchorlayout.pro delete mode 100644 examples/layouts/anchorlayout/anchorlayout.ui delete mode 100644 examples/layouts/anchorlayout/layoutitem.cpp delete mode 100644 examples/layouts/anchorlayout/layoutitem.h delete mode 100644 examples/layouts/anchorlayout/main.cpp delete mode 100644 examples/layouts/anchorlayout/scene.cpp delete mode 100644 examples/layouts/anchorlayout/scene.h delete mode 100644 examples/layouts/anchorlayout/widgetchooserdelegate.cpp delete mode 100644 examples/layouts/anchorlayout/widgetchooserdelegate.h delete mode 100644 examples/layouts/anchorlayout/window.cpp delete mode 100644 examples/layouts/anchorlayout/window.h delete mode 100644 examples/layouts/anchorlayout/xml/center.xml delete mode 100644 examples/layouts/anchorlayout/xml/linear.xml delete mode 100644 examples/layouts/anchorlayout/xml/snake.xml diff --git a/examples/layouts/anchorlayout/anchorlayout.pro b/examples/layouts/anchorlayout/anchorlayout.pro deleted file mode 100644 index 8c19404..0000000 --- a/examples/layouts/anchorlayout/anchorlayout.pro +++ /dev/null @@ -1,15 +0,0 @@ -###################################################################### -# Automatically generated by qmake (2.01a) fr 15. mai 08:51:37 2009 -###################################################################### - -TEMPLATE = app -TARGET = -DEPENDPATH += . -INCLUDEPATH += . - -# Input -HEADERS += layoutitem.h widgetchooserdelegate.h window.h scene.h -FORMS += anchorlayout.ui -SOURCES += layoutitem.cpp main.cpp widgetchooserdelegate.cpp window.cpp scene.cpp - -DEFINES += PRO_FILE_PWD=$$_PRO_FILE_PWD_ \ No newline at end of file diff --git a/examples/layouts/anchorlayout/anchorlayout.ui b/examples/layouts/anchorlayout/anchorlayout.ui deleted file mode 100644 index 5f3718b..0000000 --- a/examples/layouts/anchorlayout/anchorlayout.ui +++ /dev/null @@ -1,451 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 795 - 765 - - - - Anchor layout example - - - - - 0 - - - - - - - 0 - 128 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - - - - - 0 - 0 - 795 - 21 - - - - - &File - - - - - - - &View - - - - - - - - - - - - toolBar - - - TopToolBarArea - - - false - - - - - - - 0 - 0 - - - - Qt::BottomDockWidgetArea|Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea - - - Anchors - - - 1 - - - - - 0 - - - - - - 0 - 0 - - - - - 330 - 0 - - - - 50 - - - - Begin - - - - - Edge - - - - - End - - - - - Edge - - - - - Min - - - - - Max - - - - - - - - Add anchor ^^ - - - Ctrl+A - - - false - - - - - - - - - - 0 - 0 - - - - Item properties - - - 1 - - - - - 0 - - - - - - 0 - 0 - - - - Item properties - - - - - - TextLabel - - - - - - - - - - TextLabel - - - - - - - TextLabel - - - - - - - TextLabel - - - - - - - - - - - - - TextLabel - - - - - - - - - - - - - TextLabel - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - Layout presets - - - 1 - - - - - 0 - - - - - - - - - - Add item - - - Ctrl+N - - - - - true - - - true - - - &Anchors - - - - - true - - - true - - - Layout &presets - - - - - true - - - true - - - &Item properties - - - - - Save layout - - - Ctrl+S - - - - - &Quit - - - Ctrl+Q - - - - - - - actionLayout_presets - triggered(bool) - dwPresets - setVisible(bool) - - - -1 - -1 - - - 173 - 345 - - - - - action_Anchors - triggered(bool) - dwAnchors - setVisible(bool) - - - -1 - -1 - - - 173 - 169 - - - - - action_Item_properties - triggered(bool) - dwProperties - setVisible(bool) - - - 173 - 483 - - - 173 - 483 - - - - - dwAnchors - visibilityChanged(bool) - action_Anchors - setChecked(bool) - - - 173 - 169 - - - -1 - -1 - - - - - dwPresets - visibilityChanged(bool) - actionLayout_presets - setChecked(bool) - - - 173 - 345 - - - -1 - -1 - - - - - dwProperties - visibilityChanged(bool) - action_Item_properties - setChecked(bool) - - - 173 - 483 - - - -1 - -1 - - - - - diff --git a/examples/layouts/anchorlayout/layoutitem.cpp b/examples/layouts/anchorlayout/layoutitem.cpp deleted file mode 100644 index b85a589..0000000 --- a/examples/layouts/anchorlayout/layoutitem.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "layoutitem.h" -#include "scene.h" - -LayoutItem::LayoutItem(const QString &name, QGraphicsItem *parent/* = 0*/) - : QGraphicsWidget(parent) -{ - setData(0, name); - setGraphicsItem(this); - setFocusPolicy(Qt::ClickFocus); -} - -LayoutItem::~LayoutItem() -{ -} - -void LayoutItem::paint(QPainter *painter, - const QStyleOptionGraphicsItem *option, QWidget *widget /*= 0*/) -{ - Q_UNUSED(widget); - Q_UNUSED(option); - - // text - QString name = graphicsItem()->data(0).toString(); - painter->drawText(rect(), Qt::AlignCenter, name); - - // rect - if (Scene *scn = static_cast(scene())) { - if (scn->lastFocusItem() == this) { - QPen pen(Qt::blue, 2.0); - painter->setPen(pen); - } - } - painter->drawRoundedRect(rect(), 5, 5); -} - -void LayoutItem::focusInEvent(QFocusEvent *event) -{ - if (Scene *scn = static_cast(scene())) { - scn->emitFocusItemChanged(this); - } - QGraphicsWidget::focusInEvent(event); -} - -void LayoutItem::focusOutEvent(QFocusEvent *event) -{ - if (Scene *scn = static_cast(scene())) { - scn->emitFocusItemChanged(0); - } - QGraphicsWidget::focusOutEvent(event); -} - - diff --git a/examples/layouts/anchorlayout/layoutitem.h b/examples/layouts/anchorlayout/layoutitem.h deleted file mode 100644 index 86c0b06..0000000 --- a/examples/layouts/anchorlayout/layoutitem.h +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef LAYOUTITEM_H -#define LAYOUTITEM_H - -#include - -class LayoutItem : public QGraphicsWidget -{ - Q_OBJECT -public: - LayoutItem(const QString &name, QGraphicsItem *parent = 0); - ~LayoutItem(); - - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, - QWidget *widget = 0); - void focusInEvent(QFocusEvent *event); - void focusOutEvent(QFocusEvent *event); - -}; - - -#endif diff --git a/examples/layouts/anchorlayout/main.cpp b/examples/layouts/anchorlayout/main.cpp deleted file mode 100644 index 716a563..0000000 --- a/examples/layouts/anchorlayout/main.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "window.h" -#include - -int main(int argc, char **argv) -{ - QApplication app(argc, argv); - Window *w = new Window(app.arguments()); - w->show(); - return app.exec(); -} diff --git a/examples/layouts/anchorlayout/scene.cpp b/examples/layouts/anchorlayout/scene.cpp deleted file mode 100644 index 68b4002..0000000 --- a/examples/layouts/anchorlayout/scene.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "scene.h" - -Scene::Scene() : QGraphicsScene() -{ - -} - -void Scene::emitFocusItemChanged(QGraphicsItem *item) -{ - if (hasFocus()) { // when leaving the view, we want to remember the last focused item - emit focusItemChanged(item); - m_lastFocusItem = item; - } -} - - diff --git a/examples/layouts/anchorlayout/scene.h b/examples/layouts/anchorlayout/scene.h deleted file mode 100644 index ba7f5d8..0000000 --- a/examples/layouts/anchorlayout/scene.h +++ /dev/null @@ -1,64 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef SCENE_H -#define SCENE_H - -#include - -class Scene : public QGraphicsScene -{ - Q_OBJECT -public: - Scene(); - - void emitFocusItemChanged(QGraphicsItem *item); - QGraphicsItem *lastFocusItem() { return m_lastFocusItem; } - -Q_SIGNALS: - void focusItemChanged(QGraphicsItem *item); - -private: - QGraphicsItem *m_lastFocusItem; - -}; - -#endif // SCENE_H diff --git a/examples/layouts/anchorlayout/widgetchooserdelegate.cpp b/examples/layouts/anchorlayout/widgetchooserdelegate.cpp deleted file mode 100644 index 0f988c1..0000000 --- a/examples/layouts/anchorlayout/widgetchooserdelegate.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include - -#include "widgetchooserdelegate.h" - - -WidgetChooserDelegate::WidgetChooserDelegate(QVector *layoutItems, - QGraphicsLayout *layout, - QWidget *parent /*= 0*/) -: QStyledItemDelegate(parent), m_layoutItems(layoutItems), m_layout(layout) -{ -} - -static const char *edges[] = { - "Left", - "HCenter", - "Right", - "Top", - "VCenter", - "Bottom" - }; - -QWidget *WidgetChooserDelegate::createEditor(QWidget *parent, - const QStyleOptionViewItem &option, - const QModelIndex &index) const - -{ - int column = index.column(); - if (column == 0 || column == 2) { - // first and second anchor items - QComboBox *chooser = new QComboBox(parent); - chooser->addItem(QLatin1String("layout"), - qVariantFromValue(reinterpret_cast(m_layout))); - for (int i = 0; i < m_layoutItems->count(); ++i) { - QGraphicsLayoutItem *item = m_layoutItems->at(i); - chooser->addItem(item->graphicsItem()->data(0).toString(), - qVariantFromValue(reinterpret_cast(item))); - } - connect(chooser, SIGNAL(currentIndexChanged(int)), - this, SLOT(commitAndCloseEditor())); - return chooser; - } else if (column == 1 || column == 3) { - // first and second anchor edges - QComboBox *chooser = new QComboBox(parent); - for (int i = 0; i < sizeof(edges)/sizeof(char*); ++i) { - chooser->addItem(QLatin1String(edges[i]), i); - } - connect(chooser, SIGNAL(currentIndexChanged(int)), - this, SLOT(commitAndCloseEditor())); - return chooser; - } else { - return QStyledItemDelegate::createEditor(parent, option, index); - } -} - -void WidgetChooserDelegate::setEditorData(QWidget *editor, - const QModelIndex &index) const -{ - if (QComboBox *chooser = qobject_cast(editor)) { - int column = index.column(); - if (column >= 0 && column <= 3) { - QVariant data = index.data(Qt::UserRole); - int index = chooser->findData(data); - if (index >= 0) { - chooser->setCurrentIndex(index); - } - } - } else { - QStyledItemDelegate::setEditorData(editor, index); - } -} - -void WidgetChooserDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, - const QModelIndex &index) const -{ - if (QComboBox *chooser = qobject_cast(editor)) { - int column = index.column(); - if (column >= 0 && column <= 3) { - int currentIndex = chooser->currentIndex(); - model->setData(index, chooser->itemText(currentIndex)); - model->setData(index, chooser->itemData(currentIndex), Qt::UserRole); - } - } else { - QStyledItemDelegate::setModelData(editor, model, index); - } -} - -void WidgetChooserDelegate::commitAndCloseEditor() -{ - QComboBox *editor = qobject_cast(sender()); - emit commitData(editor); - emit closeEditor(editor); -} - diff --git a/examples/layouts/anchorlayout/widgetchooserdelegate.h b/examples/layouts/anchorlayout/widgetchooserdelegate.h deleted file mode 100644 index 50b9e48..0000000 --- a/examples/layouts/anchorlayout/widgetchooserdelegate.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef STARDELEGATE_H -#define STARDELEGATE_H - -#include - -#include - -class QGraphicsLayoutItem; -class QGraphicsLayout; - -class WidgetChooserDelegate : public QStyledItemDelegate -{ - Q_OBJECT - -public: - WidgetChooserDelegate(QVector *layoutItems, - QGraphicsLayout *layout, - QWidget *parent = 0); - - QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, - const QModelIndex &index) const; - void setEditorData(QWidget *editor, const QModelIndex &index) const; - void setModelData(QWidget *editor, QAbstractItemModel *model, - const QModelIndex &index) const; - -private slots: - void commitAndCloseEditor(); - -private: - QVector *m_layoutItems; - QGraphicsLayout *m_layout; -}; - -#endif diff --git a/examples/layouts/anchorlayout/window.cpp b/examples/layouts/anchorlayout/window.cpp deleted file mode 100644 index cab1b60..0000000 --- a/examples/layouts/anchorlayout/window.cpp +++ /dev/null @@ -1,555 +0,0 @@ -#include "window.h" -#include "layoutitem.h" -#include "scene.h" -#include "widgetchooserdelegate.h" - -#include -#include - -static QGraphicsLayoutItem *layoutItem(const QVariant &data) -{ - return reinterpret_cast(qVariantValue(data)); -} - -static QString nodeName(QGraphicsLayoutItem *item) -{ - QString str = item->isLayout() ? QLatin1String("layout") : item->graphicsItem()->data(0).toString(); - str.replace(QLatin1Char(' '), QLatin1Char('_')); - return str; -} - -#define _QUOTEMACRO(x) #x -#define QUOTEMACRO(x) _QUOTEMACRO(x) - -Window::Window(const QStringList &arguments, QWidget *parent) - : QMainWindow(parent), m_inAddAnchor(false) -{ - m_ui.setupUi(this); - m_scene = new Scene; - - m_ui.graphicsView->setScene(m_scene); - connect(m_scene, SIGNAL(focusItemChanged(QGraphicsItem*)), this, SLOT(scene_focusItemChanged(QGraphicsItem*))); - - m_window = new QGraphicsWidget(0, Qt::Window); - m_scene->addItem(m_window); - m_layout = new QGraphicsAnchorLayout(m_window); - m_window->setLayout(m_layout); - m_window->resize(400, 300); - - findLayoutFiles(); - - WidgetChooserDelegate *delegate = new WidgetChooserDelegate(&m_layoutItems, m_layout, m_ui.anchors); - m_ui.anchors->setItemDelegate(delegate); - - if (arguments.count() >= 2) { - QString fileName = QString::fromAscii("%1/xml/%2.xml").arg(QUOTEMACRO(PRO_FILE_PWD), arguments.at(1)); - if (!loadLayout(fileName, m_layout)) { - QMessageBox::warning(this, tr("Not found"), tr("Could not find %1").arg(fileName)); - } - } -} - -void Window::findLayoutFiles() -{ - m_ignoreCurrentLayoutChange = true; - QLatin1String s(QUOTEMACRO(PRO_FILE_PWD)"/xml"); - QDirIterator it(s); - while (it.hasNext()) { - it.next(); - QFileInfo fi = it.fileInfo(); - QString baseName = fi.baseName(); - if (!baseName.isEmpty()) { // avoid "." and ".." - m_ui.layouts->addItem(baseName); - } - } - m_ignoreCurrentLayoutChange = false; -} - -void Window::on_layouts_currentRowChanged(int row) -{ - if (m_ignoreCurrentLayoutChange) - return; - - QListWidgetItem *item = m_ui.layouts->item(row); - if (item) { - QString fileName = QString::fromAscii("%1/xml/%2.xml").arg(QUOTEMACRO(PRO_FILE_PWD), item->text()); - if (!loadLayout(fileName, m_layout)) { - qWarning("could not find %s", qPrintable(fileName)); - } - } -} - -void Window::on_anchors_cellChanged(int row, int column) -{ - Q_UNUSED(row); - Q_UNUSED(column); - if (!m_inAddAnchor) - rebuildLayout(); -} - - -void Window::on_pbAddAnchor_clicked(bool) -{ - addAnchorRow(); -} - -void Window::on_actionAdd_item_triggered(bool ) -{ - addItem(); -} - -void Window::on_actionSave_layout_triggered(bool ) -{ - QString fileName = QFileDialog::getSaveFileName(this, tr("Save layout"), QLatin1String(QUOTEMACRO(PRO_FILE_PWD)"/xml"), QLatin1String("*.xml")); - if (!fileName.isEmpty()) - saveLayout(fileName); -} - -void Window::on_itemName_textEdited(const QString & ) -{ - updateItem(); -} - -void Window::on_itemMinW_valueChanged(double ) -{ - updateItem(); -} - -void Window::on_itemMinH_valueChanged(double ) -{ - updateItem(); -} - -void Window::on_itemPrefW_valueChanged(double ) -{ - updateItem(); -} - -void Window::on_itemPrefH_valueChanged(double ) -{ - updateItem(); -} - -void Window::on_itemMaxW_valueChanged(double ) -{ - updateItem(); -} - -void Window::on_itemMaxH_valueChanged(double ) -{ - updateItem(); -} - -void Window::scene_focusItemChanged(QGraphicsItem *item) -{ - bool isItemValid = item && item->isWidget(); - m_ui.groupBox->setEnabled(isItemValid); - QGraphicsLayoutItem *newCurrentItem = m_currentItem; - m_currentItem = 0; - if (isItemValid) { - QGraphicsWidget *w = static_cast(item); - setItemData(item->data(0).toString(), w->minimumSize(), w->preferredSize(), w->maximumSize()); - newCurrentItem = w; - } else { - setItemData(QString(), QSizeF(), QSizeF(), QSizeF()); - } - m_currentItem = newCurrentItem; -} - -void Window::updateItem() -{ - if (!m_currentItem) - return; - if (QGraphicsItem *gi = m_currentItem->graphicsItem()) { - gi->setData(0, m_ui.itemName->text()); - gi->update(); - } - QSizeF min(m_ui.itemMinW->value(), m_ui.itemMinH->value()); - QSizeF pref(m_ui.itemPrefW->value(), m_ui.itemPrefH->value()); - QSizeF max(m_ui.itemMaxW->value(), m_ui.itemMaxH->value()); - - if (min.isValid()) - m_currentItem->setMinimumSize(min); - if (pref.isValid()) - m_currentItem->setPreferredSize(pref); - if (max.isValid()) - m_currentItem->setMaximumSize(max); -} - -void Window::rebuildLayout() -{ - int i; - for (i = m_layout->count(); i > 0; --i) { - m_layout->removeAt(0); - } - - int rc = m_ui.anchors->rowCount(); - for (i = 0; i < rc; ++i) { - bool ok; - - QGraphicsLayoutItem *startItem = layoutItemAt(m_ui.anchors->model(), i, 0); - if (!startItem) - continue; - Qt::AnchorPoint startEdge = (Qt::AnchorPoint)(m_ui.anchors->item(i, 1)->data(Qt::UserRole).toInt(&ok)); - if (!ok) - continue; - QGraphicsLayoutItem *endItem = layoutItemAt(m_ui.anchors->model(), i, 2); - if (!endItem) - continue; - Qt::AnchorPoint endEdge = (Qt::AnchorPoint)(m_ui.anchors->item(i, 3)->data(Qt::UserRole).toInt(&ok)); - if (!ok) - continue; - - m_layout->addAnchor(startItem, startEdge, endItem, endEdge); - } -} - -void Window::setItemData(const QString &name, const QSizeF &min, const QSizeF &pref, const QSizeF &max) -{ - m_ui.itemName->setText(name); - m_ui.itemMinW->setValue(min.width()); - m_ui.itemMinH->setValue(min.height()); - m_ui.itemPrefW->setValue(pref.width()); - m_ui.itemPrefH->setValue(pref.height()); - m_ui.itemMaxW->setValue(max.width()); - m_ui.itemMaxH->setValue(max.height()); -} - -QGraphicsLayoutItem *Window::addItem(const QString &name) -{ - int rc = m_layoutItems.count(); - QString effectiveName = name.isEmpty() ? QString::fromAscii("item %1").arg(rc + 1) : name; - QGraphicsLayoutItem *layoutItem = new LayoutItem(effectiveName, m_window); - m_layoutItems.append(layoutItem); - m_scene->setFocusItem(layoutItem->graphicsItem()); - return layoutItem; -} - -static const char *strEdges[] = {"Left", - "HCenter", - "Right", - "Top", - "VCenter", - "Bottom"}; - -void Window::setAnchorData(QGraphicsLayoutItem *startItem, const QString &startName, Qt::AnchorPoint startEdge, - QGraphicsLayoutItem *endItem, const QString &endName, Qt::AnchorPoint endEdge, int row /*= -1*/) -{ - if (row == -1) { - row = m_ui.anchors->rowCount(); - m_ui.anchors->insertRow(row); - } - m_inAddAnchor = true; - - QTableWidgetItem *item = new QTableWidgetItem; - item->setData(Qt::UserRole, qVariantFromValue(reinterpret_cast(startItem))); - item->setData(Qt::DisplayRole, startName); - m_ui.anchors->setItem(row, 0, item); - - item = new QTableWidgetItem; - item->setData(Qt::UserRole, int(startEdge)); - item->setData(Qt::DisplayRole, QLatin1String(strEdges[startEdge])); - m_ui.anchors->setItem(row, 1, item); - - item = new QTableWidgetItem; - item->setData(Qt::UserRole, qVariantFromValue(reinterpret_cast(endItem))); - item->setData(Qt::DisplayRole, endName); - m_ui.anchors->setItem(row, 2, item); - - item = new QTableWidgetItem; - item->setData(Qt::UserRole, int(endEdge)); - item->setData(Qt::DisplayRole, QLatin1String(strEdges[endEdge])); - m_ui.anchors->setItem(row, 3, item); - - item = new QTableWidgetItem; - item->setData(Qt::DisplayRole, 6); - m_ui.anchors->setItem(row, 4, item); - - item = new QTableWidgetItem; - item->setData(Qt::DisplayRole, 6); - m_ui.anchors->setItem(row, 5, item); - m_inAddAnchor = false; - -} - -void Window::addAnchorRow() -{ - int rc = m_ui.anchors->rowCount(); - QGraphicsLayoutItem *defaultLayoutItem = m_layout; - if (m_layoutItems.count() > 0) { - defaultLayoutItem = m_layoutItems.at(0); - } - - m_ui.anchors->insertRow(rc); - - if (defaultLayoutItem) { - QString defaultName = defaultLayoutItem->isLayout() ? - QLatin1String("layout") : - defaultLayoutItem->graphicsItem()->data(0).toString(); - setAnchorData(defaultLayoutItem, defaultName, Qt::AnchorRight, defaultLayoutItem, defaultName, Qt::AnchorLeft, rc); - rebuildLayout(); - } -} - -QGraphicsLayoutItem *Window::layoutItemAt(QAbstractItemModel *model, int row, int column /*= 0*/) -{ - int rc = model->rowCount(); - QGraphicsLayoutItem *item = 0; - if (row < rc) { - QVariant data = model->data(model->index(row, column), Qt::UserRole); - if (data.isValid()) { - item = layoutItem(data); - } - } - return item; -} - -bool Window::saveLayout(const QString& fileName) -{ - bool ok; - QFile out(fileName); - ok = out.open(QIODevice::WriteOnly); - if (ok) { - QXmlStreamWriter xml(&out); - xml.setAutoFormatting(true); - xml.writeStartDocument(); - xml.writeStartElement(QLatin1String("anchorlayout")); - int i; - for (i = 0; i < m_layoutItems.count(); ++i) { - QGraphicsLayoutItem *item = m_layoutItems.at(i); - xml.writeStartElement(QLatin1String("item")); - QString name = nodeName(item); - if (name == QLatin1String("layout")) - name = QLatin1String("this"); - xml.writeAttribute(QLatin1String("id"), name); - for (int p = 0; p < 3; ++p) { - const char *propertyNames[] = {"minimumSize", "preferredSize", "maximumSize"}; - typedef QSizeF (QGraphicsLayoutItem::*QGLISizeGetter)(void) const; - QGLISizeGetter sizeGetters[] = { &QGraphicsLayoutItem::minimumSize, - &QGraphicsLayoutItem::preferredSize, - &QGraphicsLayoutItem::maximumSize}; - QSizeF size = ((*item).*(sizeGetters[p])) (); - xml.writeStartElement(QLatin1String("property")); - xml.writeAttribute(QLatin1String("name"), QLatin1String(propertyNames[p])); - xml.writeStartElement(QLatin1String("size")); - xml.writeAttribute(QLatin1String("width"), QString::number(size.width())); - xml.writeAttribute(QLatin1String("height"), QString::number(size.height())); - xml.writeEndElement(); - xml.writeEndElement(); - } - xml.writeEndElement(); - } - - QHash edgeString; - for (i = 0; i < sizeof(strEdges)/sizeof(char*); ++i) { - edgeString.insert(i, QLatin1String(strEdges[i])); - } - int rc = m_ui.anchors->rowCount(); - for (i = 0; i < rc; ++i) { - xml.writeStartElement(QLatin1String("anchor")); - bool ok; - - QGraphicsLayoutItem *startItem = layoutItemAt(m_ui.anchors->model(), i, 0); - if (!startItem) - continue; - Qt::AnchorPoint startEdge = (Qt::AnchorPoint)(m_ui.anchors->item(i, 1)->data(Qt::UserRole).toInt(&ok)); - if (!ok) - continue; - QGraphicsLayoutItem *endItem = layoutItemAt(m_ui.anchors->model(), i, 2); - if (!endItem) - continue; - Qt::AnchorPoint endEdge = (Qt::AnchorPoint)(m_ui.anchors->item(i, 3)->data(Qt::UserRole).toInt(&ok)); - if (!ok) - continue; - - QString strStart = nodeName(startItem); - if (strStart == QLatin1String("layout")) - strStart = QLatin1String("this"); - xml.writeAttribute(QLatin1String("first"), QString::fromAscii("%1.%2").arg(strStart, edgeString.value(startEdge))); - - QString strEnd = nodeName(endItem); - if (strEnd == QLatin1String("layout")) - strEnd = QLatin1String("this"); - xml.writeAttribute(QLatin1String("second"), QString::fromAscii("%1.%2").arg(strEnd, edgeString.value(endEdge))); - - xml.writeEndElement(); - } - - xml.writeEndElement(); - xml.writeEndDocument(); - out.close(); - } - return ok; -} - -static bool parseProperty(QXmlStreamReader *xml, QString *name, QSizeF *size) -{ - QString propName = xml->attributes().value("name").toString(); - QSizeF sz; - while (!xml->atEnd()) { - xml->readNext(); - if (xml->isStartElement() && xml->name() == QLatin1String("size")) { - QXmlStreamAttributes attrs = xml->attributes(); - QString sw = attrs.value("width").toString(); - QString sh = attrs.value("height").toString(); - bool ok; - float w = sw.toFloat(&ok); - if (ok) { - sz.setWidth(w); - } - - float h = sw.toFloat(&ok); - if (ok) { - sz.setHeight(h); - } - - } - if (xml->isEndElement() && xml->name() == QLatin1String("property")) { - if (name && size && sz.isValid()) { - *name = propName; - *size = sz; - return true; - } - } - } - return false; -} - -static bool parseEdge(const QString &itemEdge, QByteArray *id, Qt::AnchorPoint *edge) -{ - QStringList item_edge = itemEdge.split(QLatin1Char('.')); - bool ok = item_edge.count() == 2; - if (ok) { - QByteArray strEdge = item_edge.at(1).toAscii().toLower(); - if (strEdge == "left") { - *edge = Qt::AnchorLeft; - } else if (strEdge == "hcenter") { - *edge = Qt::AnchorHorizontalCenter; - } else if (strEdge == "right") { - *edge = Qt::AnchorRight; - } else if (strEdge == "top") { - *edge = Qt::AnchorTop; - } else if (strEdge == "vcenter") { - *edge = Qt::AnchorVerticalCenter; - } else if (strEdge == "bottom") { - *edge = Qt::AnchorBottom; - } else { - ok = false; - } - if (ok) - *id = item_edge.at(0).toAscii(); - } - return ok; -} - -bool Window::loadLayout(const QString& fileName, QGraphicsAnchorLayout *layout) -{ - QFile input(fileName); - bool ok = input.open(QIODevice::ReadOnly | QIODevice::Text); - if (ok) { - int i; - for (i = 0; i < m_layoutItems.count(); ++i) { - QGraphicsLayoutItem *item = m_layoutItems.at(i); - delete item; - } - - m_layoutItems.clear(); - m_ui.anchors->setRowCount(0); - - QLatin1String str_anchorlayout("anchorlayout"); - QLatin1String str_item("item"); - QLatin1String str_property("property"); - QXmlStreamReader xml(&input); - - int level = 0; - QHash hash; - QString item_id; - QSizeF min, pref, max; - - while (!xml.atEnd()) { - QXmlStreamReader::TokenType token = xml.readNext(); - switch (token) { - case QXmlStreamReader::StartDocument: - break; - case QXmlStreamReader::StartElement: - if (level == 0 && xml.name() == str_anchorlayout) { - - } else if (level == 1 && xml.name() == str_item) { - item_id = xml.attributes().value("id").toString(); - } else if (level == 1 && xml.name() == QLatin1String("anchor")) { - QXmlStreamAttributes attrs = xml.attributes(); - QString first = attrs.value("first").toString(); - QString second = attrs.value("second").toString(); - QByteArray startID; - Qt::AnchorPoint startEdge; - QGraphicsLayoutItem *startItem = 0; - if (parseEdge(first, &startID, &startEdge)) { - if (startID == "this") { - startID = "layout"; - startItem = layout; - } else { - startItem = hash.value(startID); - } - } else { - qWarning("%s is not a valid edge description", qPrintable(first)); - break; - } - - QByteArray endID; - Qt::AnchorPoint endEdge; - QGraphicsLayoutItem *endItem = 0; - if (parseEdge(second, &endID, &endEdge)) { - if (endID == "this") { - endID = "layout"; - endItem = layout; - } else { - endItem = hash.value(endID); - } - } else { - qWarning("%s is not a valid edge description", qPrintable(second)); - break; - } - setAnchorData( startItem, startID, startEdge, - endItem, endID, endEdge, -1); - } else if (level == 2 && xml.name() == str_property) { - QString name; - QSizeF size; - if (parseProperty(&xml, &name, &size)) { - if (name == QLatin1String("minimumSize")) { - min = size; - } else if (name == QLatin1String("preferredSize")) { - pref = size; - } else if (name == QLatin1String("maximumSize")) { - max = size; - } - break; - } - } else { - --level; - } - ++level; - break; - case QXmlStreamReader::EndElement: - if (xml.name() == str_anchorlayout) { - } - if (xml.name() == str_item) { - QGraphicsLayoutItem *item = addItem(item_id); - item->setMinimumSize(min); - item->setPreferredSize(pref); - item->setMaximumSize(max); - hash.insert(item_id.toAscii(), item); - } - --level; - break; - } - } - if (xml.hasError()) { - // do error handling - qWarning("%s", qPrintable(xml.errorString())); - } - input.close(); - } - if (ok) - rebuildLayout(); - return ok; -} diff --git a/examples/layouts/anchorlayout/window.h b/examples/layouts/anchorlayout/window.h deleted file mode 100644 index f87b2c9..0000000 --- a/examples/layouts/anchorlayout/window.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef WINDOW_H -#define WINDOW_H - -#include "ui_anchorlayout.h" -#include - -class QGraphicsWidget; -class Scene; - -class Window : public QMainWindow -{ - Q_OBJECT -public: - Window(const QStringList &arguments, QWidget *parent = 0); - -private slots: - void on_anchors_cellChanged(int row, int column); - void on_pbAddAnchor_clicked(bool); - void on_actionAdd_item_triggered(bool checked); - void on_actionSave_layout_triggered(bool checked); - void on_layouts_currentRowChanged(int row); - void on_itemName_textEdited(const QString &text); - void on_itemMinW_valueChanged(double d); - void on_itemMinH_valueChanged(double d); - void on_itemPrefW_valueChanged(double d); - void on_itemPrefH_valueChanged(double d); - void on_itemMaxW_valueChanged(double d); - void on_itemMaxH_valueChanged(double d); - - - - // not in ui file - void scene_focusItemChanged(QGraphicsItem *item); -private: - void updateItem(); - void setItemData(const QString &name, const QSizeF &min, const QSizeF &pref, const QSizeF &max); - QGraphicsLayoutItem *addItem(const QString &name = QString()); - void addAnchorRow(); - void setAnchorData(QGraphicsLayoutItem *startItem, const QString &startName, Qt::AnchorPoint startEdge, - QGraphicsLayoutItem *endItem, const QString &endName, Qt::AnchorPoint endEdge, int row = -1); - - bool saveLayout(const QString& fileName); - bool loadLayout(const QString& fileName, QGraphicsAnchorLayout *layout); - void findLayoutFiles(); - - void rebuildLayout(); - QGraphicsLayoutItem *layoutItemAt(QAbstractItemModel *mode, int row, int column = 0); - - Ui::MainWindow m_ui; - Scene *m_scene; - bool m_changingCell; - QGraphicsWidget *m_window; - QGraphicsAnchorLayout *m_layout; - QVector m_layoutItems; - QGraphicsLayoutItem *m_currentItem; - bool m_inAddAnchor; - bool m_ignoreCurrentLayoutChange; -}; - -#endif // WINDOW_H - diff --git a/examples/layouts/anchorlayout/xml/center.xml b/examples/layouts/anchorlayout/xml/center.xml deleted file mode 100644 index cf857fc..0000000 --- a/examples/layouts/anchorlayout/xml/center.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/layouts/anchorlayout/xml/linear.xml b/examples/layouts/anchorlayout/xml/linear.xml deleted file mode 100644 index 723b72b..0000000 --- a/examples/layouts/anchorlayout/xml/linear.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/examples/layouts/anchorlayout/xml/snake.xml b/examples/layouts/anchorlayout/xml/snake.xml deleted file mode 100644 index 22b1a6d..0000000 --- a/examples/layouts/anchorlayout/xml/snake.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file -- cgit v0.12