1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "private/qdeclarativepositioners_p.h"
43 #include "private/qdeclarativepositioners_p_p.h"
45 #include <qdeclarative.h>
46 #include <qdeclarativestate_p.h>
47 #include <qdeclarativestategroup_p.h>
48 #include <qdeclarativestateoperations_p.h>
49 #include <qdeclarativeinfo.h>
50 #include <QtCore/qmath.h>
53 #include <QCoreApplication>
57 static const QDeclarativeItemPrivate::ChangeTypes watchedChanges
58 = QDeclarativeItemPrivate::Geometry
59 | QDeclarativeItemPrivate::SiblingOrder
60 | QDeclarativeItemPrivate::Visibility
61 | QDeclarativeItemPrivate::Opacity
62 | QDeclarativeItemPrivate::Destroyed;
64 void QDeclarativeBasePositionerPrivate::watchChanges(QGraphicsObject *other)
66 if (QGraphicsItemPrivate::get(other)->isDeclarativeItem) {
67 QDeclarativeItemPrivate *otherPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(other));
68 otherPrivate->addItemChangeListener(this, watchedChanges);
70 Q_Q(QDeclarativeBasePositioner);
71 QObject::connect(other, SIGNAL(widthChanged()), q, SLOT(graphicsWidgetGeometryChanged()));
72 QObject::connect(other, SIGNAL(heightChanged()), q, SLOT(graphicsWidgetGeometryChanged()));
73 QObject::connect(other, SIGNAL(opacityChanged()), q, SLOT(graphicsWidgetGeometryChanged()));
74 QObject::connect(other, SIGNAL(visibleChanged()), q, SLOT(graphicsWidgetGeometryChanged()));
78 void QDeclarativeBasePositionerPrivate::unwatchChanges(QGraphicsObject* other)
80 if (QGraphicsItemPrivate::get(other)->isDeclarativeItem) {
81 QDeclarativeItemPrivate *otherPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(other));
82 otherPrivate->removeItemChangeListener(this, watchedChanges);
84 Q_Q(QDeclarativeBasePositioner);
85 QObject::disconnect(other, SIGNAL(widthChanged()), q, SLOT(graphicsWidgetGeometryChanged()));
86 QObject::disconnect(other, SIGNAL(heightChanged()), q, SLOT(graphicsWidgetGeometryChanged()));
87 QObject::disconnect(other, SIGNAL(opacityChanged()), q, SLOT(graphicsWidgetGeometryChanged()));
88 QObject::disconnect(other, SIGNAL(visibleChanged()), q, SLOT(graphicsWidgetGeometryChanged()));
92 void QDeclarativeBasePositioner::graphicsWidgetGeometryChanged()
99 \class QDeclarativeBasePositioner
100 \brief The QDeclarativeBasePositioner class provides a base for QDeclarativeGraphics layouts.
102 To create a QDeclarativeGraphics Positioner, simply subclass QDeclarativeBasePositioner and implement
103 doLayout(), which is automatically called when the layout might need
104 updating. In doLayout() use the setX and setY functions from QDeclarativeBasePositioner, and the
105 base class will apply the positions along with the appropriate transitions. The items to
106 position are provided in order as the protected member positionedItems.
108 You also need to set a PositionerType, to declare whether you are positioning the x, y or both
109 for the child items. Depending on the chosen type, only x or y changes will be applied.
111 Note that the subclass is responsible for adding the spacing in between items.
113 QDeclarativeBasePositioner::QDeclarativeBasePositioner(PositionerType at, QDeclarativeItem *parent)
114 : QDeclarativeImplicitSizeItem(*(new QDeclarativeBasePositionerPrivate), parent)
116 Q_D(QDeclarativeBasePositioner);
120 QDeclarativeBasePositioner::QDeclarativeBasePositioner(QDeclarativeBasePositionerPrivate &dd, PositionerType at, QDeclarativeItem *parent)
121 : QDeclarativeImplicitSizeItem(dd, parent)
123 Q_D(QDeclarativeBasePositioner);
127 QDeclarativeBasePositioner::~QDeclarativeBasePositioner()
129 Q_D(QDeclarativeBasePositioner);
130 for (int i = 0; i < positionedItems.count(); ++i)
131 d->unwatchChanges(positionedItems.at(i).item);
132 positionedItems.clear();
135 int QDeclarativeBasePositioner::spacing() const
137 Q_D(const QDeclarativeBasePositioner);
141 void QDeclarativeBasePositioner::setSpacing(int s)
143 Q_D(QDeclarativeBasePositioner);
148 emit spacingChanged();
151 QDeclarativeTransition *QDeclarativeBasePositioner::move() const
153 Q_D(const QDeclarativeBasePositioner);
154 return d->moveTransition;
157 void QDeclarativeBasePositioner::setMove(QDeclarativeTransition *mt)
159 Q_D(QDeclarativeBasePositioner);
160 if (mt == d->moveTransition)
162 d->moveTransition = mt;
166 QDeclarativeTransition *QDeclarativeBasePositioner::add() const
168 Q_D(const QDeclarativeBasePositioner);
169 return d->addTransition;
172 void QDeclarativeBasePositioner::setAdd(QDeclarativeTransition *add)
174 Q_D(QDeclarativeBasePositioner);
175 if (add == d->addTransition)
178 d->addTransition = add;
182 void QDeclarativeBasePositioner::componentComplete()
184 Q_D(QDeclarativeBasePositioner);
185 QDeclarativeItem::componentComplete();
186 positionedItems.reserve(d->QGraphicsItemPrivate::children.count());
188 reportConflictingAnchors();
191 QVariant QDeclarativeBasePositioner::itemChange(GraphicsItemChange change,
192 const QVariant &value)
194 Q_D(QDeclarativeBasePositioner);
195 if (change == ItemChildAddedChange){
196 QGraphicsItem* item = value.value<QGraphicsItem*>();
197 QGraphicsObject* child = 0;
199 child = item->toGraphicsObject();
202 } else if (change == ItemChildRemovedChange) {
203 QGraphicsItem* item = value.value<QGraphicsItem*>();
204 QGraphicsObject* child = 0;
206 child = item->toGraphicsObject();
208 QDeclarativeBasePositioner::PositionedItem posItem(child);
209 int idx = positionedItems.find(posItem);
211 d->unwatchChanges(child);
212 positionedItems.remove(idx);
217 return QDeclarativeItem::itemChange(change, value);
220 void QDeclarativeBasePositioner::prePositioning()
222 Q_D(QDeclarativeBasePositioner);
223 if (!isComponentComplete())
226 if (d->doingPositioning)
229 d->queuedPositioning = false;
230 d->doingPositioning = true;
231 //Need to order children by creation order modified by stacking order
232 QList<QGraphicsItem *> children = d->QGraphicsItemPrivate::children;
233 qSort(children.begin(), children.end(), d->insertionOrder);
235 QPODVector<PositionedItem,8> oldItems;
236 positionedItems.copyAndClear(oldItems);
237 for (int ii = 0; ii < children.count(); ++ii) {
238 QGraphicsObject *child = children.at(ii)->toGraphicsObject();
241 QGraphicsItemPrivate *childPrivate = static_cast<QGraphicsItemPrivate*>(QGraphicsItemPrivate::get(child));
242 PositionedItem *item = 0;
243 PositionedItem posItem(child);
244 int wIdx = oldItems.find(posItem);
246 d->watchChanges(child);
247 positionedItems.append(posItem);
248 item = &positionedItems[positionedItems.count()-1];
250 if (child->opacity() <= 0.0 || childPrivate->explicitlyHidden || !childPrivate->width() || !childPrivate->height())
251 item->isVisible = false;
253 item = &oldItems[wIdx];
254 // Items are only omitted from positioning if they are explicitly hidden
255 // i.e. their positioning is not affected if an ancestor is hidden.
256 if (child->opacity() <= 0.0 || childPrivate->explicitlyHidden || !childPrivate->width() || !childPrivate->height()) {
257 item->isVisible = false;
258 } else if (!item->isVisible) {
259 item->isVisible = true;
264 positionedItems.append(*item);
268 doPositioning(&contentSize);
269 if(d->addTransition || d->moveTransition)
270 finishApplyTransitions();
271 d->doingPositioning = false;
272 //Set implicit size to the size of its children
273 setImplicitHeight(contentSize.height());
274 setImplicitWidth(contentSize.width());
277 void QDeclarativeBasePositioner::positionX(int x, const PositionedItem &target)
279 Q_D(QDeclarativeBasePositioner);
280 if(d->type == Horizontal || d->type == Both){
282 if (!d->addTransition)
283 target.item->setX(x);
285 d->addActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x));
286 } else if (x != target.item->x()) {
287 if (!d->moveTransition)
288 target.item->setX(x);
290 d->moveActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x));
295 void QDeclarativeBasePositioner::positionY(int y, const PositionedItem &target)
297 Q_D(QDeclarativeBasePositioner);
298 if(d->type == Vertical || d->type == Both){
300 if (!d->addTransition)
301 target.item->setY(y);
303 d->addActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y));
304 } else if (y != target.item->y()) {
305 if (!d->moveTransition)
306 target.item->setY(y);
308 d->moveActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y));
313 void QDeclarativeBasePositioner::finishApplyTransitions()
315 Q_D(QDeclarativeBasePositioner);
316 // Note that if a transition is not set the transition manager will
317 // apply the changes directly, in the case add/move aren't set
318 d->addTransitionManager.transition(d->addActions, d->addTransition);
319 d->moveTransitionManager.transition(d->moveActions, d->moveTransition);
320 d->addActions.clear();
321 d->moveActions.clear();
325 \qmlclass Column QDeclarativeColumn
326 \ingroup qml-positioning-elements
328 \brief The Column item arranges its children vertically.
331 The Column item positions its child items so that they are vertically
332 aligned and not overlapping.
334 Spacing between items can be added using the \l spacing property.
335 Transitions can be used for cases where items managed by a Column are
336 added or moved. These are stored in the \l add and \l move properties
339 See \l{Using QML Positioner and Repeater Items} for more details about this item and other
342 \section1 Example Usage
344 The following example positions differently shaped rectangles using a Column
347 \image verticalpositioner_example.png
349 \snippet doc/src/snippets/declarative/column/vertical-positioner.qml document
351 \section1 Using Transitions
353 Transitions can be used to animate items that are added to, moved within,
354 or removed from a Column item. The \l add and \l move properties can be set to
355 the transitions that will be applied when items are added to, removed from,
356 or re-positioned within a Column item.
358 The use of transitions with positioners is described in more detail in the
359 \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML
360 Positioner and Repeater Items} document.
362 \image verticalpositioner_transition.gif
368 // Define an animation for adding a new item...
371 // Define an animation for moving items within the column...
377 \section1 Limitations
379 Note that the positioner assumes that the x and y positions of its children
380 will not change. If you manually change the x or y properties in script, bind
381 the x or y properties, use anchors on a child of a positioner, or have the
382 height of a child depend on the position of a child, then the
383 positioner may exhibit strange behavior. If you need to perform any of these
384 actions, consider positioning the items without the use of a Column.
386 Items with a width or height of 0 will not be positioned.
388 \sa Row, Grid, Flow, {declarative/positioners}{Positioners example}
391 \qmlproperty Transition Column::add
393 This property holds the transition to be applied when adding an
394 item to the positioner. The transition will only be applied to the
395 added item(s). Positioner transitions will only affect the
396 position (x, y) of items.
398 For a positioner, adding an item can mean that either the object
399 has been created or reparented, and thus is now a child or the
400 positioner, or that the object has had its opacity increased from
401 zero, and thus is now visible.
406 \qmlproperty Transition Column::move
408 This property holds the transition to apply when moving an item
409 within the positioner. Positioner transitions will only affect
410 the position (x, y) of items.
412 This transition can be performed when other items are added or removed
413 from the positioner, or when items resize themselves.
415 \image positioner-move.gif
422 easing.type: Easing.OutBounce
428 \sa add, {declarative/positioners}{Positioners example}
431 \qmlproperty int Column::spacing
433 The spacing is the amount in pixels left empty between adjacent
434 items. The default spacing is 0.
438 QDeclarativeColumn::QDeclarativeColumn(QDeclarativeItem *parent)
439 : QDeclarativeBasePositioner(Vertical, parent)
443 void QDeclarativeColumn::doPositioning(QSizeF *contentSize)
447 for (int ii = 0; ii < positionedItems.count(); ++ii) {
448 const PositionedItem &child = positionedItems.at(ii);
449 if (!child.item || !child.isVisible)
452 if(child.item->y() != voffset)
453 positionY(voffset, child);
455 contentSize->setWidth(qMax(contentSize->width(), QGraphicsItemPrivate::get(child.item)->width()));
457 voffset += QGraphicsItemPrivate::get(child.item)->height();
458 voffset += spacing();
461 contentSize->setHeight(voffset - spacing());
464 void QDeclarativeColumn::reportConflictingAnchors()
466 QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate*>(QDeclarativeBasePositionerPrivate::get(this));
467 for (int ii = 0; ii < positionedItems.count(); ++ii) {
468 const PositionedItem &child = positionedItems.at(ii);
469 if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) {
470 QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(static_cast<QDeclarativeItem *>(child.item))->_anchors;
472 QDeclarativeAnchors::Anchors usedAnchors = anchors->usedAnchors();
473 if (usedAnchors & QDeclarativeAnchors::TopAnchor ||
474 usedAnchors & QDeclarativeAnchors::BottomAnchor ||
475 usedAnchors & QDeclarativeAnchors::VCenterAnchor ||
476 anchors->fill() || anchors->centerIn()) {
477 d->anchorConflict = true;
483 if (d->anchorConflict) {
484 qmlInfo(this) << "Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column";
489 \qmlclass Row QDeclarativeRow
490 \ingroup qml-positioning-elements
492 \brief The Row item arranges its children horizontally.
495 The Row item positions its child items so that they are horizontally
496 aligned and not overlapping.
498 Use \l spacing to set the spacing between items in a Row, and use the
499 \l add and \l move properties to set the transitions that should be applied
500 when items are added to, removed from, or re-positioned within the Row.
502 See \l{Using QML Positioner and Repeater Items} for more details about this item and other
505 \section1 Example Usage
507 The following example lays out differently shaped rectangles using a Row.
509 \image horizontalpositioner_example.png
511 \snippet doc/src/snippets/declarative/row/row.qml document
513 \section1 Using Transitions
515 Transitions can be used to animate items that are added to, moved within,
516 or removed from a Grid item. The \l add and \l move properties can be set to
517 the transitions that will be applied when items are added to, removed from,
518 or re-positioned within a Row item.
520 \section1 Limitations
522 Note that the positioner assumes that the x and y positions of its children
523 will not change. If you manually change the x or y properties in script, bind
524 the x or y properties, use anchors on a child of a positioner, or have the
525 width of a child depend on the position of a child, then the
526 positioner may exhibit strange behaviour. If you need to perform any of these
527 actions, consider positioning the items without the use of a Row.
529 Items with a width or height of 0 will not be positioned.
531 \sa Column, Grid, Flow, {declarative/positioners}{Positioners example}
534 \qmlproperty Transition Row::add
536 This property holds the transition to be applied when adding an
537 item to the positioner. The transition will only be applied to the
538 added item(s). Positioner transitions will only affect the
539 position (x, y) of items.
541 For a positioner, adding an item can mean that either the object
542 has been created or reparented, and thus is now a child or the
543 positioner, or that the object has had its opacity increased from
544 zero, and thus is now visible.
549 \qmlproperty Transition Row::move
551 This property holds the transition to be applied when moving an
552 item within the positioner. Positioner transitions will only affect
553 the position (x, y) of items.
555 This transition can be performed when other items are added or removed
556 from the positioner, or when items resize themselves.
564 ease: "easeOutBounce"
570 \sa add, {declarative/positioners}{Positioners example}
573 \qmlproperty int Row::spacing
575 The spacing is the amount in pixels left empty between adjacent
576 items. The default spacing is 0.
580 QDeclarativeRow::QDeclarativeRow(QDeclarativeItem *parent)
581 : QDeclarativeBasePositioner(Horizontal, parent)
586 \qmlproperty enumeration Row::layoutDirection
589 This property holds the layoutDirection of the row.
594 \o Qt.LeftToRight (default) - Items are laid out from left to right. If the width of the row is explicitly set,
595 the left anchor remains to the left of the row.
596 \o Qt.RightToLeft - Items are laid out from right to left. If the width of the row is explicitly set,
597 the right anchor remains to the right of the row.
600 \sa Grid::layoutDirection, Flow::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example}
602 Qt::LayoutDirection QDeclarativeRow::layoutDirection() const
604 return QDeclarativeBasePositionerPrivate::getLayoutDirection(this);
607 void QDeclarativeRow::setLayoutDirection(Qt::LayoutDirection layoutDirection)
609 QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate* >(QDeclarativeBasePositionerPrivate::get(this));
610 if (d->layoutDirection != layoutDirection) {
611 d->layoutDirection = layoutDirection;
612 // For RTL layout the positioning changes when the width changes.
613 if (d->layoutDirection == Qt::RightToLeft)
614 d->addItemChangeListener(d, QDeclarativeItemPrivate::Geometry);
616 d->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry);
618 emit layoutDirectionChanged();
619 emit effectiveLayoutDirectionChanged();
624 \qmlproperty enumeration Row::effectiveLayoutDirection
625 This property holds the effective layout direction of the row positioner.
627 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
628 the visual layout direction of the row positioner will be mirrored. However, the
629 property \l {Row::layoutDirection}{layoutDirection} will remain unchanged.
631 \sa Row::layoutDirection, {LayoutMirroring}{LayoutMirroring}
634 Qt::LayoutDirection QDeclarativeRow::effectiveLayoutDirection() const
636 return QDeclarativeBasePositionerPrivate::getEffectiveLayoutDirection(this);
639 void QDeclarativeRow::doPositioning(QSizeF *contentSize)
641 QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate*>(QDeclarativeBasePositionerPrivate::get(this));
645 for (int ii = 0; ii < positionedItems.count(); ++ii) {
646 const PositionedItem &child = positionedItems.at(ii);
647 if (!child.item || !child.isVisible)
650 if(d->isLeftToRight()){
651 if(child.item->x() != hoffset)
652 positionX(hoffset, child);
657 contentSize->setHeight(qMax(contentSize->height(), QGraphicsItemPrivate::get(child.item)->height()));
659 hoffset += QGraphicsItemPrivate::get(child.item)->width();
660 hoffset += spacing();
663 contentSize->setWidth(hoffset - spacing());
665 if(d->isLeftToRight())
668 //Right to Left layout
671 end = contentSize->width();
676 for (int ii = 0; ii < positionedItems.count(); ++ii) {
677 const PositionedItem &child = positionedItems.at(ii);
678 if (!child.item || !child.isVisible)
680 hoffset = end - hoffsets[acc++] - QGraphicsItemPrivate::get(child.item)->width();
681 if(child.item->x() != hoffset)
682 positionX(hoffset, child);
686 void QDeclarativeRow::reportConflictingAnchors()
688 QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate*>(QDeclarativeBasePositionerPrivate::get(this));
689 for (int ii = 0; ii < positionedItems.count(); ++ii) {
690 const PositionedItem &child = positionedItems.at(ii);
691 if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) {
692 QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(static_cast<QDeclarativeItem *>(child.item))->_anchors;
694 QDeclarativeAnchors::Anchors usedAnchors = anchors->usedAnchors();
695 if (usedAnchors & QDeclarativeAnchors::LeftAnchor ||
696 usedAnchors & QDeclarativeAnchors::RightAnchor ||
697 usedAnchors & QDeclarativeAnchors::HCenterAnchor ||
698 anchors->fill() || anchors->centerIn()) {
699 d->anchorConflict = true;
705 if (d->anchorConflict)
706 qmlInfo(this) << "Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row";
710 \qmlclass Grid QDeclarativeGrid
711 \ingroup qml-positioning-elements
713 \brief The Grid item positions its children in a grid.
716 The Grid item positions its child items so that they are
717 aligned in a grid and are not overlapping.
719 The grid positioner calculates a grid of rectangular cells of sufficient
720 size to hold all items, placing the items in the cells, from left to right
721 and top to bottom. Each item is positioned in the top-left corner of its
722 cell with position (0, 0).
724 A Grid defaults to four columns, and as many rows as are necessary to
725 fit all child items. The number of rows and columns can be constrained
726 by setting the \l rows and \l columns properties.
728 Spacing can be added between child items by setting the \l spacing
729 property. The amount of spacing applied will be the same in the
730 horizontal and vertical directions.
732 See \l{Using QML Positioner and Repeater Items} for more details about this item and other
735 \section1 Example Usage
737 The following example demonstrates this.
739 \image gridLayout_example.png
741 \snippet doc/src/snippets/declarative/grid/grid.qml document
743 \section1 Using Transitions
745 Transitions can be used to animate items that are added to, moved within,
746 or removed from a Grid item. The \l add and \l move properties can be set to
747 the transitions that will be applied when items are added to, removed from,
748 or re-positioned within a Grid item.
750 \section1 Limitations
752 Note that the positioner assumes that the x and y positions of its children
753 will not change. If you manually change the x or y properties in script, bind
754 the x or y properties, use anchors on a child of a positioner, or have the
755 width or height of a child depend on the position of a child, then the
756 positioner may exhibit strange behaviour. If you need to perform any of these
757 actions, consider positioning the items without the use of a Grid.
759 Items with a width or height of 0 will not be positioned.
761 \sa Flow, Row, Column, {declarative/positioners}{Positioners example}
764 \qmlproperty Transition Grid::add
766 This property holds the transition to be applied when adding an
767 item to the positioner. The transition will only be applied to the
768 added item(s). Positioner transitions will only affect the
769 position (x, y) of items.
771 For a positioner, adding an item can mean that either the object
772 has been created or reparented, and thus is now a child or the
773 positioner, or that the object has had its opacity increased from
774 zero, and thus is now visible.
779 \qmlproperty Transition Grid::move
781 This property holds the transition to be applied when moving an
782 item within the positioner. Positioner transitions will only affect
783 the position (x, y) of items.
785 This transition can be performed when other items are added or removed
786 from the positioner, or when items resize themselves.
793 ease: "easeOutBounce"
799 \sa add, {declarative/positioners}{Positioners example}
802 \qmlproperty int Grid::spacing
804 The spacing is the amount in pixels left empty between adjacent
805 items. The default spacing is 0.
807 The below example places a Grid containing a red, a blue and a
808 green rectangle on a gray background. The area the grid positioner
809 occupies is colored white. The positioner on the left has the
810 no spacing (the default), and the positioner on the right has
813 \inlineimage qml-grid-no-spacing.png
814 \inlineimage qml-grid-spacing.png
818 QDeclarativeGrid::QDeclarativeGrid(QDeclarativeItem *parent) :
819 QDeclarativeBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_flow(LeftToRight)
824 \qmlproperty int Grid::columns
826 This property holds the number of columns in the grid. The default
827 number of columns is 4.
829 If the grid does not have enough items to fill the specified
830 number of columns, some columns will be of zero width.
834 \qmlproperty int Grid::rows
835 This property holds the number of rows in the grid.
837 If the grid does not have enough items to fill the specified
838 number of rows, some rows will be of zero width.
841 void QDeclarativeGrid::setColumns(const int columns)
843 if (columns == m_columns)
847 emit columnsChanged();
850 void QDeclarativeGrid::setRows(const int rows)
860 \qmlproperty enumeration Grid::flow
861 This property holds the flow of the layout.
866 \o Grid.LeftToRight (default) - Items are positioned next to
867 each other in the \l layoutDirection, then wrapped to the next line.
868 \o Grid.TopToBottom - Items are positioned next to each
869 other from top to bottom, then wrapped to the next column.
872 QDeclarativeGrid::Flow QDeclarativeGrid::flow() const
877 void QDeclarativeGrid::setFlow(Flow flow)
879 if (m_flow != flow) {
887 \qmlproperty enumeration Grid::layoutDirection
890 This property holds the layout direction of the layout.
895 \o Qt.LeftToRight (default) - Items are positioned from the top to bottom,
896 and left to right. The flow direction is dependent on the
897 \l Grid::flow property.
898 \o Qt.RightToLeft - Items are positioned from the top to bottom,
899 and right to left. The flow direction is dependent on the
900 \l Grid::flow property.
903 \sa Flow::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example}
905 Qt::LayoutDirection QDeclarativeGrid::layoutDirection() const
907 return QDeclarativeBasePositionerPrivate::getLayoutDirection(this);
910 void QDeclarativeGrid::setLayoutDirection(Qt::LayoutDirection layoutDirection)
912 QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate*>(QDeclarativeBasePositionerPrivate::get(this));
913 if (d->layoutDirection != layoutDirection) {
914 d->layoutDirection = layoutDirection;
915 // For RTL layout the positioning changes when the width changes.
916 if (d->layoutDirection == Qt::RightToLeft)
917 d->addItemChangeListener(d, QDeclarativeItemPrivate::Geometry);
919 d->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry);
921 emit layoutDirectionChanged();
922 emit effectiveLayoutDirectionChanged();
927 \qmlproperty enumeration Grid::effectiveLayoutDirection
928 This property holds the effective layout direction of the grid positioner.
930 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
931 the visual layout direction of the grid positioner will be mirrored. However, the
932 property \l {Grid::layoutDirection}{layoutDirection} will remain unchanged.
934 \sa Grid::layoutDirection, {LayoutMirroring}{LayoutMirroring}
937 Qt::LayoutDirection QDeclarativeGrid::effectiveLayoutDirection() const
939 return QDeclarativeBasePositionerPrivate::getEffectiveLayoutDirection(this);
942 void QDeclarativeGrid::doPositioning(QSizeF *contentSize)
944 QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate*>(QDeclarativeBasePositionerPrivate::get(this));
947 //Is allocating the extra QPODVector too much overhead?
948 QPODVector<PositionedItem, 8> visibleItems;//we aren't concerned with invisible items
949 visibleItems.reserve(positionedItems.count());
950 for(int i=0; i<positionedItems.count(); i++)
951 if(positionedItems[i].item && positionedItems[i].isVisible)
952 visibleItems.append(positionedItems[i]);
954 int numVisible = visibleItems.count();
955 if (m_columns <= 0 && m_rows <= 0){
957 r = (numVisible+3)/4;
958 } else if (m_rows <= 0){
959 r = (numVisible+(m_columns-1))/m_columns;
960 } else if (m_columns <= 0){
961 c = (numVisible+(m_rows-1))/m_rows;
965 return; //Nothing to do
967 QList<int> maxColWidth;
968 QList<int> maxRowHeight;
970 if (m_flow == LeftToRight) {
971 for (int i=0; i < r; i++){
972 for (int j=0; j < c; j++){
978 if (childIndex == visibleItems.count())
981 const PositionedItem &child = visibleItems.at(childIndex++);
982 QGraphicsItemPrivate *childPrivate = QGraphicsItemPrivate::get(child.item);
983 if (childPrivate->width() > maxColWidth[j])
984 maxColWidth[j] = childPrivate->width();
985 if (childPrivate->height() > maxRowHeight[i])
986 maxRowHeight[i] = childPrivate->height();
990 for (int j=0; j < c; j++){
991 for (int i=0; i < r; i++){
997 if (childIndex == visibleItems.count())
1000 const PositionedItem &child = visibleItems.at(childIndex++);
1001 QGraphicsItemPrivate *childPrivate = QGraphicsItemPrivate::get(child.item);
1002 if (childPrivate->width() > maxColWidth[j])
1003 maxColWidth[j] = childPrivate->width();
1004 if (childPrivate->height() > maxRowHeight[i])
1005 maxRowHeight[i] = childPrivate->height();
1011 for(int j=0; j < maxColWidth.size(); j++){
1013 widthSum += spacing();
1014 widthSum += maxColWidth[j];
1018 for(int i=0; i < maxRowHeight.size(); i++){
1020 heightSum += spacing();
1021 heightSum += maxRowHeight[i];
1024 contentSize->setHeight(heightSum);
1025 contentSize->setWidth(widthSum);
1034 if(!d->isLeftToRight())
1039 for (int i = 0; i < visibleItems.count(); ++i) {
1040 const PositionedItem &child = visibleItems.at(i);
1041 int childXOffset = xoffset;
1042 if(!d->isLeftToRight())
1043 childXOffset -= QGraphicsItemPrivate::get(child.item)->width();
1044 if((child.item->x()!=childXOffset)||(child.item->y()!=yoffset)){
1045 positionX(childXOffset, child);
1046 positionY(yoffset, child);
1049 if (m_flow == LeftToRight) {
1050 if(d->isLeftToRight())
1051 xoffset+=maxColWidth[curCol]+spacing();
1053 xoffset-=maxColWidth[curCol]+spacing();
1057 yoffset+=maxRowHeight[curRow]+spacing();
1058 if(d->isLeftToRight())
1067 yoffset+=maxRowHeight[curRow]+spacing();
1071 if(d->isLeftToRight())
1072 xoffset+=maxColWidth[curCol]+spacing();
1074 xoffset-=maxColWidth[curCol]+spacing();
1084 void QDeclarativeGrid::reportConflictingAnchors()
1086 QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate*>(QDeclarativeBasePositionerPrivate::get(this));
1087 for (int ii = 0; ii < positionedItems.count(); ++ii) {
1088 const PositionedItem &child = positionedItems.at(ii);
1089 if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) {
1090 QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(static_cast<QDeclarativeItem *>(child.item))->_anchors;
1091 if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) {
1092 d->anchorConflict = true;
1097 if (d->anchorConflict)
1098 qmlInfo(this) << "Cannot specify anchors for items inside Grid";
1102 \qmlclass Flow QDeclarativeFlow
1103 \ingroup qml-positioning-elements
1105 \brief The Flow item arranges its children side by side, wrapping as necessary.
1108 The Flow item positions its child items like words on a page, wrapping them
1109 to create rows or columns of items that do not overlap.
1111 Spacing between items can be added using the \l spacing property.
1112 Transitions can be used for cases where items managed by a Column are
1113 added or moved. These are stored in the \l add and \l move properties
1116 See \l{Using QML Positioner and Repeater Items} for more details about this item and other
1119 \section1 Example Usage
1121 The following example positions \l Text items within a parent item using
1124 \image qml-flow-snippet.png
1126 \snippet doc/src/snippets/declarative/flow.qml flow item
1128 \section1 Using Transitions
1130 Transitions can be used to animate items that are added to, moved within,
1131 or removed from a Flow item. The \l add and \l move properties can be set to
1132 the transitions that will be applied when items are added to, removed from,
1133 or re-positioned within a Flow item.
1135 The use of transitions with positioners is described in more detail in the
1136 \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML
1137 Positioner and Repeater Items} document.
1139 \section1 Limitations
1141 Note that the positioner assumes that the x and y positions of its children
1142 will not change. If you manually change the x or y properties in script, bind
1143 the x or y properties, use anchors on a child of a positioner, or have the
1144 width or height of a child depend on the position of a child, then the
1145 positioner may exhibit strange behaviour. If you need to perform any of these
1146 actions, consider positioning the items without the use of a Flow.
1148 Items with a width or height of 0 will not be positioned.
1150 \sa Column, Row, Grid, {declarative/positioners}{Positioners example}
1153 \qmlproperty Transition Flow::add
1155 This property holds the transition to be applied when adding an
1156 item to the positioner. The transition will only be applied to the
1157 added item(s). Positioner transitions will only affect the
1158 position (x, y) of items.
1160 For a positioner, adding an item can mean that either the object
1161 has been created or reparented, and thus is now a child or the
1162 positioner, or that the object has had its opacity increased from
1163 zero, and thus is now visible.
1168 \qmlproperty Transition Flow::move
1170 This property holds the transition to be applied when moving an
1171 item within the positioner. Positioner transitions will only affect
1172 the position (x, y) of items.
1174 This transition can be performed when other items are added or removed
1175 from the positioner, or when items resize themselves.
1183 ease: "easeOutBounce"
1189 \sa add, {declarative/positioners}{Positioners example}
1192 \qmlproperty int Flow::spacing
1194 spacing is the amount in pixels left empty between each adjacent
1195 item, and defaults to 0.
1200 class QDeclarativeFlowPrivate : public QDeclarativeBasePositionerPrivate
1202 Q_DECLARE_PUBLIC(QDeclarativeFlow)
1205 QDeclarativeFlowPrivate()
1206 : QDeclarativeBasePositionerPrivate(), flow(QDeclarativeFlow::LeftToRight)
1209 QDeclarativeFlow::Flow flow;
1212 QDeclarativeFlow::QDeclarativeFlow(QDeclarativeItem *parent)
1213 : QDeclarativeBasePositioner(*(new QDeclarativeFlowPrivate), Both, parent)
1215 Q_D(QDeclarativeFlow);
1216 // Flow layout requires relayout if its own size changes too.
1217 d->addItemChangeListener(d, QDeclarativeItemPrivate::Geometry);
1221 \qmlproperty enumeration Flow::flow
1222 This property holds the flow of the layout.
1224 Possible values are:
1227 \o Flow.LeftToRight (default) - Items are positioned next to
1228 to each other according to the \l layoutDirection until the width of the Flow
1229 is exceeded, then wrapped to the next line.
1230 \o Flow.TopToBottom - Items are positioned next to each
1231 other from top to bottom until the height of the Flow is exceeded,
1232 then wrapped to the next column.
1235 QDeclarativeFlow::Flow QDeclarativeFlow::flow() const
1237 Q_D(const QDeclarativeFlow);
1241 void QDeclarativeFlow::setFlow(Flow flow)
1243 Q_D(QDeclarativeFlow);
1244 if (d->flow != flow) {
1252 \qmlproperty enumeration Flow::layoutDirection
1255 This property holds the layout direction of the layout.
1257 Possible values are:
1260 \o Qt.LeftToRight (default) - Items are positioned from the top to bottom,
1261 and left to right. The flow direction is dependent on the
1262 \l Flow::flow property.
1263 \o Qt.RightToLeft - Items are positioned from the top to bottom,
1264 and right to left. The flow direction is dependent on the
1265 \l Flow::flow property.
1268 \sa Grid::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example}
1271 Qt::LayoutDirection QDeclarativeFlow::layoutDirection() const
1273 Q_D(const QDeclarativeFlow);
1274 return d->layoutDirection;
1277 void QDeclarativeFlow::setLayoutDirection(Qt::LayoutDirection layoutDirection)
1279 Q_D(QDeclarativeFlow);
1280 if (d->layoutDirection != layoutDirection) {
1281 d->layoutDirection = layoutDirection;
1283 emit layoutDirectionChanged();
1284 emit effectiveLayoutDirectionChanged();
1289 \qmlproperty enumeration Flow::effectiveLayoutDirection
1290 This property holds the effective layout direction of the flow positioner.
1292 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1293 the visual layout direction of the grid positioner will be mirrored. However, the
1294 property \l {Flow::layoutDirection}{layoutDirection} will remain unchanged.
1296 \sa Flow::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1299 Qt::LayoutDirection QDeclarativeFlow::effectiveLayoutDirection() const
1301 return QDeclarativeBasePositionerPrivate::getEffectiveLayoutDirection(this);
1304 void QDeclarativeFlow::doPositioning(QSizeF *contentSize)
1306 Q_D(QDeclarativeFlow);
1311 QList<int> hoffsets;
1313 for (int i = 0; i < positionedItems.count(); ++i) {
1314 const PositionedItem &child = positionedItems.at(i);
1315 if (!child.item || !child.isVisible)
1318 QGraphicsItemPrivate *childPrivate = QGraphicsItemPrivate::get(child.item);
1319 if (d->flow == LeftToRight) {
1320 if (widthValid() && hoffset && hoffset + childPrivate->width() > width()) {
1322 voffset += linemax + spacing();
1326 if (heightValid() && voffset && voffset + childPrivate->height() > height()) {
1328 hoffset += linemax + spacing();
1333 if(d->isLeftToRight()){
1334 if(child.item->x() != hoffset)
1335 positionX(hoffset, child);
1337 hoffsets << hoffset;
1339 if(child.item->y() != voffset)
1340 positionY(voffset, child);
1342 contentSize->setWidth(qMax(contentSize->width(), hoffset + childPrivate->width()));
1343 contentSize->setHeight(qMax(contentSize->height(), voffset + childPrivate->height()));
1345 if (d->flow == LeftToRight) {
1346 hoffset += childPrivate->width();
1347 hoffset += spacing();
1348 linemax = qMax(linemax, qCeil(childPrivate->height()));
1350 voffset += childPrivate->height();
1351 voffset += spacing();
1352 linemax = qMax(linemax, qCeil(childPrivate->width()));
1356 if(d->isLeftToRight())
1363 end = contentSize->width();
1365 for (int i = 0; i < positionedItems.count(); ++i) {
1366 const PositionedItem &child = positionedItems.at(i);
1367 if (!child.item || !child.isVisible)
1369 hoffset = end - hoffsets[acc++] - QGraphicsItemPrivate::get(child.item)->width();
1370 if(child.item->x() != hoffset)
1371 positionX(hoffset, child);
1375 void QDeclarativeFlow::reportConflictingAnchors()
1377 Q_D(QDeclarativeFlow);
1378 for (int ii = 0; ii < positionedItems.count(); ++ii) {
1379 const PositionedItem &child = positionedItems.at(ii);
1380 if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) {
1381 QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(static_cast<QDeclarativeItem *>(child.item))->_anchors;
1382 if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) {
1383 d->anchorConflict = true;
1388 if (d->anchorConflict)
1389 qmlInfo(this) << "Cannot specify anchors for items inside Flow";