diff -r b72c6db6890b -r 5dc02b23752f src/declarative/graphicsitems/qdeclarativepositioners.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/declarative/graphicsitems/qdeclarativepositioners.cpp Tue Jul 06 15:10:48 2010 +0300 @@ -0,0 +1,1057 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative 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 Technology Preview License Agreement accompanying +** this package. +** +** 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.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativepositioners_p.h" +#include "private/qdeclarativepositioners_p_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +static const QDeclarativeItemPrivate::ChangeTypes watchedChanges + = QDeclarativeItemPrivate::Geometry + | QDeclarativeItemPrivate::SiblingOrder + | QDeclarativeItemPrivate::Visibility + | QDeclarativeItemPrivate::Opacity + | QDeclarativeItemPrivate::Destroyed; + +void QDeclarativeBasePositionerPrivate::watchChanges(QDeclarativeItem *other) +{ + QDeclarativeItemPrivate *otherPrivate = static_cast(QGraphicsItemPrivate::get(other)); + otherPrivate->addItemChangeListener(this, watchedChanges); +} + +void QDeclarativeBasePositionerPrivate::unwatchChanges(QDeclarativeItem* other) +{ + QDeclarativeItemPrivate *otherPrivate = static_cast(QGraphicsItemPrivate::get(other)); + otherPrivate->removeItemChangeListener(this, watchedChanges); +} + +/*! + \internal + \class QDeclarativeBasePositioner + \brief The QDeclarativeBasePositioner class provides a base for QDeclarativeGraphics layouts. + + To create a QDeclarativeGraphics Positioner, simply subclass QDeclarativeBasePositioner and implement + doLayout(), which is automatically called when the layout might need + updating. In doLayout() use the setX and setY functions from QDeclarativeBasePositioner, and the + base class will apply the positions along with the appropriate transitions. The items to + position are provided in order as the protected member positionedItems. + + You also need to set a PositionerType, to declare whether you are positioning the x, y or both + for the child items. Depending on the chosen type, only x or y changes will be applied. + + Note that the subclass is responsible for adding the + spacing in between items. +*/ +QDeclarativeBasePositioner::QDeclarativeBasePositioner(PositionerType at, QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarativeBasePositionerPrivate), parent) +{ + Q_D(QDeclarativeBasePositioner); + d->init(at); +} + +QDeclarativeBasePositioner::QDeclarativeBasePositioner(QDeclarativeBasePositionerPrivate &dd, PositionerType at, QDeclarativeItem *parent) + : QDeclarativeItem(dd, parent) +{ + Q_D(QDeclarativeBasePositioner); + d->init(at); +} + +QDeclarativeBasePositioner::~QDeclarativeBasePositioner() +{ + Q_D(QDeclarativeBasePositioner); + for (int i = 0; i < positionedItems.count(); ++i) + d->unwatchChanges(positionedItems.at(i).item); + positionedItems.clear(); +} + +int QDeclarativeBasePositioner::spacing() const +{ + Q_D(const QDeclarativeBasePositioner); + return d->spacing; +} + +void QDeclarativeBasePositioner::setSpacing(int s) +{ + Q_D(QDeclarativeBasePositioner); + if (s==d->spacing) + return; + d->spacing = s; + prePositioning(); + emit spacingChanged(); +} + +QDeclarativeTransition *QDeclarativeBasePositioner::move() const +{ + Q_D(const QDeclarativeBasePositioner); + return d->moveTransition; +} + +void QDeclarativeBasePositioner::setMove(QDeclarativeTransition *mt) +{ + Q_D(QDeclarativeBasePositioner); + if (mt == d->moveTransition) + return; + d->moveTransition = mt; + emit moveChanged(); +} + +QDeclarativeTransition *QDeclarativeBasePositioner::add() const +{ + Q_D(const QDeclarativeBasePositioner); + return d->addTransition; +} + +void QDeclarativeBasePositioner::setAdd(QDeclarativeTransition *add) +{ + Q_D(QDeclarativeBasePositioner); + if (add == d->addTransition) + return; + + d->addTransition = add; + emit addChanged(); +} + +void QDeclarativeBasePositioner::componentComplete() +{ + Q_D(QDeclarativeBasePositioner); + QDeclarativeItem::componentComplete(); + positionedItems.reserve(d->QGraphicsItemPrivate::children.count()); + prePositioning(); + reportConflictingAnchors(); +} + +QVariant QDeclarativeBasePositioner::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + Q_D(QDeclarativeBasePositioner); + if (change == ItemChildAddedChange){ + QGraphicsItem* item = value.value(); + QDeclarativeItem* child = 0; + if(item) + child = qobject_cast(item->toGraphicsObject()); + if (child) + prePositioning(); + } else if (change == ItemChildRemovedChange) { + QGraphicsItem* item = value.value(); + QDeclarativeItem* child = 0; + if(item) + child = qobject_cast(item->toGraphicsObject()); + if (child) { + QDeclarativeBasePositioner::PositionedItem posItem(child); + int idx = positionedItems.find(posItem); + if (idx >= 0) { + d->unwatchChanges(child); + positionedItems.remove(idx); + } + prePositioning(); + } + } + + return QDeclarativeItem::itemChange(change, value); +} + +void QDeclarativeBasePositioner::prePositioning() +{ + Q_D(QDeclarativeBasePositioner); + if (!isComponentComplete()) + return; + + if (d->doingPositioning) + return; + + d->queuedPositioning = false; + d->doingPositioning = true; + //Need to order children by creation order modified by stacking order + QList children = d->QGraphicsItemPrivate::children; + qSort(children.begin(), children.end(), d->insertionOrder); + + QPODVector oldItems; + positionedItems.copyAndClear(oldItems); + for (int ii = 0; ii < children.count(); ++ii) { + QDeclarativeItem *child = qobject_cast(children.at(ii)); + if (!child) + continue; + PositionedItem *item = 0; + PositionedItem posItem(child); + int wIdx = oldItems.find(posItem); + if (wIdx < 0) { + d->watchChanges(child); + positionedItems.append(posItem); + item = &positionedItems[positionedItems.count()-1]; + item->isNew = true; + if (child->opacity() <= 0.0 || !child->isVisible()) + item->isVisible = false; + } else { + item = &oldItems[wIdx]; + if (child->opacity() <= 0.0 || !child->isVisible()) { + item->isVisible = false; + } else if (!item->isVisible) { + item->isVisible = true; + item->isNew = true; + } else { + item->isNew = false; + } + positionedItems.append(*item); + } + } + QSizeF contentSize; + doPositioning(&contentSize); + if(d->addTransition || d->moveTransition) + finishApplyTransitions(); + d->doingPositioning = false; + //Set implicit size to the size of its children + setImplicitHeight(contentSize.height()); + setImplicitWidth(contentSize.width()); +} + +void QDeclarativeBasePositioner::positionX(int x, const PositionedItem &target) +{ + Q_D(QDeclarativeBasePositioner); + if(d->type == Horizontal || d->type == Both){ + if (target.isNew) { + if (!d->addTransition) + target.item->setX(x); + else + d->addActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x)); + } else if (x != target.item->x()) { + if (!d->moveTransition) + target.item->setX(x); + else + d->moveActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x)); + } + } +} + +void QDeclarativeBasePositioner::positionY(int y, const PositionedItem &target) +{ + Q_D(QDeclarativeBasePositioner); + if(d->type == Vertical || d->type == Both){ + if (target.isNew) { + if (!d->addTransition) + target.item->setY(y); + else + d->addActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y)); + } else if (y != target.item->y()) { + if (!d->moveTransition) + target.item->setY(y); + else + d->moveActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y)); + } + } +} + +void QDeclarativeBasePositioner::finishApplyTransitions() +{ + Q_D(QDeclarativeBasePositioner); + // Note that if a transition is not set the transition manager will + // apply the changes directly, in the case add/move aren't set + d->addTransitionManager.transition(d->addActions, d->addTransition); + d->moveTransitionManager.transition(d->moveActions, d->moveTransition); + d->addActions.clear(); + d->moveActions.clear(); +} + +/*! + \qmlclass Column QDeclarativeColumn + \since 4.7 + \brief The Column item lines up its children vertically. + \inherits Item + + The Column item positions its child items so that they are vertically + aligned and not overlapping. Spacing between items can be added. + + The below example positions differently shaped rectangles using a Column. + \table + \row + \o \image verticalpositioner_example.png + \o + \qml +Column { + spacing: 2 + Rectangle { color: "red"; width: 50; height: 50 } + Rectangle { color: "green"; width: 20; height: 50 } + Rectangle { color: "blue"; width: 50; height: 20 } +} + \endqml + \endtable + + Column also provides for transitions to be set when items are added, moved, + or removed in the positioner. Adding and removing apply both to items which are deleted + or have their position in the document changed so as to no longer be children of the positioner, + as well as to items which have their opacity set to or from zero so as to appear or disappear. + + \table + \row + \o \image verticalpositioner_transition.gif + \o + \qml +Column { + spacing: 2 + add: ... + move: ... + ... +} + \endqml + \endtable + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + height of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. + +*/ +/*! + \qmlproperty Transition Column::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x,y) of items. + + Added can mean that either the object has been created or + reparented, and thus is now a child or the positioner, or that the + object has had its opacity increased from zero, and thus is now + visible. + + +*/ +/*! + \qmlproperty Transition Column::move + + This property holds the transition to apply when moving an item + within the positioner. Positioner transitions will only affect + the position (x,y) of items. + + This can happen when other items are added or removed from the + positioner, or when items resize themselves. + + \table + \row + \o \image positioner-move.gif + \o + \qml +Column { + move: Transition { + NumberAnimation { + properties: "y" + easing.type: Easing.OutBounce + } + } +} + \endqml + \endtable +*/ +/*! + \qmlproperty int Column::spacing + + spacing is the amount in pixels left empty between each adjacent + item, and defaults to 0. + + The below example places a Grid containing a red, a blue and a + green rectangle on a gray background. The area the grid positioner + occupies is colored white. The top positioner has the default of no spacing, + and the bottom positioner has its spacing set to 2. + + \image spacing_a.png + \image spacing_b.png + +*/ +/*! + \internal + \class QDeclarativeColumn + \brief The QDeclarativeColumn class lines up items vertically. +*/ +QDeclarativeColumn::QDeclarativeColumn(QDeclarativeItem *parent) +: QDeclarativeBasePositioner(Vertical, parent) +{ +} + +static inline bool isInvisible(QDeclarativeItem *child) +{ + return child->opacity() == 0.0 || !child->isVisible() || !child->width() || !child->height(); +} + +void QDeclarativeColumn::doPositioning(QSizeF *contentSize) +{ + int voffset = 0; + + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (!child.item || isInvisible(child.item)) + continue; + + if(child.item->y() != voffset) + positionY(voffset, child); + + contentSize->setWidth(qMax(contentSize->width(), child.item->width())); + + voffset += child.item->height(); + voffset += spacing(); + } + + contentSize->setHeight(voffset - spacing()); +} + +void QDeclarativeColumn::reportConflictingAnchors() +{ + QDeclarativeBasePositionerPrivate *d = static_cast(QDeclarativeBasePositionerPrivate::get(this)); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item) { + QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(child.item)->_anchors; + if (anchors) { + QDeclarativeAnchors::Anchors usedAnchors = anchors->usedAnchors(); + if (usedAnchors & QDeclarativeAnchors::TopAnchor || + usedAnchors & QDeclarativeAnchors::BottomAnchor || + usedAnchors & QDeclarativeAnchors::VCenterAnchor || + anchors->fill() || anchors->centerIn()) { + d->anchorConflict = true; + break; + } + } + } + } + if (d->anchorConflict) { + qmlInfo(this) << "Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column"; + } +} + +/*! + \qmlclass Row QDeclarativeRow + \since 4.7 + \brief The Row item lines up its children horizontally. + \inherits Item + + The Row item positions its child items so that they are + horizontally aligned and not overlapping. Spacing can be added between the + items, and a margin around all items can also be added. It also provides for + transitions to be set when items are added, moved, or removed in the + positioner. Adding and removing apply both to items which are deleted or have + their position in the document changed so as to no longer be children of the + positioner, as well as to items which have their opacity set to or from zero + so as to appear or disappear. + + The below example lays out differently shaped rectangles using a Row. + \qml +Row { + spacing: 2 + Rectangle { color: "red"; width: 50; height: 50 } + Rectangle { color: "green"; width: 20; height: 50 } + Rectangle { color: "blue"; width: 50; height: 20 } +} + \endqml + \image horizontalpositioner_example.png + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + width of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. + +*/ +/*! + \qmlproperty Transition Row::add + This property holds the transition to apply when adding an item to the positioner. + The transition will only be applied to the added item(s). + Positioner transitions will only affect the position (x,y) of items. + + Added can mean that either the object has been created or + reparented, and thus is now a child or the positioner, or that the + object has had its opacity increased from zero, and thus is now + visible. + + +*/ +/*! + \qmlproperty Transition Row::move + + This property holds the transition to apply when moving an item + within the positioner. Positioner transitions will only affect + the position (x,y) of items. + + This can happen when other items are added or removed from the + positioner, or when items resize themselves. + + \qml +Row { + id: positioner + move: Transition { + NumberAnimation { + properties: "x" + ease: "easeOutBounce" + } + } +} + \endqml + +*/ +/*! + \qmlproperty int Row::spacing + + spacing is the amount in pixels left empty between each adjacent + item, and defaults to 0. + + The below example places a Grid containing a red, a blue and a + green rectangle on a gray background. The area the grid positioner + occupies is colored white. The top positioner has the default of no spacing, + and the bottom positioner has its spacing set to 2. + + \image spacing_a.png + \image spacing_b.png + +*/ +/*! + \internal + \class QDeclarativeRow + \brief The QDeclarativeRow class lines up items horizontally. +*/ +QDeclarativeRow::QDeclarativeRow(QDeclarativeItem *parent) +: QDeclarativeBasePositioner(Horizontal, parent) +{ +} + +void QDeclarativeRow::doPositioning(QSizeF *contentSize) +{ + int hoffset = 0; + + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (!child.item || isInvisible(child.item)) + continue; + + if(child.item->x() != hoffset) + positionX(hoffset, child); + + contentSize->setHeight(qMax(contentSize->height(), child.item->height())); + + hoffset += child.item->width(); + hoffset += spacing(); + } + + contentSize->setWidth(hoffset - spacing()); +} + +void QDeclarativeRow::reportConflictingAnchors() +{ + QDeclarativeBasePositionerPrivate *d = static_cast(QDeclarativeBasePositionerPrivate::get(this)); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item) { + QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(child.item)->_anchors; + if (anchors) { + QDeclarativeAnchors::Anchors usedAnchors = anchors->usedAnchors(); + if (usedAnchors & QDeclarativeAnchors::LeftAnchor || + usedAnchors & QDeclarativeAnchors::RightAnchor || + usedAnchors & QDeclarativeAnchors::HCenterAnchor || + anchors->fill() || anchors->centerIn()) { + d->anchorConflict = true; + break; + } + } + } + } + if (d->anchorConflict) + qmlInfo(this) << "Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row"; +} + +/*! + \qmlclass Grid QDeclarativeGrid + \since 4.7 + \brief The Grid item positions its children in a grid. + \inherits Item + + The Grid item positions its child items so that they are + aligned in a grid and are not overlapping. Spacing can be added + between the items. It also provides for transitions to be set when items are + added, moved, or removed in the positioner. Adding and removing apply + both to items which are deleted or have their position in the + document changed so as to no longer be children of the positioner, as + well as to items which have their opacity set to or from zero so + as to appear or disappear. + + The Grid defaults to using four columns, and as many rows as + are necessary to fit all the child items. The number of rows + and/or the number of columns can be constrained by setting the rows + or columns properties. The grid positioner calculates a grid with + rectangular cells of sufficient size to hold all items, and then + places the items in the cells, going across then down, and + positioning each item at the (0,0) corner of the cell. The below + example demonstrates this. + + \table + \row + \o \image gridLayout_example.png + \o + \qml +Grid { + columns: 3 + spacing: 2 + Rectangle { color: "red"; width: 50; height: 50 } + Rectangle { color: "green"; width: 20; height: 50 } + Rectangle { color: "blue"; width: 50; height: 20 } + Rectangle { color: "cyan"; width: 50; height: 50 } + Rectangle { color: "magenta"; width: 10; height: 10 } +} + \endqml + \endtable + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + width or height of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. +*/ +/*! + \qmlproperty Transition Grid::add + This property holds the transition to apply when adding an item to the positioner. + The transition is only applied to the added item(s). + Positioner transitions will only affect the position (x,y) of items, + as that is all the positioners affect. To animate other property change + you will have to do so based on how you have changed those properties. + + Added can mean that either the object has been created or + reparented, and thus is now a child or the positioner, or that the + object has had its opacity increased from zero, and thus is now + visible. + + +*/ +/*! + \qmlproperty Transition Grid::move + This property holds the transition to apply when moving an item within the positioner. + Positioner transitions will only affect the position (x,y) of items. + + This can happen when other items are added or removed from the positioner, or + when items resize themselves. + + \qml +Grid { + move: Transition { + NumberAnimation { + properties: "x,y" + ease: "easeOutBounce" + } + } +} + \endqml + +*/ +/*! + \qmlproperty int Grid::spacing + + spacing is the amount in pixels left empty between each adjacent + item, and defaults to 0. + + The below example places a Grid containing a red, a blue and a + green rectangle on a gray background. The area the grid positioner + occupies is colored white. The top positioner has the default of no spacing, + and the bottom positioner has its spacing set to 2. + + \image spacing_a.png + \image spacing_b.png + +*/ +/*! + \internal + \class QDeclarativeGrid + \brief The QDeclarativeGrid class lays out items in a grid. +*/ +QDeclarativeGrid::QDeclarativeGrid(QDeclarativeItem *parent) : + QDeclarativeBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_flow(LeftToRight) +{ +} + +/*! + \qmlproperty int Grid::columns + This property holds the number of columns in the grid. + + When the columns property is set the Grid will always have + that many columns. Note that if you do not have enough items to + fill this many columns some columns will be of zero width. +*/ + +/*! + \qmlproperty int Grid::rows + This property holds the number of rows in the grid. + + When the rows property is set the Grid will always have that + many rows. Note that if you do not have enough items to fill this + many rows some rows will be of zero width. +*/ + +void QDeclarativeGrid::setColumns(const int columns) +{ + if (columns == m_columns) + return; + m_columns = columns; + prePositioning(); + emit columnsChanged(); +} + +void QDeclarativeGrid::setRows(const int rows) +{ + if (rows == m_rows) + return; + m_rows = rows; + prePositioning(); + emit rowsChanged(); +} + +/*! + \qmlproperty enumeration Grid::flow + This property holds the flow of the layout. + + Possible values are \c Grid.LeftToRight (default) and \c Grid.TopToBottom. + + If \a flow is \c Grid.LeftToRight, the items are positioned next to + to each other from left to right, then wrapped to the next line. + If \a flow is \c Grid.TopToBottom, the items are positioned next to each + other from top to bottom, then wrapped to the next column. +*/ +QDeclarativeGrid::Flow QDeclarativeGrid::flow() const +{ + return m_flow; +} + +void QDeclarativeGrid::setFlow(Flow flow) +{ + if (m_flow != flow) { + m_flow = flow; + prePositioning(); + emit flowChanged(); + } +} + +void QDeclarativeGrid::doPositioning(QSizeF *contentSize) +{ + int c = m_columns; + int r = m_rows; + int numVisible = positionedItems.count(); + if (m_columns <= 0 && m_rows <= 0){ + c = 4; + r = (numVisible+3)/4; + } else if (m_rows <= 0){ + r = (numVisible+(m_columns-1))/m_columns; + } else if (m_columns <= 0){ + c = (numVisible+(m_rows-1))/m_rows; + } + + QList maxColWidth; + QList maxRowHeight; + int childIndex =0; + if (m_flow == LeftToRight) { + for (int i=0; i < r; i++){ + for (int j=0; j < c; j++){ + if (j==0) + maxRowHeight << 0; + if (i==0) + maxColWidth << 0; + + if (childIndex == positionedItems.count()) + continue; + const PositionedItem &child = positionedItems.at(childIndex++); + if (!child.item || isInvisible(child.item)) + continue; + if (child.item->width() > maxColWidth[j]) + maxColWidth[j] = child.item->width(); + if (child.item->height() > maxRowHeight[i]) + maxRowHeight[i] = child.item->height(); + } + } + } else { + for (int j=0; j < c; j++){ + for (int i=0; i < r; i++){ + if (j==0) + maxRowHeight << 0; + if (i==0) + maxColWidth << 0; + + if (childIndex == positionedItems.count()) + continue; + const PositionedItem &child = positionedItems.at(childIndex++); + if (!child.item || isInvisible(child.item)) + continue; + if (child.item->width() > maxColWidth[j]) + maxColWidth[j] = child.item->width(); + if (child.item->height() > maxRowHeight[i]) + maxRowHeight[i] = child.item->height(); + } + } + } + + int xoffset=0; + int yoffset=0; + int curRow =0; + int curCol =0; + for (int i = 0; i < positionedItems.count(); ++i) { + const PositionedItem &child = positionedItems.at(i); + if (!child.item || isInvisible(child.item)) + continue; + if((child.item->x()!=xoffset)||(child.item->y()!=yoffset)){ + positionX(xoffset, child); + positionY(yoffset, child); + } + + if (m_flow == LeftToRight) { + contentSize->setWidth(qMax(contentSize->width(), xoffset + child.item->width())); + contentSize->setHeight(yoffset + maxRowHeight[curRow]); + + xoffset+=maxColWidth[curCol]+spacing(); + curCol++; + curCol%=c; + if (!curCol){ + yoffset+=maxRowHeight[curRow]+spacing(); + xoffset=0; + curRow++; + if (curRow>=r) + break; + } + } else { + contentSize->setHeight(qMax(contentSize->height(), yoffset + child.item->height())); + contentSize->setWidth(xoffset + maxColWidth[curCol]); + + yoffset+=maxRowHeight[curRow]+spacing(); + curRow++; + curRow%=r; + if (!curRow){ + xoffset+=maxColWidth[curCol]+spacing(); + yoffset=0; + curCol++; + if (curCol>=c) + break; + } + } + } +} + +void QDeclarativeGrid::reportConflictingAnchors() +{ + QDeclarativeBasePositionerPrivate *d = static_cast(QDeclarativeBasePositionerPrivate::get(this)); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item) { + QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(child.item)->_anchors; + if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) { + d->anchorConflict = true; + break; + } + } + } + if (d->anchorConflict) + qmlInfo(this) << "Cannot specify anchors for items inside Grid"; +} + +/*! + \qmlclass Flow QDeclarativeFlow + \since 4.7 + \brief The Flow item lines up its children side by side, wrapping as necessary. + \inherits Item + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + width or height of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. + +*/ +/*! + \qmlproperty Transition Flow::add + This property holds the transition to apply when adding an item to the positioner. + The transition will only be applied to the added item(s). + Positioner transitions will only affect the position (x,y) of items. + + Added can mean that either the object has been created or reparented, and thus is now a child or the positioner, or that the object has had its opacity increased from zero, and thus is now visible. + + +*/ +/*! + \qmlproperty Transition Flow::move + This property holds the transition to apply when moving an item within the positioner. + Positioner transitions will only affect the position (x,y) of items. + + This can happen when other items are added or removed from the positioner, or when items resize themselves. + + \qml +Flow { + id: positioner + move: Transition { + NumberAnimation { + properties: "x,y" + ease: "easeOutBounce" + } + } +} + \endqml + +*/ +/*! + \qmlproperty int Flow::spacing + + spacing is the amount in pixels left empty between each adjacent + item, and defaults to 0. + +*/ + +class QDeclarativeFlowPrivate : public QDeclarativeBasePositionerPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeFlow) + +public: + QDeclarativeFlowPrivate() + : QDeclarativeBasePositionerPrivate(), flow(QDeclarativeFlow::LeftToRight) + {} + + QDeclarativeFlow::Flow flow; +}; + +QDeclarativeFlow::QDeclarativeFlow(QDeclarativeItem *parent) +: QDeclarativeBasePositioner(*(new QDeclarativeFlowPrivate), Both, parent) +{ + Q_D(QDeclarativeFlow); + // Flow layout requires relayout if its own size changes too. + d->addItemChangeListener(d, QDeclarativeItemPrivate::Geometry); +} + +/*! + \qmlproperty enumeration Flow::flow + This property holds the flow of the layout. + + Possible values are \c Flow.LeftToRight (default) and \c Flow.TopToBottom. + + If \a flow is \c Flow.LeftToRight, the items are positioned next to + to each other from left to right until the width of the Flow + is exceeded, then wrapped to the next line. + If \a flow is \c Flow.TopToBottom, the items are positioned next to each + other from top to bottom until the height of the Flow is exceeded, + then wrapped to the next column. +*/ +QDeclarativeFlow::Flow QDeclarativeFlow::flow() const +{ + Q_D(const QDeclarativeFlow); + return d->flow; +} + +void QDeclarativeFlow::setFlow(Flow flow) +{ + Q_D(QDeclarativeFlow); + if (d->flow != flow) { + d->flow = flow; + prePositioning(); + emit flowChanged(); + } +} + +void QDeclarativeFlow::doPositioning(QSizeF *contentSize) +{ + Q_D(QDeclarativeFlow); + + int hoffset = 0; + int voffset = 0; + int linemax = 0; + + for (int i = 0; i < positionedItems.count(); ++i) { + const PositionedItem &child = positionedItems.at(i); + if (!child.item || isInvisible(child.item)) + continue; + + if (d->flow == LeftToRight) { + if (hoffset && hoffset + child.item->width() > width()) { + hoffset = 0; + voffset += linemax + spacing(); + linemax = 0; + } + } else { + if (voffset && voffset + child.item->height() > height()) { + voffset = 0; + hoffset += linemax + spacing(); + linemax = 0; + } + } + + if(child.item->x() != hoffset || child.item->y() != voffset){ + positionX(hoffset, child); + positionY(voffset, child); + } + + contentSize->setWidth(qMax(contentSize->width(), hoffset + child.item->width())); + contentSize->setHeight(qMax(contentSize->height(), voffset + child.item->height())); + + if (d->flow == LeftToRight) { + hoffset += child.item->width(); + hoffset += spacing(); + linemax = qMax(linemax, qCeil(child.item->height())); + } else { + voffset += child.item->height(); + voffset += spacing(); + linemax = qMax(linemax, qCeil(child.item->width())); + } + } +} + +void QDeclarativeFlow::reportConflictingAnchors() +{ + Q_D(QDeclarativeFlow); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item) { + QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(child.item)->_anchors; + if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) { + d->anchorConflict = true; + break; + } + } + } + if (d->anchorConflict) + qmlInfo(this) << "Cannot specify anchors for items inside Flow"; +} + +QT_END_NAMESPACE