source: trunk/src/declarative/graphicsitems/qdeclarativepositioners.cpp@ 1125

Last change on this file since 1125 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 38.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
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.
23**
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.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativepositioners_p.h"
43#include "private/qdeclarativepositioners_p_p.h"
44
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>
51
52#include <QDebug>
53#include <QCoreApplication>
54
55QT_BEGIN_NAMESPACE
56
57static const QDeclarativeItemPrivate::ChangeTypes watchedChanges
58 = QDeclarativeItemPrivate::Geometry
59 | QDeclarativeItemPrivate::SiblingOrder
60 | QDeclarativeItemPrivate::Visibility
61 | QDeclarativeItemPrivate::Opacity
62 | QDeclarativeItemPrivate::Destroyed;
63
64void QDeclarativeBasePositionerPrivate::watchChanges(QGraphicsObject *other)
65{
66 if (QGraphicsItemPrivate::get(other)->isDeclarativeItem) {
67 QDeclarativeItemPrivate *otherPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(other));
68 otherPrivate->addItemChangeListener(this, watchedChanges);
69 } else {
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()));
75 }
76}
77
78void QDeclarativeBasePositionerPrivate::unwatchChanges(QGraphicsObject* other)
79{
80 if (QGraphicsItemPrivate::get(other)->isDeclarativeItem) {
81 QDeclarativeItemPrivate *otherPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(other));
82 otherPrivate->removeItemChangeListener(this, watchedChanges);
83 } else {
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()));
89 }
90}
91
92void QDeclarativeBasePositioner::graphicsWidgetGeometryChanged()
93{
94 prePositioning();
95}
96
97/*!
98 \internal
99 \class QDeclarativeBasePositioner
100 \brief The QDeclarativeBasePositioner class provides a base for QDeclarativeGraphics layouts.
101
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.
107
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.
110
111 Note that the subclass is responsible for adding the spacing in between items.
112*/
113QDeclarativeBasePositioner::QDeclarativeBasePositioner(PositionerType at, QDeclarativeItem *parent)
114 : QDeclarativeItem(*(new QDeclarativeBasePositionerPrivate), parent)
115{
116 Q_D(QDeclarativeBasePositioner);
117 d->init(at);
118}
119
120QDeclarativeBasePositioner::QDeclarativeBasePositioner(QDeclarativeBasePositionerPrivate &dd, PositionerType at, QDeclarativeItem *parent)
121 : QDeclarativeItem(dd, parent)
122{
123 Q_D(QDeclarativeBasePositioner);
124 d->init(at);
125}
126
127QDeclarativeBasePositioner::~QDeclarativeBasePositioner()
128{
129 Q_D(QDeclarativeBasePositioner);
130 for (int i = 0; i < positionedItems.count(); ++i)
131 d->unwatchChanges(positionedItems.at(i).item);
132 positionedItems.clear();
133}
134
135int QDeclarativeBasePositioner::spacing() const
136{
137 Q_D(const QDeclarativeBasePositioner);
138 return d->spacing;
139}
140
141void QDeclarativeBasePositioner::setSpacing(int s)
142{
143 Q_D(QDeclarativeBasePositioner);
144 if (s==d->spacing)
145 return;
146 d->spacing = s;
147 prePositioning();
148 emit spacingChanged();
149}
150
151QDeclarativeTransition *QDeclarativeBasePositioner::move() const
152{
153 Q_D(const QDeclarativeBasePositioner);
154 return d->moveTransition;
155}
156
157void QDeclarativeBasePositioner::setMove(QDeclarativeTransition *mt)
158{
159 Q_D(QDeclarativeBasePositioner);
160 if (mt == d->moveTransition)
161 return;
162 d->moveTransition = mt;
163 emit moveChanged();
164}
165
166QDeclarativeTransition *QDeclarativeBasePositioner::add() const
167{
168 Q_D(const QDeclarativeBasePositioner);
169 return d->addTransition;
170}
171
172void QDeclarativeBasePositioner::setAdd(QDeclarativeTransition *add)
173{
174 Q_D(QDeclarativeBasePositioner);
175 if (add == d->addTransition)
176 return;
177
178 d->addTransition = add;
179 emit addChanged();
180}
181
182void QDeclarativeBasePositioner::componentComplete()
183{
184 Q_D(QDeclarativeBasePositioner);
185 QDeclarativeItem::componentComplete();
186 positionedItems.reserve(d->QGraphicsItemPrivate::children.count());
187 prePositioning();
188 reportConflictingAnchors();
189}
190
191QVariant QDeclarativeBasePositioner::itemChange(GraphicsItemChange change,
192 const QVariant &value)
193{
194 Q_D(QDeclarativeBasePositioner);
195 if (change == ItemChildAddedChange){
196 QGraphicsItem* item = value.value<QGraphicsItem*>();
197 QGraphicsObject* child = 0;
198 if(item)
199 child = item->toGraphicsObject();
200 if (child)
201 prePositioning();
202 } else if (change == ItemChildRemovedChange) {
203 QGraphicsItem* item = value.value<QGraphicsItem*>();
204 QGraphicsObject* child = 0;
205 if(item)
206 child = item->toGraphicsObject();
207 if (child) {
208 QDeclarativeBasePositioner::PositionedItem posItem(child);
209 int idx = positionedItems.find(posItem);
210 if (idx >= 0) {
211 d->unwatchChanges(child);
212 positionedItems.remove(idx);
213 }
214 prePositioning();
215 }
216 }
217 return QDeclarativeItem::itemChange(change, value);
218}
219
220void QDeclarativeBasePositioner::prePositioning()
221{
222 Q_D(QDeclarativeBasePositioner);
223 if (!isComponentComplete())
224 return;
225
226 if (d->doingPositioning)
227 return;
228
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);
234
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();
239 if (!child)
240 continue;
241 QGraphicsItemPrivate *childPrivate = static_cast<QGraphicsItemPrivate*>(QGraphicsItemPrivate::get(child));
242 PositionedItem *item = 0;
243 PositionedItem posItem(child);
244 int wIdx = oldItems.find(posItem);
245 if (wIdx < 0) {
246 d->watchChanges(child);
247 positionedItems.append(posItem);
248 item = &positionedItems[positionedItems.count()-1];
249 item->isNew = true;
250 if (child->opacity() <= 0.0 || childPrivate->explicitlyHidden || !childPrivate->width() || !childPrivate->height())
251 item->isVisible = false;
252 } else {
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;
260 item->isNew = true;
261 } else {
262 item->isNew = false;
263 }
264 positionedItems.append(*item);
265 }
266 }
267 QSizeF contentSize;
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());
275}
276
277void QDeclarativeBasePositioner::positionX(int x, const PositionedItem &target)
278{
279 Q_D(QDeclarativeBasePositioner);
280 if(d->type == Horizontal || d->type == Both){
281 if (target.isNew) {
282 if (!d->addTransition)
283 target.item->setX(x);
284 else
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);
289 else
290 d->moveActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x));
291 }
292 }
293}
294
295void QDeclarativeBasePositioner::positionY(int y, const PositionedItem &target)
296{
297 Q_D(QDeclarativeBasePositioner);
298 if(d->type == Vertical || d->type == Both){
299 if (target.isNew) {
300 if (!d->addTransition)
301 target.item->setY(y);
302 else
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);
307 else
308 d->moveActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y));
309 }
310 }
311}
312
313void QDeclarativeBasePositioner::finishApplyTransitions()
314{
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();
322}
323
324/*!
325 \qmlclass Column QDeclarativeColumn
326 \ingroup qml-positioning-elements
327 \since 4.7
328 \brief The Column item arranges its children vertically.
329 \inherits Item
330
331 The Column item positions its child items so that they are vertically
332 aligned and not overlapping.
333
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
337 respectively.
338
339 See \l{Using QML Positioner and Repeater Items} for more details about this item and other
340 related items.
341
342 \section1 Example Usage
343
344 The following example positions differently shaped rectangles using a Column
345 item.
346
347 \image verticalpositioner_example.png
348
349 \snippet doc/src/snippets/declarative/column/vertical-positioner.qml document
350
351 \section1 Using Transitions
352
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.
357
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.
361
362 \image verticalpositioner_transition.gif
363
364 \qml
365 Column {
366 spacing: 2
367 add: ...
368 move: ...
369 ...
370 }
371 \endqml
372
373 \section1 Limitations
374
375 Note that the positioner assumes that the x and y positions of its children
376 will not change. If you manually change the x or y properties in script, bind
377 the x or y properties, use anchors on a child of a positioner, or have the
378 height of a child depend on the position of a child, then the
379 positioner may exhibit strange behavior. If you need to perform any of these
380 actions, consider positioning the items without the use of a Column.
381
382 Items with a width or height of 0 will not be positioned.
383
384 \sa Row, Grid, Flow, {declarative/positioners}{Positioners example}
385*/
386/*!
387 \qmlproperty Transition Column::add
388
389 This property holds the transition to be applied when adding an
390 item to the positioner. The transition will only be applied to the
391 added item(s). Positioner transitions will only affect the
392 position (x, y) of items.
393
394 For a positioner, adding an item can mean that either the object
395 has been created or reparented, and thus is now a child or the
396 positioner, or that the object has had its opacity increased from
397 zero, and thus is now visible.
398
399 \sa move
400*/
401/*!
402 \qmlproperty Transition Column::move
403
404 This property holds the transition to apply when moving an item
405 within the positioner. Positioner transitions will only affect
406 the position (x, y) of items.
407
408 This transition can be performed when other items are added or removed
409 from the positioner, or when items resize themselves.
410
411 \image positioner-move.gif
412
413 \qml
414 Column {
415 move: Transition {
416 NumberAnimation {
417 properties: "y"
418 easing.type: Easing.OutBounce
419 }
420 }
421 }
422 \endqml
423
424 \sa add, {declarative/positioners}{Positioners example}
425*/
426/*!
427 \qmlproperty int Column::spacing
428
429 The spacing is the amount in pixels left empty between adjacent
430 items. The default spacing is 0.
431
432 \sa Grid::spacing
433*/
434QDeclarativeColumn::QDeclarativeColumn(QDeclarativeItem *parent)
435: QDeclarativeBasePositioner(Vertical, parent)
436{
437}
438
439void QDeclarativeColumn::doPositioning(QSizeF *contentSize)
440{
441 int voffset = 0;
442
443 for (int ii = 0; ii < positionedItems.count(); ++ii) {
444 const PositionedItem &child = positionedItems.at(ii);
445 if (!child.item || !child.isVisible)
446 continue;
447
448 if(child.item->y() != voffset)
449 positionY(voffset, child);
450
451 contentSize->setWidth(qMax(contentSize->width(), QGraphicsItemPrivate::get(child.item)->width()));
452
453 voffset += QGraphicsItemPrivate::get(child.item)->height();
454 voffset += spacing();
455 }
456
457 contentSize->setHeight(voffset - spacing());
458}
459
460void QDeclarativeColumn::reportConflictingAnchors()
461{
462 QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate*>(QDeclarativeBasePositionerPrivate::get(this));
463 for (int ii = 0; ii < positionedItems.count(); ++ii) {
464 const PositionedItem &child = positionedItems.at(ii);
465 if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) {
466 QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(static_cast<QDeclarativeItem *>(child.item))->_anchors;
467 if (anchors) {
468 QDeclarativeAnchors::Anchors usedAnchors = anchors->usedAnchors();
469 if (usedAnchors & QDeclarativeAnchors::TopAnchor ||
470 usedAnchors & QDeclarativeAnchors::BottomAnchor ||
471 usedAnchors & QDeclarativeAnchors::VCenterAnchor ||
472 anchors->fill() || anchors->centerIn()) {
473 d->anchorConflict = true;
474 break;
475 }
476 }
477 }
478 }
479 if (d->anchorConflict) {
480 qmlInfo(this) << "Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column";
481 }
482}
483
484/*!
485 \qmlclass Row QDeclarativeRow
486 \ingroup qml-positioning-elements
487 \since 4.7
488 \brief The Row item arranges its children horizontally.
489 \inherits Item
490
491 The Row item positions its child items so that they are horizontally
492 aligned and not overlapping.
493
494 Use \l spacing to set the spacing between items in a Row, and use the
495 \l add and \l move properties to set the transitions that should be applied
496 when items are added to, removed from, or re-positioned within the Row.
497
498 See \l{Using QML Positioner and Repeater Items} for more details about this item and other
499 related items.
500
501 \section1 Example Usage
502
503 The following example lays out differently shaped rectangles using a Row.
504
505 \image horizontalpositioner_example.png
506
507 \snippet doc/src/snippets/declarative/row/row.qml document
508
509 \section1 Using Transitions
510
511 Transitions can be used to animate items that are added to, moved within,
512 or removed from a Grid item. The \l add and \l move properties can be set to
513 the transitions that will be applied when items are added to, removed from,
514 or re-positioned within a Row item.
515
516 \section1 Limitations
517
518 Note that the positioner assumes that the x and y positions of its children
519 will not change. If you manually change the x or y properties in script, bind
520 the x or y properties, use anchors on a child of a positioner, or have the
521 width of a child depend on the position of a child, then the
522 positioner may exhibit strange behaviour. If you need to perform any of these
523 actions, consider positioning the items without the use of a Row.
524
525 Items with a width or height of 0 will not be positioned.
526
527 \sa Column, Grid, Flow, {declarative/positioners}{Positioners example}
528*/
529/*!
530 \qmlproperty Transition Row::add
531
532 This property holds the transition to be applied when adding an
533 item to the positioner. The transition will only be applied to the
534 added item(s). Positioner transitions will only affect the
535 position (x, y) of items.
536
537 For a positioner, adding an item can mean that either the object
538 has been created or reparented, and thus is now a child or the
539 positioner, or that the object has had its opacity increased from
540 zero, and thus is now visible.
541
542 \sa move
543*/
544/*!
545 \qmlproperty Transition Row::move
546
547 This property holds the transition to be applied when moving an
548 item within the positioner. Positioner transitions will only affect
549 the position (x, y) of items.
550
551 This transition can be performed when other items are added or removed
552 from the positioner, or when items resize themselves.
553
554 \qml
555 Row {
556 id: positioner
557 move: Transition {
558 NumberAnimation {
559 properties: "x"
560 ease: "easeOutBounce"
561 }
562 }
563 }
564 \endqml
565
566 \sa add, {declarative/positioners}{Positioners example}
567*/
568/*!
569 \qmlproperty int Row::spacing
570
571 The spacing is the amount in pixels left empty between adjacent
572 items. The default spacing is 0.
573
574 \sa Grid::spacing
575*/
576QDeclarativeRow::QDeclarativeRow(QDeclarativeItem *parent)
577: QDeclarativeBasePositioner(Horizontal, parent)
578{
579}
580
581void QDeclarativeRow::doPositioning(QSizeF *contentSize)
582{
583 int hoffset = 0;
584
585 for (int ii = 0; ii < positionedItems.count(); ++ii) {
586 const PositionedItem &child = positionedItems.at(ii);
587 if (!child.item || !child.isVisible)
588 continue;
589
590 if(child.item->x() != hoffset)
591 positionX(hoffset, child);
592
593 contentSize->setHeight(qMax(contentSize->height(), QGraphicsItemPrivate::get(child.item)->height()));
594
595 hoffset += QGraphicsItemPrivate::get(child.item)->width();
596 hoffset += spacing();
597 }
598
599 contentSize->setWidth(hoffset - spacing());
600}
601
602void QDeclarativeRow::reportConflictingAnchors()
603{
604 QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate*>(QDeclarativeBasePositionerPrivate::get(this));
605 for (int ii = 0; ii < positionedItems.count(); ++ii) {
606 const PositionedItem &child = positionedItems.at(ii);
607 if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) {
608 QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(static_cast<QDeclarativeItem *>(child.item))->_anchors;
609 if (anchors) {
610 QDeclarativeAnchors::Anchors usedAnchors = anchors->usedAnchors();
611 if (usedAnchors & QDeclarativeAnchors::LeftAnchor ||
612 usedAnchors & QDeclarativeAnchors::RightAnchor ||
613 usedAnchors & QDeclarativeAnchors::HCenterAnchor ||
614 anchors->fill() || anchors->centerIn()) {
615 d->anchorConflict = true;
616 break;
617 }
618 }
619 }
620 }
621 if (d->anchorConflict)
622 qmlInfo(this) << "Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row";
623}
624
625/*!
626 \qmlclass Grid QDeclarativeGrid
627 \ingroup qml-positioning-elements
628 \since 4.7
629 \brief The Grid item positions its children in a grid.
630 \inherits Item
631
632 The Grid item positions its child items so that they are
633 aligned in a grid and are not overlapping.
634
635 The grid positioner calculates a grid of rectangular cells of sufficient
636 size to hold all items, placing the items in the cells, from left to right
637 and top to bottom. Each item is positioned in the top-left corner of its
638 cell with position (0, 0).
639
640 A Grid defaults to four columns, and as many rows as are necessary to
641 fit all child items. The number of rows and columns can be constrained
642 by setting the \l rows and \l columns properties.
643
644 Spacing can be added between child items by setting the \l spacing
645 property. The amount of spacing applied will be the same in the
646 horizontal and vertical directions.
647
648 See \l{Using QML Positioner and Repeater Items} for more details about this item and other
649 related items.
650
651 \section1 Example Usage
652
653 The following example demonstrates this.
654
655 \image gridLayout_example.png
656
657 \snippet doc/src/snippets/declarative/grid/grid.qml document
658
659 \section1 Using Transitions
660
661 Transitions can be used to animate items that are added to, moved within,
662 or removed from a Grid item. The \l add and \l move properties can be set to
663 the transitions that will be applied when items are added to, removed from,
664 or re-positioned within a Grid item.
665
666 \section1 Limitations
667
668 Note that the positioner assumes that the x and y positions of its children
669 will not change. If you manually change the x or y properties in script, bind
670 the x or y properties, use anchors on a child of a positioner, or have the
671 width or height of a child depend on the position of a child, then the
672 positioner may exhibit strange behaviour. If you need to perform any of these
673 actions, consider positioning the items without the use of a Grid.
674
675 Items with a width or height of 0 will not be positioned.
676
677 \sa Flow, Row, Column, {declarative/positioners}{Positioners example}
678*/
679/*!
680 \qmlproperty Transition Grid::add
681
682 This property holds the transition to be applied when adding an
683 item to the positioner. The transition will only be applied to the
684 added item(s). Positioner transitions will only affect the
685 position (x, y) of items.
686
687 For a positioner, adding an item can mean that either the object
688 has been created or reparented, and thus is now a child or the
689 positioner, or that the object has had its opacity increased from
690 zero, and thus is now visible.
691
692 \sa move
693*/
694/*!
695 \qmlproperty Transition Grid::move
696
697 This property holds the transition to be applied when moving an
698 item within the positioner. Positioner transitions will only affect
699 the position (x, y) of items.
700
701 This transition can be performed when other items are added or removed
702 from the positioner, or when items resize themselves.
703
704 \qml
705 Grid {
706 move: Transition {
707 NumberAnimation {
708 properties: "x,y"
709 ease: "easeOutBounce"
710 }
711 }
712 }
713 \endqml
714
715 \sa add, {declarative/positioners}{Positioners example}
716*/
717/*!
718 \qmlproperty int Grid::spacing
719
720 The spacing is the amount in pixels left empty between adjacent
721 items. The default spacing is 0.
722
723 The below example places a Grid containing a red, a blue and a
724 green rectangle on a gray background. The area the grid positioner
725 occupies is colored white. The positioner on the left has the
726 no spacing (the default), and the positioner on the right has
727 a spacing of 6.
728
729 \inlineimage qml-grid-no-spacing.png
730 \inlineimage qml-grid-spacing.png
731
732 \sa rows, columns
733*/
734QDeclarativeGrid::QDeclarativeGrid(QDeclarativeItem *parent) :
735 QDeclarativeBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_flow(LeftToRight)
736{
737}
738
739/*!
740 \qmlproperty int Grid::columns
741
742 This property holds the number of columns in the grid. The default
743 number of columns is 4.
744
745 If the grid does not have enough items to fill the specified
746 number of columns, some columns will be of zero width.
747*/
748
749/*!
750 \qmlproperty int Grid::rows
751 This property holds the number of rows in the grid.
752
753 If the grid does not have enough items to fill the specified
754 number of rows, some rows will be of zero width.
755*/
756
757void QDeclarativeGrid::setColumns(const int columns)
758{
759 if (columns == m_columns)
760 return;
761 m_columns = columns;
762 prePositioning();
763 emit columnsChanged();
764}
765
766void QDeclarativeGrid::setRows(const int rows)
767{
768 if (rows == m_rows)
769 return;
770 m_rows = rows;
771 prePositioning();
772 emit rowsChanged();
773}
774
775/*!
776 \qmlproperty enumeration Grid::flow
777 This property holds the flow of the layout.
778
779 Possible values are:
780
781 \list
782 \o Grid.LeftToRight (default) - Items are positioned next to
783 to each other from left to right, then wrapped to the next line.
784 \o Grid.TopToBottom - Items are positioned next to each
785 other from top to bottom, then wrapped to the next column.
786 \endlist
787*/
788QDeclarativeGrid::Flow QDeclarativeGrid::flow() const
789{
790 return m_flow;
791}
792
793void QDeclarativeGrid::setFlow(Flow flow)
794{
795 if (m_flow != flow) {
796 m_flow = flow;
797 prePositioning();
798 emit flowChanged();
799 }
800}
801
802void QDeclarativeGrid::doPositioning(QSizeF *contentSize)
803{
804
805 int c = m_columns;
806 int r = m_rows;
807 //Is allocating the extra QPODVector too much overhead?
808 QPODVector<PositionedItem, 8> visibleItems;//we aren't concerned with invisible items
809 visibleItems.reserve(positionedItems.count());
810 for(int i=0; i<positionedItems.count(); i++)
811 if(positionedItems[i].item && positionedItems[i].isVisible)
812 visibleItems.append(positionedItems[i]);
813
814 int numVisible = visibleItems.count();
815 if (m_columns <= 0 && m_rows <= 0){
816 c = 4;
817 r = (numVisible+3)/4;
818 } else if (m_rows <= 0){
819 r = (numVisible+(m_columns-1))/m_columns;
820 } else if (m_columns <= 0){
821 c = (numVisible+(m_rows-1))/m_rows;
822 }
823
824 QList<int> maxColWidth;
825 QList<int> maxRowHeight;
826 int childIndex =0;
827 if (m_flow == LeftToRight) {
828 for (int i=0; i < r; i++){
829 for (int j=0; j < c; j++){
830 if (j==0)
831 maxRowHeight << 0;
832 if (i==0)
833 maxColWidth << 0;
834
835 if (childIndex == visibleItems.count())
836 break;
837
838 const PositionedItem &child = visibleItems.at(childIndex++);
839 QGraphicsItemPrivate *childPrivate = QGraphicsItemPrivate::get(child.item);
840 if (childPrivate->width() > maxColWidth[j])
841 maxColWidth[j] = childPrivate->width();
842 if (childPrivate->height() > maxRowHeight[i])
843 maxRowHeight[i] = childPrivate->height();
844 }
845 }
846 } else {
847 for (int j=0; j < c; j++){
848 for (int i=0; i < r; i++){
849 if (j==0)
850 maxRowHeight << 0;
851 if (i==0)
852 maxColWidth << 0;
853
854 if (childIndex == positionedItems.count())
855 break;
856
857 const PositionedItem &child = visibleItems.at(childIndex++);
858 QGraphicsItemPrivate *childPrivate = QGraphicsItemPrivate::get(child.item);
859 if (childPrivate->width() > maxColWidth[j])
860 maxColWidth[j] = childPrivate->width();
861 if (childPrivate->height() > maxRowHeight[i])
862 maxRowHeight[i] = childPrivate->height();
863 }
864 }
865 }
866
867 int xoffset=0;
868 int yoffset=0;
869 int curRow =0;
870 int curCol =0;
871 for (int i = 0; i < visibleItems.count(); ++i) {
872 const PositionedItem &child = visibleItems.at(i);
873 if((child.item->x()!=xoffset)||(child.item->y()!=yoffset)){
874 positionX(xoffset, child);
875 positionY(yoffset, child);
876 }
877
878 if (m_flow == LeftToRight) {
879 contentSize->setWidth(qMax(contentSize->width(), xoffset + QGraphicsItemPrivate::get(child.item)->width()));
880 contentSize->setHeight(yoffset + maxRowHeight[curRow]);
881
882 xoffset+=maxColWidth[curCol]+spacing();
883 curCol++;
884 curCol%=c;
885 if (!curCol){
886 yoffset+=maxRowHeight[curRow]+spacing();
887 xoffset=0;
888 curRow++;
889 if (curRow>=r)
890 break;
891 }
892 } else {
893 contentSize->setHeight(qMax(contentSize->height(), yoffset + QGraphicsItemPrivate::get(child.item)->height()));
894 contentSize->setWidth(xoffset + maxColWidth[curCol]);
895
896 yoffset+=maxRowHeight[curRow]+spacing();
897 curRow++;
898 curRow%=r;
899 if (!curRow){
900 xoffset+=maxColWidth[curCol]+spacing();
901 yoffset=0;
902 curCol++;
903 if (curCol>=c)
904 break;
905 }
906 }
907 }
908}
909
910void QDeclarativeGrid::reportConflictingAnchors()
911{
912 QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate*>(QDeclarativeBasePositionerPrivate::get(this));
913 for (int ii = 0; ii < positionedItems.count(); ++ii) {
914 const PositionedItem &child = positionedItems.at(ii);
915 if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) {
916 QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(static_cast<QDeclarativeItem *>(child.item))->_anchors;
917 if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) {
918 d->anchorConflict = true;
919 break;
920 }
921 }
922 }
923 if (d->anchorConflict)
924 qmlInfo(this) << "Cannot specify anchors for items inside Grid";
925}
926
927/*!
928 \qmlclass Flow QDeclarativeFlow
929 \ingroup qml-positioning-elements
930 \since 4.7
931 \brief The Flow item arranges its children side by side, wrapping as necessary.
932 \inherits Item
933
934 The Flow item positions its child items like words on a page, wrapping them
935 to create rows or columns of items that do not overlap.
936
937 Spacing between items can be added using the \l spacing property.
938 Transitions can be used for cases where items managed by a Column are
939 added or moved. These are stored in the \l add and \l move properties
940 respectively.
941
942 See \l{Using QML Positioner and Repeater Items} for more details about this item and other
943 related items.
944
945 \section1 Example Usage
946
947 The following example positions \l Text items within a parent item using
948 a Flow item.
949
950 \image qml-flow-snippet.png
951
952 \snippet doc/src/snippets/declarative/flow.qml flow item
953
954 \section1 Using Transitions
955
956 Transitions can be used to animate items that are added to, moved within,
957 or removed from a Flow item. The \l add and \l move properties can be set to
958 the transitions that will be applied when items are added to, removed from,
959 or re-positioned within a Flow item.
960
961 The use of transitions with positioners is described in more detail in the
962 \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML
963 Positioner and Repeater Items} document.
964
965 \section1 Limitations
966
967 Note that the positioner assumes that the x and y positions of its children
968 will not change. If you manually change the x or y properties in script, bind
969 the x or y properties, use anchors on a child of a positioner, or have the
970 width or height of a child depend on the position of a child, then the
971 positioner may exhibit strange behaviour. If you need to perform any of these
972 actions, consider positioning the items without the use of a Flow.
973
974 Items with a width or height of 0 will not be positioned.
975
976 \sa Column, Row, Grid, {declarative/positioners}{Positioners example}
977*/
978/*!
979 \qmlproperty Transition Flow::add
980
981 This property holds the transition to be applied when adding an
982 item to the positioner. The transition will only be applied to the
983 added item(s). Positioner transitions will only affect the
984 position (x, y) of items.
985
986 For a positioner, adding an item can mean that either the object
987 has been created or reparented, and thus is now a child or the
988 positioner, or that the object has had its opacity increased from
989 zero, and thus is now visible.
990
991 \sa move
992*/
993/*!
994 \qmlproperty Transition Flow::move
995
996 This property holds the transition to be applied when moving an
997 item within the positioner. Positioner transitions will only affect
998 the position (x, y) of items.
999
1000 This transition can be performed when other items are added or removed
1001 from the positioner, or when items resize themselves.
1002
1003 \qml
1004 Flow {
1005 id: positioner
1006 move: Transition {
1007 NumberAnimation {
1008 properties: "x,y"
1009 ease: "easeOutBounce"
1010 }
1011 }
1012 }
1013 \endqml
1014
1015 \sa add, {declarative/positioners}{Positioners example}
1016*/
1017/*!
1018 \qmlproperty int Flow::spacing
1019
1020 spacing is the amount in pixels left empty between each adjacent
1021 item, and defaults to 0.
1022
1023 \sa Grid::spacing
1024*/
1025
1026class QDeclarativeFlowPrivate : public QDeclarativeBasePositionerPrivate
1027{
1028 Q_DECLARE_PUBLIC(QDeclarativeFlow)
1029
1030public:
1031 QDeclarativeFlowPrivate()
1032 : QDeclarativeBasePositionerPrivate(), flow(QDeclarativeFlow::LeftToRight)
1033 {}
1034
1035 QDeclarativeFlow::Flow flow;
1036};
1037
1038QDeclarativeFlow::QDeclarativeFlow(QDeclarativeItem *parent)
1039: QDeclarativeBasePositioner(*(new QDeclarativeFlowPrivate), Both, parent)
1040{
1041 Q_D(QDeclarativeFlow);
1042 // Flow layout requires relayout if its own size changes too.
1043 d->addItemChangeListener(d, QDeclarativeItemPrivate::Geometry);
1044}
1045
1046/*!
1047 \qmlproperty enumeration Flow::flow
1048 This property holds the flow of the layout.
1049
1050 Possible values are:
1051
1052 \list
1053 \o Flow.LeftToRight (default) - Items are positioned next to
1054 to each other from left to right until the width of the Flow
1055 is exceeded, then wrapped to the next line.
1056 \o Flow.TopToBottom - Items are positioned next to each
1057 other from top to bottom until the height of the Flow is exceeded,
1058 then wrapped to the next column.
1059 \endlist
1060*/
1061QDeclarativeFlow::Flow QDeclarativeFlow::flow() const
1062{
1063 Q_D(const QDeclarativeFlow);
1064 return d->flow;
1065}
1066
1067void QDeclarativeFlow::setFlow(Flow flow)
1068{
1069 Q_D(QDeclarativeFlow);
1070 if (d->flow != flow) {
1071 d->flow = flow;
1072 prePositioning();
1073 emit flowChanged();
1074 }
1075}
1076
1077void QDeclarativeFlow::doPositioning(QSizeF *contentSize)
1078{
1079 Q_D(QDeclarativeFlow);
1080
1081 int hoffset = 0;
1082 int voffset = 0;
1083 int linemax = 0;
1084
1085 for (int i = 0; i < positionedItems.count(); ++i) {
1086 const PositionedItem &child = positionedItems.at(i);
1087 if (!child.item || !child.isVisible)
1088 continue;
1089
1090 QGraphicsItemPrivate *childPrivate = QGraphicsItemPrivate::get(child.item);
1091 if (d->flow == LeftToRight) {
1092 if (widthValid() && hoffset && hoffset + childPrivate->width() > width()) {
1093 hoffset = 0;
1094 voffset += linemax + spacing();
1095 linemax = 0;
1096 }
1097 } else {
1098 if (heightValid() && voffset && voffset + childPrivate->height() > height()) {
1099 voffset = 0;
1100 hoffset += linemax + spacing();
1101 linemax = 0;
1102 }
1103 }
1104
1105 if(child.item->x() != hoffset || child.item->y() != voffset){
1106 positionX(hoffset, child);
1107 positionY(voffset, child);
1108 }
1109
1110 contentSize->setWidth(qMax(contentSize->width(), hoffset + childPrivate->width()));
1111 contentSize->setHeight(qMax(contentSize->height(), voffset + childPrivate->height()));
1112
1113 if (d->flow == LeftToRight) {
1114 hoffset += childPrivate->width();
1115 hoffset += spacing();
1116 linemax = qMax(linemax, qCeil(childPrivate->height()));
1117 } else {
1118 voffset += childPrivate->height();
1119 voffset += spacing();
1120 linemax = qMax(linemax, qCeil(childPrivate->width()));
1121 }
1122 }
1123}
1124
1125void QDeclarativeFlow::reportConflictingAnchors()
1126{
1127 Q_D(QDeclarativeFlow);
1128 for (int ii = 0; ii < positionedItems.count(); ++ii) {
1129 const PositionedItem &child = positionedItems.at(ii);
1130 if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) {
1131 QDeclarativeAnchors *anchors = QDeclarativeItemPrivate::get(static_cast<QDeclarativeItem *>(child.item))->_anchors;
1132 if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) {
1133 d->anchorConflict = true;
1134 break;
1135 }
1136 }
1137 }
1138 if (d->anchorConflict)
1139 qmlInfo(this) << "Cannot specify anchors for items inside Flow";
1140}
1141
1142QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.