1 /****************************************************************************
3 ** Copyright (C) 2012 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 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickpositioners_p.h"
43 #include "qquickpositioners_p_p.h"
45 #include <QtDeclarative/qdeclarative.h>
46 #include <QtDeclarative/qdeclarativeinfo.h>
47 #include <QtCore/qmath.h>
48 #include <QtCore/qcoreapplication.h>
50 #include <QtQuick/private/qdeclarativestate_p.h>
51 #include <QtQuick/private/qdeclarativestategroup_p.h>
52 #include <private/qdeclarativestateoperations_p.h>
53 #include <QtQuick/private/qdeclarativetransition_p.h>
57 static const QQuickItemPrivate::ChangeTypes watchedChanges
58 = QQuickItemPrivate::Geometry
59 | QQuickItemPrivate::SiblingOrder
60 | QQuickItemPrivate::Visibility
61 | QQuickItemPrivate::Destroyed;
63 void QQuickBasePositionerPrivate::watchChanges(QQuickItem *other)
65 QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(other);
66 otherPrivate->addItemChangeListener(this, watchedChanges);
69 void QQuickBasePositionerPrivate::unwatchChanges(QQuickItem* other)
71 QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(other);
72 otherPrivate->removeItemChangeListener(this, watchedChanges);
75 QQuickBasePositioner::QQuickBasePositioner(PositionerType at, QQuickItem *parent)
76 : QQuickImplicitSizeItem(*(new QQuickBasePositionerPrivate), parent)
78 Q_D(QQuickBasePositioner);
83 \class QQuickBasePositioner
84 \brief The QQuickBasePositioner class provides a base for QQuickGraphics layouts.
86 To create a QQuickGraphics Positioner, simply subclass QQuickBasePositioner and implement
87 doLayout(), which is automatically called when the layout might need
88 updating. In doLayout() use the setX and setY functions from QQuickBasePositioner, and the
89 base class will apply the positions along with the appropriate transitions. The items to
90 position are provided in order as the protected member positionedItems.
92 You also need to set a PositionerType, to declare whether you are positioning the x, y or both
93 for the child items. Depending on the chosen type, only x or y changes will be applied.
95 Note that the subclass is responsible for adding the spacing in between items.
97 Positioning is usually delayed until before a frame is rendered, to batch multiple repositioning
98 changes into one calculation.
101 QQuickBasePositioner::QQuickBasePositioner(QQuickBasePositionerPrivate &dd, PositionerType at, QQuickItem *parent)
102 : QQuickImplicitSizeItem(dd, parent)
104 Q_D(QQuickBasePositioner);
108 QQuickBasePositioner::~QQuickBasePositioner()
110 Q_D(QQuickBasePositioner);
111 for (int i = 0; i < positionedItems.count(); ++i)
112 d->unwatchChanges(positionedItems.at(i).item);
113 positionedItems.clear();
116 void QQuickBasePositioner::updatePolish()
118 Q_D(QQuickBasePositioner);
119 if (d->positioningDirty)
123 int QQuickBasePositioner::spacing() const
125 Q_D(const QQuickBasePositioner);
129 void QQuickBasePositioner::setSpacing(int s)
131 Q_D(QQuickBasePositioner);
135 d->setPositioningDirty();
136 emit spacingChanged();
139 QDeclarativeTransition *QQuickBasePositioner::move() const
141 Q_D(const QQuickBasePositioner);
142 return d->moveTransition;
145 void QQuickBasePositioner::setMove(QDeclarativeTransition *mt)
147 Q_D(QQuickBasePositioner);
148 if (mt == d->moveTransition)
150 d->moveTransition = mt;
154 QDeclarativeTransition *QQuickBasePositioner::add() const
156 Q_D(const QQuickBasePositioner);
157 return d->addTransition;
160 void QQuickBasePositioner::setAdd(QDeclarativeTransition *add)
162 Q_D(QQuickBasePositioner);
163 if (add == d->addTransition)
166 d->addTransition = add;
170 void QQuickBasePositioner::componentComplete()
172 QQuickItem::componentComplete();
173 positionedItems.reserve(childItems().count());
177 void QQuickBasePositioner::itemChange(ItemChange change, const ItemChangeData &value)
179 Q_D(QQuickBasePositioner);
180 if (change == ItemChildAddedChange){
181 d->setPositioningDirty();
182 } else if (change == ItemChildRemovedChange) {
183 QQuickItem *child = value.item;
184 QQuickBasePositioner::PositionedItem posItem(child);
185 int idx = positionedItems.find(posItem);
187 d->unwatchChanges(child);
188 positionedItems.remove(idx);
190 d->setPositioningDirty();
193 QQuickItem::itemChange(change, value);
196 void QQuickBasePositioner::prePositioning()
198 Q_D(QQuickBasePositioner);
199 if (!isComponentComplete())
202 if (d->doingPositioning)
205 d->positioningDirty = false;
206 d->doingPositioning = true;
207 //Need to order children by creation order modified by stacking order
208 QList<QQuickItem *> children = childItems();
210 QPODVector<PositionedItem,8> oldItems;
211 positionedItems.copyAndClear(oldItems);
212 for (int ii = 0; ii < children.count(); ++ii) {
213 QQuickItem *child = children.at(ii);
214 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
215 PositionedItem *item = 0;
216 PositionedItem posItem(child);
217 int wIdx = oldItems.find(posItem);
219 d->watchChanges(child);
220 positionedItems.append(posItem);
221 item = &positionedItems[positionedItems.count()-1];
223 if (!childPrivate->explicitVisible || !child->width() || !child->height())
224 item->isVisible = false;
226 item = &oldItems[wIdx];
227 // Items are only omitted from positioning if they are explicitly hidden
228 // i.e. their positioning is not affected if an ancestor is hidden.
229 if (!childPrivate->explicitVisible || !child->width() || !child->height()) {
230 item->isVisible = false;
231 } else if (!item->isVisible) {
232 item->isVisible = true;
237 positionedItems.append(*item);
240 QSizeF contentSize(0,0);
241 reportConflictingAnchors();
242 if (!d->anchorConflict) {
243 doPositioning(&contentSize);
244 updateAttachedProperties();
246 if (!d->addActions.isEmpty() || !d->moveActions.isEmpty())
247 finishApplyTransitions();
248 d->doingPositioning = false;
249 //Set implicit size to the size of its children
250 setImplicitSize(contentSize.width(), contentSize.height());
253 void QQuickBasePositioner::positionX(int x, const PositionedItem &target)
255 Q_D(QQuickBasePositioner);
256 if (d->type == Horizontal || d->type == Both) {
258 if (!d->addTransition || !d->addTransition->enabled())
259 target.item->setX(x);
261 d->addActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x));
262 } else if (x != target.item->x()) {
263 if (!d->moveTransition || !d->moveTransition->enabled())
264 target.item->setX(x);
266 d->moveActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x));
271 void QQuickBasePositioner::positionY(int y, const PositionedItem &target)
273 Q_D(QQuickBasePositioner);
274 if (d->type == Vertical || d->type == Both) {
276 if (!d->addTransition || !d->addTransition->enabled())
277 target.item->setY(y);
279 d->addActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y));
280 } else if (y != target.item->y()) {
281 if (!d->moveTransition || !d->moveTransition->enabled())
282 target.item->setY(y);
284 d->moveActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y));
289 void QQuickBasePositioner::finishApplyTransitions()
291 Q_D(QQuickBasePositioner);
292 // Note that if a transition is not set the transition manager will
293 // apply the changes directly, in the case add/move aren't set
294 d->addTransitionManager.transition(d->addActions, d->addTransition);
295 d->moveTransitionManager.transition(d->moveActions, d->moveTransition);
296 d->addActions.clear();
297 d->moveActions.clear();
300 QQuickPositionerAttached *QQuickBasePositioner::qmlAttachedProperties(QObject *obj)
302 return new QQuickPositionerAttached(obj);
305 void QQuickBasePositioner::updateAttachedProperties(QQuickPositionerAttached *specificProperty, QQuickItem *specificPropertyOwner) const
307 // If this function is deemed too expensive or shows up in profiles, it could
308 // be changed to run only when there are attached properties present. This
309 // could be a flag in the positioner that is set by the attached property
311 QQuickPositionerAttached *prevLastProperty = 0;
312 QQuickPositionerAttached *lastProperty = 0;
314 int visibleItemIndex = 0;
315 for (int ii = 0; ii < positionedItems.count(); ++ii) {
316 const PositionedItem &child = positionedItems.at(ii);
320 QQuickPositionerAttached *property = 0;
322 if (specificProperty) {
323 if (specificPropertyOwner == child.item) {
324 property = specificProperty;
327 property = static_cast<QQuickPositionerAttached *>(qmlAttachedPropertiesObject<QQuickBasePositioner>(child.item, false));
330 if (child.isVisible) {
332 property->setIndex(visibleItemIndex);
333 property->setIsFirstItem(visibleItemIndex == 0);
335 if (property->isLastItem())
336 prevLastProperty = property;
339 lastProperty = property;
341 } else if (property) {
342 property->setIndex(-1);
343 property->setIsFirstItem(false);
344 property->setIsLastItem(false);
348 if (prevLastProperty && prevLastProperty != lastProperty)
349 prevLastProperty->setIsLastItem(false);
351 lastProperty->setIsLastItem(true);
355 \qmlclass Positioner QQuickPositionerAttached
356 \inqmlmodule QtQuick 2
357 \ingroup qml-positioning-elements
358 \brief The Positioner type provides attached properties that contain details on where an item exists in a positioner.
360 Positioner items (such as Column, Row, Flow and Grid) provide automatic layout
361 for child items. Attaching this property allows a child item to determine
362 where it exists within the positioner.
365 QQuickPositionerAttached::QQuickPositionerAttached(QObject *parent) : QObject(parent), m_index(-1), m_isFirstItem(false), m_isLastItem(false)
367 QQuickItem *attachedItem = qobject_cast<QQuickItem *>(parent);
369 QQuickBasePositioner *positioner = qobject_cast<QQuickBasePositioner *>(attachedItem->parent());
371 positioner->updateAttachedProperties(this, attachedItem);
377 \qmlattachedproperty Item QtQuick2::Positioner::index
379 This property allows the item to determine
380 its index within the positioner.
382 void QQuickPositionerAttached::setIndex(int index)
384 if (m_index == index)
391 \qmlattachedproperty Item QtQuick2::Positioner::isFirstItem
392 \qmlattachedproperty Item QtQuick2::Positioner::isLastItem
394 These properties allow the item to determine if it
395 is the first or last item in the positioner, respectively.
397 void QQuickPositionerAttached::setIsFirstItem(bool isFirstItem)
399 if (m_isFirstItem == isFirstItem)
401 m_isFirstItem = isFirstItem;
402 emit isFirstItemChanged();
405 void QQuickPositionerAttached::setIsLastItem(bool isLastItem)
407 if (m_isLastItem == isLastItem)
409 m_isLastItem = isLastItem;
410 emit isLastItemChanged();
414 \qmlclass Column QQuickColumn
415 \inqmlmodule QtQuick 2
416 \ingroup qml-positioning-elements
417 \brief The Column item arranges its children vertically.
420 The Column item positions its child items so that they are vertically
421 aligned and not overlapping.
423 Spacing between items can be added using the \l spacing property.
424 Transitions can be used for cases where items managed by a Column are
425 added or moved. These are stored in the \l add and \l move properties
428 See \l{Using QML Positioner and Repeater Items} for more details about this item and other
431 \section1 Example Usage
433 The following example positions differently shaped rectangles using a Column
436 \image verticalpositioner_example.png
438 \snippet doc/src/snippets/declarative/column/vertical-positioner.qml document
440 \section1 Using Transitions
442 Transitions can be used to animate items that are added to, moved within,
443 or removed from a Column item. The \l add and \l move properties can be set to
444 the transitions that will be applied when items are added to, removed from,
445 or re-positioned within a Column item.
447 The use of transitions with positioners is described in more detail in the
448 \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML
449 Positioner and Repeater Items} document.
451 \image verticalpositioner_transition.gif
457 // Define an animation for adding a new item...
460 // Define an animation for moving items within the column...
466 \section1 Limitations
468 Note that the positioner assumes that the x and y positions of its children
469 will not change. If you manually change the x or y properties in script, bind
470 the x or y properties, use anchors on a child of a positioner, or have the
471 height of a child depend on the position of a child, then the
472 positioner may exhibit strange behavior. If you need to perform any of these
473 actions, consider positioning the items without the use of a Column.
475 Items with a width or height of 0 will not be positioned.
477 Positioning is batched and syncronized with painting to reduce the number of
478 calculations needed. This means that positioners may not reposition items immediately
479 when changes occur, but it will have moved by the next frame.
481 \sa Row, Grid, Flow, Positioner, {declarative/positioners}{Positioners example}
484 \qmlproperty Transition QtQuick2::Column::add
486 This property holds the transition to be applied when adding an
487 item to the positioner. The transition will only be applied to the
488 added item(s). Positioner transitions will only affect the
489 position (x, y) of items.
491 For a positioner, adding an item can mean that either the object
492 has been created or reparented, and thus is now a child or the
493 positioner, or that the object has had its opacity increased from
494 zero, and thus is now visible.
499 \qmlproperty Transition QtQuick2::Column::move
501 This property holds the transition to apply when moving an item
502 within the positioner. Positioner transitions will only affect
503 the position (x, y) of items.
505 This transition can be performed when other items are added or removed
506 from the positioner, or when items resize themselves.
508 \image positioner-move.gif
521 \sa add, {declarative/positioners}{Positioners example}
524 \qmlproperty int QtQuick2::Column::spacing
526 The spacing is the amount in pixels left empty between adjacent
527 items. The default spacing is 0.
531 QQuickColumn::QQuickColumn(QQuickItem *parent)
532 : QQuickBasePositioner(Vertical, parent)
536 void QQuickColumn::doPositioning(QSizeF *contentSize)
540 for (int ii = 0; ii < positionedItems.count(); ++ii) {
541 const PositionedItem &child = positionedItems.at(ii);
542 if (!child.item || !child.isVisible)
545 if (child.item->y() != voffset)
546 positionY(voffset, child);
548 contentSize->setWidth(qMax(contentSize->width(), child.item->width()));
550 voffset += child.item->height();
551 voffset += spacing();
554 if (voffset != 0)//If we positioned any items, undo the spacing from the last item
555 voffset -= spacing();
556 contentSize->setHeight(voffset);
559 void QQuickColumn::reportConflictingAnchors()
561 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate*>(QQuickBasePositionerPrivate::get(this));
562 for (int ii = 0; ii < positionedItems.count(); ++ii) {
563 const PositionedItem &child = positionedItems.at(ii);
565 QQuickAnchors *anchors = QQuickItemPrivate::get(static_cast<QQuickItem *>(child.item))->_anchors;
567 QQuickAnchors::Anchors usedAnchors = anchors->usedAnchors();
568 if (usedAnchors & QQuickAnchors::TopAnchor ||
569 usedAnchors & QQuickAnchors::BottomAnchor ||
570 usedAnchors & QQuickAnchors::VCenterAnchor ||
571 anchors->fill() || anchors->centerIn()) {
572 d->anchorConflict = true;
578 if (d->anchorConflict) {
579 qmlInfo(this) << "Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column."
580 << " Column will not function.";
584 \qmlclass Row QQuickRow
585 \inqmlmodule QtQuick 2
586 \ingroup qml-positioning-elements
587 \brief The Row item arranges its children horizontally.
590 The Row item positions its child items so that they are horizontally
591 aligned and not overlapping.
593 Use \l spacing to set the spacing between items in a Row, and use the
594 \l add and \l move properties to set the transitions that should be applied
595 when items are added to, removed from, or re-positioned within the Row.
597 See \l{Using QML Positioner and Repeater Items} for more details about this item and other
600 \section1 Example Usage
602 The following example lays out differently shaped rectangles using a Row.
604 \image horizontalpositioner_example.png
606 \snippet doc/src/snippets/declarative/row/row.qml document
608 \section1 Using Transitions
610 Transitions can be used to animate items that are added to, moved within,
611 or removed from a Grid item. The \l add and \l move properties can be set to
612 the transitions that will be applied when items are added to, removed from,
613 or re-positioned within a Row item.
615 \section1 Limitations
617 Note that the positioner assumes that the x and y positions of its children
618 will not change. If you manually change the x or y properties in script, bind
619 the x or y properties, use anchors on a child of a positioner, or have the
620 width of a child depend on the position of a child, then the
621 positioner may exhibit strange behavior. If you need to perform any of these
622 actions, consider positioning the items without the use of a Row.
624 Items with a width or height of 0 will not be positioned.
626 Positioning is batched and syncronized with painting to reduce the number of
627 calculations needed. This means that positioners may not reposition items immediately
628 when changes occur, but it will have moved by the next frame.
630 \sa Column, Grid, Flow, Positioner, {declarative/positioners}{Positioners example}
633 \qmlproperty Transition QtQuick2::Row::add
635 This property holds the transition to be applied when adding an
636 item to the positioner. The transition will only be applied to the
637 added item(s). Positioner transitions will only affect the
638 position (x, y) of items.
640 For a positioner, adding an item can mean that either the object
641 has been created or reparented, and thus is now a child or the
642 positioner, or that the object has had its opacity increased from
643 zero, and thus is now visible.
648 \qmlproperty Transition QtQuick2::Row::move
650 This property holds the transition to be applied when moving an
651 item within the positioner. Positioner transitions will only affect
652 the position (x, y) of items.
654 This transition can be performed when other items are added or removed
655 from the positioner, or when items resize themselves.
669 \sa add, {declarative/positioners}{Positioners example}
672 \qmlproperty int QtQuick2::Row::spacing
674 The spacing is the amount in pixels left empty between adjacent
675 items. The default spacing is 0.
680 QQuickRow::QQuickRow(QQuickItem *parent)
681 : QQuickBasePositioner(Horizontal, parent)
685 \qmlproperty enumeration QtQuick2::Row::layoutDirection
687 This property holds the layoutDirection of the row.
692 \o Qt.LeftToRight (default) - Items are laid out from left to right. If the width of the row is explicitly set,
693 the left anchor remains to the left of the row.
694 \o Qt.RightToLeft - Items are laid out from right to left. If the width of the row is explicitly set,
695 the right anchor remains to the right of the row.
698 \sa Grid::layoutDirection, Flow::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example}
701 Qt::LayoutDirection QQuickRow::layoutDirection() const
703 return QQuickBasePositionerPrivate::getLayoutDirection(this);
706 void QQuickRow::setLayoutDirection(Qt::LayoutDirection layoutDirection)
708 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate* >(QQuickBasePositionerPrivate::get(this));
709 if (d->layoutDirection != layoutDirection) {
710 d->layoutDirection = layoutDirection;
711 // For RTL layout the positioning changes when the width changes.
712 if (d->layoutDirection == Qt::RightToLeft)
713 d->addItemChangeListener(d, QQuickItemPrivate::Geometry);
715 d->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
717 emit layoutDirectionChanged();
718 emit effectiveLayoutDirectionChanged();
722 \qmlproperty enumeration QtQuick2::Row::effectiveLayoutDirection
723 This property holds the effective layout direction of the row positioner.
725 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
726 the visual layout direction of the row positioner will be mirrored. However, the
727 property \l {Row::layoutDirection}{layoutDirection} will remain unchanged.
729 \sa Row::layoutDirection, {LayoutMirroring}{LayoutMirroring}
732 Qt::LayoutDirection QQuickRow::effectiveLayoutDirection() const
734 return QQuickBasePositionerPrivate::getEffectiveLayoutDirection(this);
737 void QQuickRow::doPositioning(QSizeF *contentSize)
739 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate* >(QQuickBasePositionerPrivate::get(this));
743 for (int ii = 0; ii < positionedItems.count(); ++ii) {
744 const PositionedItem &child = positionedItems.at(ii);
745 if (!child.item || !child.isVisible)
748 if (d->isLeftToRight()) {
749 if (child.item->x() != hoffset)
750 positionX(hoffset, child);
755 contentSize->setHeight(qMax(contentSize->height(), child.item->height()));
757 hoffset += child.item->width();
758 hoffset += spacing();
761 if (hoffset != 0)//If we positioned any items, undo the extra spacing from the last item
762 hoffset -= spacing();
763 contentSize->setWidth(hoffset);
765 if (d->isLeftToRight())
768 //Right to Left layout
771 end = contentSize->width();
776 for (int ii = 0; ii < positionedItems.count(); ++ii) {
777 const PositionedItem &child = positionedItems.at(ii);
778 if (!child.item || !child.isVisible)
780 hoffset = end - hoffsets[acc++] - child.item->width();
781 if (child.item->x() != hoffset)
782 positionX(hoffset, child);
786 void QQuickRow::reportConflictingAnchors()
788 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate*>(QQuickBasePositionerPrivate::get(this));
789 for (int ii = 0; ii < positionedItems.count(); ++ii) {
790 const PositionedItem &child = positionedItems.at(ii);
792 QQuickAnchors *anchors = QQuickItemPrivate::get(static_cast<QQuickItem *>(child.item))->_anchors;
794 QQuickAnchors::Anchors usedAnchors = anchors->usedAnchors();
795 if (usedAnchors & QQuickAnchors::LeftAnchor ||
796 usedAnchors & QQuickAnchors::RightAnchor ||
797 usedAnchors & QQuickAnchors::HCenterAnchor ||
798 anchors->fill() || anchors->centerIn()) {
799 d->anchorConflict = true;
805 if (d->anchorConflict)
806 qmlInfo(this) << "Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row."
807 << " Row will not function.";
811 \qmlclass Grid QQuickGrid
812 \inqmlmodule QtQuick 2
813 \ingroup qml-positioning-elements
814 \brief The Grid item positions its children in a grid.
817 The Grid item positions its child items so that they are
818 aligned in a grid and are not overlapping.
820 The grid positioner calculates a grid of rectangular cells of sufficient
821 size to hold all items, placing the items in the cells, from left to right
822 and top to bottom. Each item is positioned in the top-left corner of its
823 cell with position (0, 0).
825 A Grid defaults to four columns, and as many rows as are necessary to
826 fit all child items. The number of rows and columns can be constrained
827 by setting the \l rows and \l columns properties.
829 Spacing can be added between child items by setting the \l spacing
830 property. The amount of spacing applied will be the same in the
831 horizontal and vertical directions.
833 See \l{Using QML Positioner and Repeater Items} for more details about this item and other
836 \section1 Example Usage
838 The following example demonstrates this.
840 \image gridLayout_example.png
842 \snippet doc/src/snippets/declarative/grid/grid.qml document
844 \section1 Using Transitions
846 Transitions can be used to animate items that are added to, moved within,
847 or removed from a Grid item. The \l add and \l move properties can be set to
848 the transitions that will be applied when items are added to, removed from,
849 or re-positioned within a Grid item.
851 \section1 Limitations
853 Note that the positioner assumes that the x and y positions of its children
854 will not change. If you manually change the x or y properties in script, bind
855 the x or y properties, use anchors on a child of a positioner, or have the
856 width or height of a child depend on the position of a child, then the
857 positioner may exhibit strange behavior. If you need to perform any of these
858 actions, consider positioning the items without the use of a Grid.
860 Items with a width or height of 0 will not be positioned.
862 Positioning is batched and syncronized with painting to reduce the number of
863 calculations needed. This means that positioners may not reposition items immediately
864 when changes occur, but it will have moved by the next frame.
866 \sa Flow, Row, Column, Positioner, {declarative/positioners}{Positioners example}
869 \qmlproperty Transition QtQuick2::Grid::add
871 This property holds the transition to be applied when adding an
872 item to the positioner. The transition will only be applied to the
873 added item(s). Positioner transitions will only affect the
874 position (x, y) of items.
876 For a positioner, adding an item can mean that either the object
877 has been created or reparented, and thus is now a child or the
878 positioner, or that the object has had its opacity increased from
879 zero, and thus is now visible.
884 \qmlproperty Transition QtQuick2::Grid::move
886 This property holds the transition to be applied when moving an
887 item within the positioner. Positioner transitions will only affect
888 the position (x, y) of items.
890 This transition can be performed when other items are added or removed
891 from the positioner, or when items resize themselves.
904 \sa add, {declarative/positioners}{Positioners example}
907 \qmlproperty int QtQuick2::Grid::spacing
909 The spacing is the amount in pixels left empty between adjacent
910 items. The default spacing is 0.
912 The below example places a Grid containing a red, a blue and a
913 green rectangle on a gray background. The area the grid positioner
914 occupies is colored white. The positioner on the left has the
915 no spacing (the default), and the positioner on the right has
918 \inlineimage qml-grid-no-spacing.png
919 \inlineimage qml-grid-spacing.png
923 QQuickGrid::QQuickGrid(QQuickItem *parent) :
924 QQuickBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_rowSpacing(-1), m_columnSpacing(-1), m_flow(LeftToRight)
929 \qmlproperty int QtQuick2::Grid::columns
931 This property holds the number of columns in the grid. The default
932 number of columns is 4.
934 If the grid does not have enough items to fill the specified
935 number of columns, some columns will be of zero width.
939 \qmlproperty int QtQuick2::Grid::rows
940 This property holds the number of rows in the grid.
942 If the grid does not have enough items to fill the specified
943 number of rows, some rows will be of zero width.
946 void QQuickGrid::setColumns(const int columns)
948 if (columns == m_columns)
952 emit columnsChanged();
955 void QQuickGrid::setRows(const int rows)
965 \qmlproperty enumeration QtQuick2::Grid::flow
966 This property holds the flow of the layout.
971 \o Grid.LeftToRight (default) - Items are positioned next to
972 each other in the \l layoutDirection, then wrapped to the next line.
973 \o Grid.TopToBottom - Items are positioned next to each
974 other from top to bottom, then wrapped to the next column.
977 QQuickGrid::Flow QQuickGrid::flow() const
982 void QQuickGrid::setFlow(Flow flow)
984 if (m_flow != flow) {
992 \qmlproperty int QtQuick2::Grid::rowSpacing
994 This property holds the spacing in pixels between rows.
999 void QQuickGrid::setRowSpacing(const int rowSpacing)
1001 if (rowSpacing == m_rowSpacing)
1003 m_rowSpacing = rowSpacing;
1005 emit rowSpacingChanged();
1009 \qmlproperty int QtQuick2::Grid::columnSpacing
1011 This property holds the spacing in pixels between columns.
1016 void QQuickGrid::setColumnSpacing(const int columnSpacing)
1018 if (columnSpacing == m_columnSpacing)
1020 m_columnSpacing = columnSpacing;
1022 emit columnSpacingChanged();
1026 \qmlproperty enumeration QtQuick2::Grid::layoutDirection
1028 This property holds the layout direction of the layout.
1030 Possible values are:
1033 \o Qt.LeftToRight (default) - Items are positioned from the top to bottom,
1034 and left to right. The flow direction is dependent on the
1035 \l Grid::flow property.
1036 \o Qt.RightToLeft - Items are positioned from the top to bottom,
1037 and right to left. The flow direction is dependent on the
1038 \l Grid::flow property.
1041 \sa Flow::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example}
1043 Qt::LayoutDirection QQuickGrid::layoutDirection() const
1045 return QQuickBasePositionerPrivate::getLayoutDirection(this);
1048 void QQuickGrid::setLayoutDirection(Qt::LayoutDirection layoutDirection)
1050 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate*>(QQuickBasePositionerPrivate::get(this));
1051 if (d->layoutDirection != layoutDirection) {
1052 d->layoutDirection = layoutDirection;
1053 // For RTL layout the positioning changes when the width changes.
1054 if (d->layoutDirection == Qt::RightToLeft)
1055 d->addItemChangeListener(d, QQuickItemPrivate::Geometry);
1057 d->removeItemChangeListener(d, QQuickItemPrivate::Geometry);
1059 emit layoutDirectionChanged();
1060 emit effectiveLayoutDirectionChanged();
1065 \qmlproperty enumeration QtQuick2::Grid::effectiveLayoutDirection
1066 This property holds the effective layout direction of the grid positioner.
1068 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1069 the visual layout direction of the grid positioner will be mirrored. However, the
1070 property \l {Grid::layoutDirection}{layoutDirection} will remain unchanged.
1072 \sa Grid::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1074 Qt::LayoutDirection QQuickGrid::effectiveLayoutDirection() const
1076 return QQuickBasePositionerPrivate::getEffectiveLayoutDirection(this);
1079 void QQuickGrid::doPositioning(QSizeF *contentSize)
1081 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate*>(QQuickBasePositionerPrivate::get(this));
1084 //Is allocating the extra QPODVector too much overhead?
1085 QPODVector<PositionedItem, 8> visibleItems;//we aren't concerned with invisible items
1086 visibleItems.reserve(positionedItems.count());
1087 for (int i=0; i<positionedItems.count(); i++)
1088 if (positionedItems[i].item && positionedItems[i].isVisible)
1089 visibleItems.append(positionedItems[i]);
1091 int numVisible = visibleItems.count();
1092 if (m_columns <= 0 && m_rows <= 0){
1094 r = (numVisible+3)/4;
1095 } else if (m_rows <= 0){
1096 r = (numVisible+(m_columns-1))/m_columns;
1097 } else if (m_columns <= 0){
1098 c = (numVisible+(m_rows-1))/m_rows;
1102 return; //Nothing to do
1104 QList<int> maxColWidth;
1105 QList<int> maxRowHeight;
1107 if (m_flow == LeftToRight) {
1108 for (int i=0; i < r; i++){
1109 for (int j=0; j < c; j++){
1115 if (childIndex == visibleItems.count())
1118 const PositionedItem &child = visibleItems.at(childIndex++);
1119 if (child.item->width() > maxColWidth[j])
1120 maxColWidth[j] = child.item->width();
1121 if (child.item->height() > maxRowHeight[i])
1122 maxRowHeight[i] = child.item->height();
1126 for (int j=0; j < c; j++){
1127 for (int i=0; i < r; i++){
1133 if (childIndex == visibleItems.count())
1136 const PositionedItem &child = visibleItems.at(childIndex++);
1137 if (child.item->width() > maxColWidth[j])
1138 maxColWidth[j] = child.item->width();
1139 if (child.item->height() > maxRowHeight[i])
1140 maxRowHeight[i] = child.item->height();
1145 int columnSpacing = m_columnSpacing;
1146 if (columnSpacing == -1)
1147 columnSpacing = spacing();
1149 int rowSpacing = m_rowSpacing;
1150 if (rowSpacing == -1)
1151 rowSpacing = spacing();
1154 for (int j=0; j < maxColWidth.size(); j++){
1156 widthSum += columnSpacing;
1157 widthSum += maxColWidth[j];
1161 for (int i=0; i < maxRowHeight.size(); i++){
1163 heightSum += rowSpacing;
1164 heightSum += maxRowHeight[i];
1167 contentSize->setHeight(heightSum);
1168 contentSize->setWidth(widthSum);
1177 if (!d->isLeftToRight())
1182 for (int i = 0; i < visibleItems.count(); ++i) {
1183 const PositionedItem &child = visibleItems.at(i);
1184 int childXOffset = xoffset;
1185 if (!d->isLeftToRight())
1186 childXOffset -= child.item->width();
1187 if ((child.item->x() != childXOffset) || (child.item->y() != yoffset)){
1188 positionX(childXOffset, child);
1189 positionY(yoffset, child);
1192 if (m_flow == LeftToRight) {
1193 if (d->isLeftToRight())
1194 xoffset += maxColWidth[curCol]+columnSpacing;
1196 xoffset -= maxColWidth[curCol]+columnSpacing;
1200 yoffset += maxRowHeight[curRow]+rowSpacing;
1201 if (d->isLeftToRight())
1210 yoffset+=maxRowHeight[curRow]+rowSpacing;
1214 if (d->isLeftToRight())
1215 xoffset += maxColWidth[curCol]+columnSpacing;
1217 xoffset -= maxColWidth[curCol]+columnSpacing;
1227 void QQuickGrid::reportConflictingAnchors()
1229 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate*>(QQuickBasePositionerPrivate::get(this));
1230 for (int ii = 0; ii < positionedItems.count(); ++ii) {
1231 const PositionedItem &child = positionedItems.at(ii);
1233 QQuickAnchors *anchors = QQuickItemPrivate::get(static_cast<QQuickItem *>(child.item))->_anchors;
1234 if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) {
1235 d->anchorConflict = true;
1240 if (d->anchorConflict)
1241 qmlInfo(this) << "Cannot specify anchors for items inside Grid." << " Grid will not function.";
1245 \qmlclass Flow QQuickFlow
1246 \inqmlmodule QtQuick 2
1247 \ingroup qml-positioning-elements
1248 \brief The Flow item arranges its children side by side, wrapping as necessary.
1251 The Flow item positions its child items like words on a page, wrapping them
1252 to create rows or columns of items that do not overlap.
1254 Spacing between items can be added using the \l spacing property.
1255 Transitions can be used for cases where items managed by a Column are
1256 added or moved. These are stored in the \l add and \l move properties
1259 See \l{Using QML Positioner and Repeater Items} for more details about this item and other
1262 \section1 Example Usage
1264 The following example positions \l Text items within a parent item using
1267 \image qml-flow-snippet.png
1269 \snippet doc/src/snippets/declarative/flow.qml flow item
1271 \section1 Using Transitions
1273 Transitions can be used to animate items that are added to, moved within,
1274 or removed from a Flow item. The \l add and \l move properties can be set to
1275 the transitions that will be applied when items are added to, removed from,
1276 or re-positioned within a Flow item.
1278 The use of transitions with positioners is described in more detail in the
1279 \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML
1280 Positioner and Repeater Items} document.
1282 \section1 Limitations
1284 Note that the positioner assumes that the x and y positions of its children
1285 will not change. If you manually change the x or y properties in script, bind
1286 the x or y properties, use anchors on a child of a positioner, or have the
1287 width or height of a child depend on the position of a child, then the
1288 positioner may exhibit strange behavior. If you need to perform any of these
1289 actions, consider positioning the items without the use of a Flow.
1291 Items with a width or height of 0 will not be positioned.
1293 Positioning is batched and syncronized with painting to reduce the number of
1294 calculations needed. This means that positioners may not reposition items immediately
1295 when changes occur, but it will have moved by the next frame.
1297 \sa Column, Row, Grid, Positioner, {declarative/positioners}{Positioners example}
1300 \qmlproperty Transition QtQuick2::Flow::add
1302 This property holds the transition to be applied when adding an
1303 item to the positioner. The transition will only be applied to the
1304 added item(s). Positioner transitions will only affect the
1305 position (x, y) of items.
1307 For a positioner, adding an item can mean that either the object
1308 has been created or reparented, and thus is now a child or the
1309 positioner, or that the object has had its opacity increased from
1310 zero, and thus is now visible.
1315 \qmlproperty Transition QtQuick2::Flow::move
1317 This property holds the transition to be applied when moving an
1318 item within the positioner. Positioner transitions will only affect
1319 the position (x, y) of items.
1321 This transition can be performed when other items are added or removed
1322 from the positioner, or when items resize themselves.
1330 ease: "easeOutBounce"
1336 \sa add, {declarative/positioners}{Positioners example}
1339 \qmlproperty int QtQuick2::Flow::spacing
1341 spacing is the amount in pixels left empty between each adjacent
1342 item, and defaults to 0.
1347 class QQuickFlowPrivate : public QQuickBasePositionerPrivate
1349 Q_DECLARE_PUBLIC(QQuickFlow)
1353 : QQuickBasePositionerPrivate(), flow(QQuickFlow::LeftToRight)
1356 QQuickFlow::Flow flow;
1359 QQuickFlow::QQuickFlow(QQuickItem *parent)
1360 : QQuickBasePositioner(*(new QQuickFlowPrivate), Both, parent)
1363 // Flow layout requires relayout if its own size changes too.
1364 d->addItemChangeListener(d, QQuickItemPrivate::Geometry);
1368 \qmlproperty enumeration QtQuick2::Flow::flow
1369 This property holds the flow of the layout.
1371 Possible values are:
1374 \o Flow.LeftToRight (default) - Items are positioned next to
1375 to each other according to the \l layoutDirection until the width of the Flow
1376 is exceeded, then wrapped to the next line.
1377 \o Flow.TopToBottom - Items are positioned next to each
1378 other from top to bottom until the height of the Flow is exceeded,
1379 then wrapped to the next column.
1382 QQuickFlow::Flow QQuickFlow::flow() const
1384 Q_D(const QQuickFlow);
1388 void QQuickFlow::setFlow(Flow flow)
1391 if (d->flow != flow) {
1399 \qmlproperty enumeration QtQuick2::Flow::layoutDirection
1401 This property holds the layout direction of the layout.
1403 Possible values are:
1406 \o Qt.LeftToRight (default) - Items are positioned from the top to bottom,
1407 and left to right. The flow direction is dependent on the
1408 \l Flow::flow property.
1409 \o Qt.RightToLeft - Items are positioned from the top to bottom,
1410 and right to left. The flow direction is dependent on the
1411 \l Flow::flow property.
1414 \sa Grid::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example}
1417 Qt::LayoutDirection QQuickFlow::layoutDirection() const
1419 Q_D(const QQuickFlow);
1420 return d->layoutDirection;
1423 void QQuickFlow::setLayoutDirection(Qt::LayoutDirection layoutDirection)
1426 if (d->layoutDirection != layoutDirection) {
1427 d->layoutDirection = layoutDirection;
1429 emit layoutDirectionChanged();
1430 emit effectiveLayoutDirectionChanged();
1435 \qmlproperty enumeration QtQuick2::Flow::effectiveLayoutDirection
1436 This property holds the effective layout direction of the flow positioner.
1438 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1439 the visual layout direction of the grid positioner will be mirrored. However, the
1440 property \l {Flow::layoutDirection}{layoutDirection} will remain unchanged.
1442 \sa Flow::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1445 Qt::LayoutDirection QQuickFlow::effectiveLayoutDirection() const
1447 return QQuickBasePositionerPrivate::getEffectiveLayoutDirection(this);
1450 void QQuickFlow::doPositioning(QSizeF *contentSize)
1457 QList<int> hoffsets;
1459 for (int i = 0; i < positionedItems.count(); ++i) {
1460 const PositionedItem &child = positionedItems.at(i);
1461 if (!child.item || !child.isVisible)
1464 if (d->flow == LeftToRight) {
1465 if (widthValid() && hoffset && hoffset + child.item->width() > width()) {
1467 voffset += linemax + spacing();
1471 if (heightValid() && voffset && voffset + child.item->height() > height()) {
1473 hoffset += linemax + spacing();
1478 if (d->isLeftToRight()) {
1479 if (child.item->x() != hoffset)
1480 positionX(hoffset, child);
1482 hoffsets << hoffset;
1484 if (child.item->y() != voffset)
1485 positionY(voffset, child);
1487 contentSize->setWidth(qMax(contentSize->width(), hoffset + child.item->width()));
1488 contentSize->setHeight(qMax(contentSize->height(), voffset + child.item->height()));
1490 if (d->flow == LeftToRight) {
1491 hoffset += child.item->width();
1492 hoffset += spacing();
1493 linemax = qMax(linemax, qCeil(child.item->height()));
1495 voffset += child.item->height();
1496 voffset += spacing();
1497 linemax = qMax(linemax, qCeil(child.item->width()));
1500 if (d->isLeftToRight())
1507 end = contentSize->width();
1509 for (int i = 0; i < positionedItems.count(); ++i) {
1510 const PositionedItem &child = positionedItems.at(i);
1511 if (!child.item || !child.isVisible)
1513 hoffset = end - hoffsets[acc++] - child.item->width();
1514 if (child.item->x() != hoffset)
1515 positionX(hoffset, child);
1519 void QQuickFlow::reportConflictingAnchors()
1522 for (int ii = 0; ii < positionedItems.count(); ++ii) {
1523 const PositionedItem &child = positionedItems.at(ii);
1525 QQuickAnchors *anchors = QQuickItemPrivate::get(static_cast<QQuickItem *>(child.item))->_anchors;
1526 if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) {
1527 d->anchorConflict = true;
1532 if (d->anchorConflict)
1533 qmlInfo(this) << "Cannot specify anchors for items inside Flow." << " Flow will not function.";