1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickpositioners_p.h"
5#include "qquickpositioners_p_p.h"
6
7#include <QtQml/qqml.h>
8#include <QtQml/qqmlinfo.h>
9#include <QtCore/qcoreapplication.h>
10
11#include <QtQuick/private/qquicktransition_p.h>
12
13#include <algorithm>
14
15QT_BEGIN_NAMESPACE
16
17static const QQuickItemPrivate::ChangeTypes positionerWatchedChanges
18 = QQuickItemPrivate::Geometry
19 | QQuickItemPrivate::SiblingOrder
20 | QQuickItemPrivate::Visibility
21 | QQuickItemPrivate::Destroyed;
22
23void QQuickBasePositionerPrivate::watchChanges(QQuickItem *other)
24{
25 QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(item: other);
26 otherPrivate->addItemChangeListener(listener: this, types: positionerWatchedChanges);
27}
28
29void QQuickBasePositionerPrivate::unwatchChanges(QQuickItem* other)
30{
31 QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(item: other);
32 otherPrivate->removeItemChangeListener(this, types: positionerWatchedChanges);
33}
34
35
36QQuickBasePositioner::PositionedItem::PositionedItem(QQuickItem *i)
37 : item(i)
38#if QT_CONFIG(quick_viewtransitions)
39 , transitionableItem(nullptr)
40#endif
41 , index(-1)
42 , isNew(false)
43 , isVisible(true)
44 , topPadding(0)
45 , leftPadding(0)
46 , rightPadding(0)
47 , bottomPadding(0)
48{
49}
50
51qreal QQuickBasePositioner::PositionedItem::itemX() const
52{
53 return
54#if QT_CONFIG(quick_viewtransitions)
55 transitionableItem ? transitionableItem->itemX() :
56#endif
57 item->x();
58}
59
60qreal QQuickBasePositioner::PositionedItem::itemY() const
61{
62 return
63#if QT_CONFIG(quick_viewtransitions)
64 transitionableItem ? transitionableItem->itemY() :
65#endif
66 item->y();
67}
68
69void QQuickBasePositioner::PositionedItem::moveTo(const QPointF &pos)
70{
71#if QT_CONFIG(quick_viewtransitions)
72 if (transitionableItem)
73 transitionableItem->moveTo(pos);
74 else
75#endif
76 item->setPosition(pos);
77}
78
79#if QT_CONFIG(quick_viewtransitions)
80void QQuickBasePositioner::PositionedItem::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
81{
82 if (!transitioner)
83 return;
84 if (!transitionableItem)
85 transitionableItem = std::make_unique<QQuickItemViewTransitionableItem>(args&: item);
86 transitioner->transitionNextReposition(item: transitionableItem.get(), type, isTarget: asTarget);
87}
88
89bool QQuickBasePositioner::PositionedItem::prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds)
90{
91 return transitionableItem ? transitionableItem->prepareTransition(transitioner, index, viewBounds) : false;
92}
93
94void QQuickBasePositioner::PositionedItem::startTransition(QQuickItemViewTransitioner *transitioner)
95{
96 if (transitionableItem)
97 transitionableItem->startTransition(transitioner, index);
98}
99#endif
100
101void QQuickBasePositioner::PositionedItem::updatePadding(qreal lp, qreal tp, qreal rp, qreal bp)
102{
103 leftPadding = lp;
104 topPadding = tp;
105 rightPadding = rp;
106 bottomPadding = bp;
107}
108
109QQuickBasePositioner::QQuickBasePositioner(PositionerType at, QQuickItem *parent)
110 : QQuickImplicitSizeItem(*(new QQuickBasePositionerPrivate), parent)
111{
112 Q_D(QQuickBasePositioner);
113 d->init(at);
114}
115/*!
116 \internal
117 \class QQuickBasePositioner
118 \brief For specifying a base for QQuickGraphics layouts
119
120 To create a QQuickGraphics Positioner, simply subclass QQuickBasePositioner and implement
121 doLayout(), which is automatically called when the layout might need
122 updating. In doLayout() use the setX and setY functions from QQuickBasePositioner, and the
123 base class will apply the positions along with the appropriate transitions. The items to
124 position are provided in order as the protected member positionedItems.
125
126 You also need to set a PositionerType, to declare whether you are positioning the x, y or both
127 for the child items. Depending on the chosen type, only x or y changes will be applied.
128
129 Note that the subclass is responsible for adding the spacing in between items.
130
131 Positioning is batched and synchronized with painting to reduce the number of
132 calculations needed. This means that positioners may not reposition items immediately
133 when changes occur, but it will have moved by the next frame.
134*/
135
136QQuickBasePositioner::QQuickBasePositioner(QQuickBasePositionerPrivate &dd, PositionerType at, QQuickItem *parent)
137 : QQuickImplicitSizeItem(dd, parent)
138{
139 Q_D(QQuickBasePositioner);
140 d->init(at);
141}
142
143QQuickBasePositioner::~QQuickBasePositioner()
144{
145 Q_D(QQuickBasePositioner);
146#if QT_CONFIG(quick_viewtransitions)
147 delete d->transitioner;
148#endif
149 for (const PositionedItem &pi : positionedItems)
150 d->unwatchChanges(other: pi.item);
151 for (const PositionedItem &pi : unpositionedItems)
152 d->unwatchChanges(other: pi.item);
153 positionedItems.clear();
154 unpositionedItems.clear();
155}
156
157void QQuickBasePositioner::updatePolish()
158{
159 Q_D(QQuickBasePositioner);
160 if (d->positioningDirty)
161 prePositioning();
162}
163
164qreal QQuickBasePositioner::spacing() const
165{
166 Q_D(const QQuickBasePositioner);
167 return d->spacing;
168}
169
170void QQuickBasePositioner::setSpacing(qreal s)
171{
172 Q_D(QQuickBasePositioner);
173 if (s == d->spacing)
174 return;
175 d->spacing = s;
176 d->setPositioningDirty();
177 emit spacingChanged();
178}
179
180#if QT_CONFIG(quick_viewtransitions)
181QQuickTransition *QQuickBasePositioner::populate() const
182{
183 Q_D(const QQuickBasePositioner);
184 return d->transitioner ? d->transitioner->populateTransition : nullptr;
185}
186
187void QQuickBasePositioner::setPopulate(QQuickTransition *transition)
188{
189 Q_D(QQuickBasePositioner);
190 if (!d->transitioner)
191 d->transitioner = new QQuickItemViewTransitioner;
192 if (d->transitioner->populateTransition != transition) {
193 d->transitioner->populateTransition = transition;
194 emit populateChanged();
195 }
196}
197
198QQuickTransition *QQuickBasePositioner::move() const
199{
200 Q_D(const QQuickBasePositioner);
201 return d->transitioner ? d->transitioner->displacedTransition : nullptr;
202}
203
204void QQuickBasePositioner::setMove(QQuickTransition *mt)
205{
206 Q_D(QQuickBasePositioner);
207 if (!d->transitioner)
208 d->transitioner = new QQuickItemViewTransitioner;
209 if (mt == d->transitioner->displacedTransition)
210 return;
211
212 d->transitioner->displacedTransition = mt;
213 emit moveChanged();
214}
215
216QQuickTransition *QQuickBasePositioner::add() const
217{
218 Q_D(const QQuickBasePositioner);
219 return d->transitioner ? d->transitioner->addTransition : nullptr;
220}
221
222void QQuickBasePositioner::setAdd(QQuickTransition *add)
223{
224 Q_D(QQuickBasePositioner);
225 if (!d->transitioner)
226 d->transitioner = new QQuickItemViewTransitioner;
227 if (add == d->transitioner->addTransition)
228 return;
229
230 d->transitioner->addTransition = add;
231 emit addChanged();
232}
233#endif
234
235void QQuickBasePositioner::componentComplete()
236{
237#if QT_CONFIG(quick_viewtransitions)
238 Q_D(QQuickBasePositioner);
239#endif
240 QQuickItem::componentComplete();
241#if QT_CONFIG(quick_viewtransitions)
242 if (d->transitioner)
243 d->transitioner->setPopulateTransitionEnabled(true);
244#endif
245 positionedItems.reserve(n: childItems().size());
246 prePositioning();
247#if QT_CONFIG(quick_viewtransitions)
248 if (d->transitioner)
249 d->transitioner->setPopulateTransitionEnabled(false);
250#endif
251}
252
253void QQuickBasePositioner::itemChange(ItemChange change, const ItemChangeData &value)
254{
255 Q_D(QQuickBasePositioner);
256 if (change == ItemChildAddedChange) {
257 d->setPositioningDirty();
258 } else if (change == ItemChildRemovedChange) {
259 QQuickItem *child = value.item;
260 auto it = std::find(first: positionedItems.begin(), last: positionedItems.end(), val: child);
261 if (it != positionedItems.end()) {
262 d->unwatchChanges(other: child);
263 positionedItems.erase(position: it);
264 } else {
265 it = std::find(first: unpositionedItems.begin(), last: unpositionedItems.end(), val: child);
266 if (it != unpositionedItems.end()) {
267 d->unwatchChanges(other: child);
268 unpositionedItems.erase(position: it);
269 }
270 }
271 d->setPositioningDirty();
272 }
273
274 QQuickItem::itemChange(change, value);
275}
276
277void QQuickBasePositioner::forceLayout()
278{
279 updatePolish();
280}
281
282void QQuickBasePositioner::prePositioning()
283{
284 Q_D(QQuickBasePositioner);
285 if (!isComponentComplete())
286 return;
287
288 if (d->doingPositioning)
289 return;
290
291 d->positioningDirty = false;
292 d->doingPositioning = true;
293 //Need to order children by creation order modified by stacking order
294 QList<QQuickItem *> children = childItems();
295
296 std::vector<PositionedItem> oldItems;
297 oldItems.reserve(n: positionedItems.size() + unpositionedItems.size());
298
299 std::move(first: positionedItems.begin(), last: positionedItems.end(),
300 result: std::back_inserter(x&: oldItems));
301 positionedItems.clear();
302
303 std::move(first: unpositionedItems.begin(), last: unpositionedItems.end(),
304 result: std::back_inserter(x&: oldItems));
305 unpositionedItems.clear();
306
307#if QT_CONFIG(quick_viewtransitions)
308 int addedIndex = -1;
309#endif
310
311 for (int ii = 0; ii < children.size(); ++ii) {
312 QQuickItem *child = children.at(i: ii);
313 if (QQuickItemPrivate::get(item: child)->isTransparentForPositioner())
314 continue;
315 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(item: child);
316 PositionedItem posItem(child);
317 auto it = std::find(first: oldItems.begin(), last: oldItems.end(), val: posItem);
318 if (it == oldItems.end()) {
319 // This is a newly added item.
320 d->watchChanges(other: child);
321 posItem.isNew = true;
322 if (!childPrivate->explicitVisible || !child->width() || !child->height()) {
323 posItem.isVisible = false;
324 posItem.index = -1;
325 // If we hide a zero-width or height item by setting visible to false,
326 // the !childPrivate->explicitVisible will then always trigger. We can't
327 // overwrite what the user has set, and we don't want to introduce a separate
328 // flag to track whether the visible property was actually explicitly set so
329 // that we can implicitly set it, so instead we use culled for this.
330 childPrivate->setCulled(true);
331 unpositionedItems.push_back(x: std::move(posItem));
332 } else {
333 const int posIndex = int(positionedItems.size());
334 posItem.index = posIndex;
335 positionedItems.push_back(x: std::move(posItem));
336
337#if QT_CONFIG(quick_viewtransitions)
338 if (d->transitioner) {
339 if (addedIndex < 0)
340 addedIndex = posIndex;
341 PositionedItem *theItem = &positionedItems.back();
342 if (d->transitioner->canTransition(type: QQuickItemViewTransitioner::PopulateTransition, asTarget: true))
343 theItem->transitionNextReposition(transitioner: d->transitioner, type: QQuickItemViewTransitioner::PopulateTransition, asTarget: true);
344 else if (!d->transitioner->populateTransitionEnabled())
345 theItem->transitionNextReposition(transitioner: d->transitioner, type: QQuickItemViewTransitioner::AddTransition, asTarget: true);
346 }
347#endif
348 }
349 } else {
350 // This item already existed within us.
351 PositionedItem *item = &*it;
352 // Items are only omitted from positioning if they are explicitly hidden
353 // i.e. their positioning is not affected if an ancestor is hidden.
354 if (!childPrivate->explicitVisible || !child->width() || !child->height()) {
355 item->isVisible = false;
356 item->index = -1;
357 childPrivate->setCulled(true);
358 unpositionedItems.push_back(x: std::move(*item));
359 } else if (!item->isVisible) {
360 // item changed from non-visible to visible, treat it as a "new" item
361 item->isVisible = true;
362 item->isNew = true;
363 const int itemIndex = int(positionedItems.size());
364 item->index = itemIndex;
365 childPrivate->setCulled(false);
366 positionedItems.push_back(x: std::move(*item));
367
368#if QT_CONFIG(quick_viewtransitions)
369 if (d->transitioner) {
370 if (addedIndex < 0)
371 addedIndex = itemIndex;
372 positionedItems.back().transitionNextReposition(transitioner: d->transitioner, type: QQuickItemViewTransitioner::AddTransition, asTarget: true);
373 }
374#endif
375 } else {
376 item->isNew = false;
377 const int itemIndex = int(positionedItems.size());
378 item->index = itemIndex;
379 positionedItems.push_back(x: std::move(*item));
380 }
381 }
382 }
383
384#if QT_CONFIG(quick_viewtransitions)
385 if (d->transitioner) {
386 for (PositionedItem &item : positionedItems) {
387 if (!item.isNew) {
388 if (addedIndex >= 0) {
389 item.transitionNextReposition(transitioner: d->transitioner, type: QQuickItemViewTransitioner::AddTransition, asTarget: false);
390 } else {
391 // just queue the item for a move-type displace - if the item hasn't
392 // moved anywhere, it won't be transitioned anyway
393 item.transitionNextReposition(transitioner: d->transitioner, type: QQuickItemViewTransitioner::MoveTransition, asTarget: false);
394 }
395 }
396 }
397 }
398#endif
399
400 QSizeF contentSize(0,0);
401 reportConflictingAnchors();
402 if (!d->anchorConflict) {
403 doPositioning(contentSize: &contentSize);
404 updateAttachedProperties();
405 }
406
407#if QT_CONFIG(quick_viewtransitions)
408 if (d->transitioner) {
409 QRectF viewBounds(QPointF(), contentSize);
410 for (PositionedItem &item : positionedItems)
411 item.prepareTransition(transitioner: d->transitioner, viewBounds);
412 for (PositionedItem &item : positionedItems)
413 item.startTransition(transitioner: d->transitioner);
414 d->transitioner->resetTargetLists();
415 }
416#endif
417
418 d->doingPositioning = false;
419
420 //Set implicit size to the size of its children
421 setImplicitSize(contentSize.width(), contentSize.height());
422
423 emit positioningComplete();
424}
425
426void QQuickBasePositioner::positionItem(qreal x, qreal y, PositionedItem *target)
427{
428 if ( target->itemX() != x || target->itemY() != y )
429 target->moveTo(pos: QPointF(x, y));
430}
431
432void QQuickBasePositioner::positionItemX(qreal x, PositionedItem *target)
433{
434 Q_D(QQuickBasePositioner);
435 if (target->itemX() != x
436 && (d->type == Horizontal || d->type == Both)) {
437 target->moveTo(pos: QPointF(x, target->itemY()));
438 }
439}
440
441void QQuickBasePositioner::positionItemY(qreal y, PositionedItem *target)
442{
443 Q_D(QQuickBasePositioner);
444 if (target->itemY() != y
445 && (d->type == Vertical || d->type == Both)) {
446 target->moveTo(pos: QPointF(target->itemX(), y));
447 }
448}
449
450QQuickPositionerAttached *QQuickBasePositioner::qmlAttachedProperties(QObject *obj)
451{
452 return new QQuickPositionerAttached(obj);
453}
454
455void QQuickBasePositioner::updateAttachedProperties(QQuickPositionerAttached *specificProperty, QQuickItem *specificPropertyOwner) const
456{
457 // If this function is deemed too expensive or shows up in profiles, it could
458 // be changed to run only when there are attached properties present. This
459 // could be a flag in the positioner that is set by the attached property
460 // constructor.
461 QQuickPositionerAttached *prevLastProperty = nullptr;
462 QQuickPositionerAttached *lastProperty = nullptr;
463
464 const int positionedItemsSize = int(positionedItems.size());
465 for (int ii = 0; ii < positionedItemsSize; ++ii) {
466 const PositionedItem &child = positionedItems[ii];
467 if (!child.item)
468 continue;
469
470 QQuickPositionerAttached *property = nullptr;
471
472 if (specificProperty) {
473 if (specificPropertyOwner == child.item) {
474 property = specificProperty;
475 }
476 } else {
477 property = static_cast<QQuickPositionerAttached *>(qmlAttachedPropertiesObject<QQuickBasePositioner>(obj: child.item, create: false));
478 }
479
480 if (property) {
481 property->setIndex(ii);
482 property->setIsFirstItem(ii == 0);
483
484 if (property->isLastItem()) {
485 if (prevLastProperty)
486 prevLastProperty->setIsLastItem(false); // there can be only one last property
487 prevLastProperty = property;
488 }
489 }
490
491 lastProperty = property;
492 }
493
494 if (prevLastProperty && prevLastProperty != lastProperty)
495 prevLastProperty->setIsLastItem(false);
496 if (lastProperty)
497 lastProperty->setIsLastItem(true);
498
499 // clear attached properties for unpositioned items
500 for (const PositionedItem &child : unpositionedItems) {
501 if (!child.item)
502 continue;
503
504 QQuickPositionerAttached *property = nullptr;
505
506 if (specificProperty) {
507 if (specificPropertyOwner == child.item) {
508 property = specificProperty;
509 }
510 } else {
511 property = static_cast<QQuickPositionerAttached *>(qmlAttachedPropertiesObject<QQuickBasePositioner>(obj: child.item, create: false));
512 }
513
514 if (property) {
515 property->setIndex(-1);
516 property->setIsFirstItem(false);
517 property->setIsLastItem(false);
518 }
519 }
520}
521
522qreal QQuickBasePositioner::padding() const
523{
524 Q_D(const QQuickBasePositioner);
525 return d->padding();
526}
527
528void QQuickBasePositioner::setPadding(qreal padding)
529{
530 Q_D(QQuickBasePositioner);
531 if (qFuzzyCompare(p1: d->padding(), p2: padding))
532 return;
533
534 d->extra.value().padding = padding;
535 d->setPositioningDirty();
536 emit paddingChanged();
537 if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
538 emit topPaddingChanged();
539 if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding)
540 emit leftPaddingChanged();
541 if (!d->extra.isAllocated() || !d->extra->explicitRightPadding)
542 emit rightPaddingChanged();
543 if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding)
544 emit bottomPaddingChanged();
545}
546
547void QQuickBasePositioner::resetPadding()
548{
549 setPadding(0);
550}
551
552qreal QQuickBasePositioner::topPadding() const
553{
554 Q_D(const QQuickBasePositioner);
555 if (d->extra.isAllocated() && d->extra->explicitTopPadding)
556 return d->extra->topPadding;
557 return d->padding();
558}
559
560void QQuickBasePositioner::setTopPadding(qreal padding)
561{
562 Q_D(QQuickBasePositioner);
563 d->setTopPadding(value: padding);
564}
565
566void QQuickBasePositioner::resetTopPadding()
567{
568 Q_D(QQuickBasePositioner);
569 d->setTopPadding(value: 0, reset: true);
570}
571
572qreal QQuickBasePositioner::leftPadding() const
573{
574 Q_D(const QQuickBasePositioner);
575 if (d->extra.isAllocated() && d->extra->explicitLeftPadding)
576 return d->extra->leftPadding;
577 return d->padding();
578}
579
580void QQuickBasePositioner::setLeftPadding(qreal padding)
581{
582 Q_D(QQuickBasePositioner);
583 d->setLeftPadding(value: padding);
584}
585
586void QQuickBasePositioner::resetLeftPadding()
587{
588 Q_D(QQuickBasePositioner);
589 d->setLeftPadding(value: 0, reset: true);
590}
591
592qreal QQuickBasePositioner::rightPadding() const
593{
594 Q_D(const QQuickBasePositioner);
595 if (d->extra.isAllocated() && d->extra->explicitRightPadding)
596 return d->extra->rightPadding;
597 return d->padding();
598}
599
600void QQuickBasePositioner::setRightPadding(qreal padding)
601{
602 Q_D(QQuickBasePositioner);
603 d->setRightPadding(value: padding);
604}
605
606void QQuickBasePositioner::resetRightPadding()
607{
608 Q_D(QQuickBasePositioner);
609 d->setRightPadding(value: 0, reset: true);
610}
611
612qreal QQuickBasePositioner::bottomPadding() const
613{
614 Q_D(const QQuickBasePositioner);
615 if (d->extra.isAllocated() && d->extra->explicitBottomPadding)
616 return d->extra->bottomPadding;
617 return d->padding();
618}
619
620void QQuickBasePositioner::setBottomPadding(qreal padding)
621{
622 Q_D(QQuickBasePositioner);
623 d->setBottomPadding(value: padding);
624}
625
626void QQuickBasePositioner::resetBottomPadding()
627{
628 Q_D(QQuickBasePositioner);
629 d->setBottomPadding(value: 0, reset: true);
630}
631
632QQuickBasePositionerPrivate::ExtraData::ExtraData()
633 : padding(0)
634 , topPadding(0)
635 , leftPadding(0)
636 , rightPadding(0)
637 , bottomPadding(0)
638 , explicitTopPadding(false)
639 , explicitLeftPadding(false)
640 , explicitRightPadding(false)
641 , explicitBottomPadding(false)
642{
643}
644
645void QQuickBasePositionerPrivate::setTopPadding(qreal value, bool reset)
646{
647 Q_Q(QQuickBasePositioner);
648 qreal oldPadding = q->topPadding();
649 if (!reset || extra.isAllocated()) {
650 extra.value().topPadding = value;
651 extra.value().explicitTopPadding = !reset;
652 }
653 if ((!reset && !qFuzzyCompare(p1: oldPadding, p2: value)) || (reset && !qFuzzyCompare(p1: oldPadding, p2: padding()))) {
654 setPositioningDirty();
655 emit q->topPaddingChanged();
656 }
657}
658
659void QQuickBasePositionerPrivate::setLeftPadding(qreal value, bool reset)
660{
661 Q_Q(QQuickBasePositioner);
662 qreal oldPadding = q->leftPadding();
663 if (!reset || extra.isAllocated()) {
664 extra.value().leftPadding = value;
665 extra.value().explicitLeftPadding = !reset;
666 }
667 if ((!reset && !qFuzzyCompare(p1: oldPadding, p2: value)) || (reset && !qFuzzyCompare(p1: oldPadding, p2: padding()))) {
668 setPositioningDirty();
669 emit q->leftPaddingChanged();
670 }
671}
672
673void QQuickBasePositionerPrivate::setRightPadding(qreal value, bool reset)
674{
675 Q_Q(QQuickBasePositioner);
676 qreal oldPadding = q->rightPadding();
677 if (!reset || extra.isAllocated()) {
678 extra.value().rightPadding = value;
679 extra.value().explicitRightPadding = !reset;
680 }
681 if ((!reset && !qFuzzyCompare(p1: oldPadding, p2: value)) || (reset && !qFuzzyCompare(p1: oldPadding, p2: padding()))) {
682 setPositioningDirty();
683 emit q->rightPaddingChanged();
684 }
685}
686
687void QQuickBasePositionerPrivate::setBottomPadding(qreal value, bool reset)
688{
689 Q_Q(QQuickBasePositioner);
690 qreal oldPadding = q->bottomPadding();
691 if (!reset || extra.isAllocated()) {
692 extra.value().bottomPadding = value;
693 extra.value().explicitBottomPadding = !reset;
694 }
695 if ((!reset && !qFuzzyCompare(p1: oldPadding, p2: value)) || (reset && !qFuzzyCompare(p1: oldPadding, p2: padding()))) {
696 setPositioningDirty();
697 emit q->bottomPaddingChanged();
698 }
699}
700
701/*!
702 \qmltype Positioner
703 \nativetype QQuickPositionerAttached
704 \inqmlmodule QtQuick
705 \ingroup qtquick-positioners
706 \brief Provides attached properties that contain details on where an item exists in a positioner.
707
708 An object of type Positioner is attached to the top-level child item within a
709 Column, Row, Flow or Grid. It provides properties that allow a child item to determine
710 where it exists within the layout of its parent Column, Row, Flow or Grid.
711
712 For example, below is a \l Grid with 16 child rectangles, as created through a \l Repeater.
713 Each \l Rectangle displays its index in the Grid using \l {Positioner::index}{Positioner.index}, and the first
714 item is colored differently by taking \l {Positioner::isFirstItem}{Positioner.isFirstItem} into account:
715
716 \code
717 Grid {
718 Repeater {
719 model: 16
720
721 Rectangle {
722 id: rect
723 width: 30; height: 30
724 border.width: 1
725 color: Positioner.isFirstItem ? "yellow" : "lightsteelblue"
726
727 Text { text: rect.Positioner.index }
728 }
729 }
730 }
731 \endcode
732
733 \image positioner-example.png
734*/
735
736QQuickPositionerAttached::QQuickPositionerAttached(QObject *parent) : QObject(parent), m_index(-1), m_isFirstItem(false), m_isLastItem(false)
737{
738 QQuickItem *attachedItem = qobject_cast<QQuickItem *>(o: parent);
739 if (attachedItem) {
740 QQuickBasePositioner *positioner = qobject_cast<QQuickBasePositioner *>(object: attachedItem->parent());
741 if (positioner) {
742 positioner->updateAttachedProperties(specificProperty: this, specificPropertyOwner: attachedItem);
743 }
744 }
745}
746
747/*!
748 \qmlattachedproperty int QtQuick::Positioner::index
749
750 This property allows the item to determine
751 its index within the positioner.
752*/
753void QQuickPositionerAttached::setIndex(int index)
754{
755 if (m_index == index)
756 return;
757 m_index = index;
758 emit indexChanged();
759}
760
761/*!
762 \qmlattachedproperty bool QtQuick::Positioner::isFirstItem
763 \qmlattachedproperty bool QtQuick::Positioner::isLastItem
764
765 These properties allow the item to determine if it
766 is the first or last item in the positioner, respectively.
767*/
768void QQuickPositionerAttached::setIsFirstItem(bool isFirstItem)
769{
770 if (m_isFirstItem == isFirstItem)
771 return;
772 m_isFirstItem = isFirstItem;
773 emit isFirstItemChanged();
774}
775
776void QQuickPositionerAttached::setIsLastItem(bool isLastItem)
777{
778 if (m_isLastItem == isLastItem)
779 return;
780 m_isLastItem = isLastItem;
781 emit isLastItemChanged();
782}
783
784/*!
785 \qmltype Column
786 \nativetype QQuickColumn
787 \inqmlmodule QtQuick
788 \inherits Item
789 \ingroup qtquick-positioners
790 \brief Positions its children in a column.
791
792 Column is a type that positions its child items along a single column.
793 It can be used as a convenient way to vertically position a series of items without
794 using \l {Positioning with Anchors}{anchors}.
795
796 Below is a Column that contains three rectangles of various sizes:
797
798 \snippet qml/column/vertical-positioner.qml document
799
800 The Column automatically positions these items in a vertical formation, like this:
801
802 \image verticalpositioner_example.png
803
804 If an item within a Column is not \l {Item::}{visible}, or if it has a width or
805 height of 0, the item will not be laid out and it will not be visible within the
806 column. Also, since a Column automatically positions its children vertically, a child
807 item within a Column should not set its \l {Item::y}{y} position or vertically
808 anchor itself using the \l {Item::anchors.top}{top}, \l {Item::anchors.bottom}{bottom},
809 \l {Item::anchors.verticalCenter}{anchors.verticalCenter}, \l {Item::anchors.fill}{fill}
810 or \l {Item::anchors.centerIn}{centerIn} anchors. If you need to perform these actions,
811 consider positioning the items without the use of a Column.
812
813 Note that items in a Column can use the \l Positioner attached property to access
814 more information about its position within the Column.
815
816 For more information on using Column and other related positioner-types, see
817 \l{Item Positioners}.
818
819
820 \section1 Using Transitions
821
822 A Column animate items using specific transitions when items are added to or moved
823 within a Column.
824
825 For example, the Column below sets the \l move property to a specific \l Transition:
826
827 \snippet qml/column/column-transitions.qml document
828
829 When the Space key is pressed, the \l {Item::visible}{visible} value of the green
830 \l Rectangle is toggled. As it appears and disappears, the blue \l Rectangle moves within
831 the Column, and the \l move transition is automatically applied to the blue \l Rectangle:
832
833 \image verticalpositioner_transition.gif
834
835 \sa Row, Grid, Flow, Positioner, ColumnLayout, {Qt Quick Examples - Positioners}
836*/
837/*!
838 \since 5.6
839 \qmlproperty real QtQuick::Column::padding
840 \qmlproperty real QtQuick::Column::topPadding
841 \qmlproperty real QtQuick::Column::leftPadding
842 \qmlproperty real QtQuick::Column::bottomPadding
843 \qmlproperty real QtQuick::Column::rightPadding
844
845 These properties hold the padding around the content.
846*/
847/*!
848 \qmlproperty Transition QtQuick::Column::populate
849
850 This property holds the transition to be run for items that are part of
851 this positioner at the time of its creation. The transition is run when the positioner
852 is first created.
853
854 The transition can use the \l ViewTransition property to access more details about
855 the item that is being added. See the \l ViewTransition documentation for more details
856 and examples on using these transitions.
857
858 \sa add, ViewTransition, {Qt Quick Examples - Positioners}
859*/
860/*!
861 \qmlproperty Transition QtQuick::Column::add
862
863 This property holds the transition to be run for items that are added to this
864 positioner. For a positioner, this applies to:
865
866 \list
867 \li Items that are created or reparented as a child of the positioner after the
868 positioner has been created
869 \li Child items that change their \l Item::visible property from false to true, and thus
870 are now visible
871 \endlist
872
873 The transition can use the \l ViewTransition property to access more details about
874 the item that is being added. See the \l ViewTransition documentation for more details
875 and examples on using these transitions.
876
877 \note This transition is not applied to the items that are already part of the positioner
878 at the time of its creation. In this case, the \l populate transition is applied instead.
879
880 \sa populate, ViewTransition, {Qt Quick Examples - Positioners}
881*/
882/*!
883 \qmlproperty Transition QtQuick::Column::move
884
885 This property holds the transition to run for items that have moved within the
886 positioner. For a positioner, this applies to:
887
888 \list
889 \li Child items that move when they are displaced due to the addition, removal or
890 rearrangement of other items in the positioner
891 \li Child items that are repositioned due to the resizing of other items in the positioner
892 \endlist
893
894 The transition can use the \l ViewTransition property to access more details about
895 the item that is being moved. Note, however, that for this move transition, the
896 ViewTransition.targetIndexes and ViewTransition.targetItems lists are only set when
897 this transition is triggered by the addition of other items in the positioner; in other
898 cases, these lists will be empty. See the \l ViewTransition documentation for more details
899 and examples on using these transitions.
900
901 \sa add, populate, ViewTransition, {Qt Quick Examples - Positioners}
902*/
903/*!
904 \qmlproperty real QtQuick::Column::spacing
905
906 The spacing is the amount in pixels left empty between adjacent
907 items. The default spacing is 0.
908
909 \sa Grid::spacing
910*/
911/*!
912 \qmlmethod QtQuick::Column::forceLayout()
913 \since 5.9
914
915 Column typically positions its children once per frame. This means that
916 inside script blocks it is possible for the underlying children to have changed,
917 but the Column to have not yet been updated accordingly.
918
919 This method forces the Column to immediately respond to any outstanding
920 changes in its children.
921
922 \b Note: methods in general should only be called after the Component has completed.
923*/
924/*!
925 \qmlsignal QtQuick::Column::positioningComplete()
926 \since 5.9
927
928 This signal is emitted when positioning has been completed.
929*/
930
931QQuickColumn::QQuickColumn(QQuickItem *parent)
932: QQuickBasePositioner(Vertical, parent)
933{
934}
935
936void QQuickColumn::doPositioning(QSizeF *contentSize)
937{
938 //Precondition: All items in the positioned list have a valid item pointer and should be positioned
939 qreal voffset = topPadding();
940 const qreal padding = leftPadding() + rightPadding();
941 contentSize->setWidth(qMax(a: contentSize->width(), b: padding));
942
943 for (PositionedItem &child : positionedItems) {
944 positionItem(x: child.itemX() + leftPadding() - child.leftPadding, y: voffset, target: &child);
945 child.updatePadding(lp: leftPadding(), tp: topPadding(), rp: rightPadding(), bp: bottomPadding());
946 contentSize->setWidth(qMax(a: contentSize->width(), b: child.item->width() + padding));
947
948 voffset += child.item->height();
949 voffset += spacing();
950 }
951
952 if (voffset - topPadding() != 0)//If we positioned any items, undo the spacing from the last item
953 voffset -= spacing();
954 contentSize->setHeight(voffset + bottomPadding());
955}
956
957void QQuickColumn::reportConflictingAnchors()
958{
959 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate*>(QQuickBasePositionerPrivate::get(item: this));
960 for (const PositionedItem &child : positionedItems) {
961 if (child.item) {
962 QQuickAnchors *anchors = QQuickItemPrivate::get(item: static_cast<QQuickItem *>(child.item))->_anchors;
963 if (anchors) {
964 QQuickAnchors::Anchors usedAnchors = anchors->usedAnchors();
965 if (usedAnchors & QQuickAnchors::TopAnchor ||
966 usedAnchors & QQuickAnchors::BottomAnchor ||
967 usedAnchors & QQuickAnchors::VCenterAnchor ||
968 anchors->fill() || anchors->centerIn()) {
969 d->anchorConflict = true;
970 break;
971 }
972 }
973 }
974 }
975 if (d->anchorConflict) {
976 qmlWarning(me: this) << "Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column."
977 << " Column will not function.";
978 }
979}
980/*!
981 \qmltype Row
982 \nativetype QQuickRow
983 \inqmlmodule QtQuick
984 \inherits Item
985 \ingroup qtquick-positioners
986 \brief Positions its children in a row.
987
988 Row is a type that positions its child items along a single row.
989 It can be used as a convenient way to horizontally position a series of items without
990 using \l {Positioning with Anchors}{anchors}.
991
992 Below is a Row that contains three rectangles of various sizes:
993
994 \snippet qml/row/row.qml document
995
996 The Row automatically positions these items in a horizontal formation, like this:
997
998 \image horizontalpositioner_example.png
999
1000 If an item within a Row is not \l {Item::}{visible}, or if it has a width or
1001 height of 0, the item will not be laid out and it will not be visible within the
1002 row. Also, since a Row automatically positions its children horizontally, a child
1003 item within a Row should not set its \l {Item::x}{x} position or horizontally
1004 anchor itself using the \l {Item::anchors.left}{left}, \l {Item::anchors.right}{right},
1005 \l {Item::anchors.horizontalCenter}{anchors.horizontalCenter}, \l {Item::anchors.fill}{fill}
1006 or \l {Item::anchors.centerIn}{centerIn} anchors. If you need to perform these actions,
1007 consider positioning the items without the use of a Row.
1008
1009 Note that items in a Row can use the \l Positioner attached property to access
1010 more information about its position within the Row.
1011
1012 For more information on using Row and other related positioner-types, see
1013 \l{Item Positioners}.
1014
1015
1016 \sa Column, Grid, Flow, Positioner, RowLayout, {Qt Quick Examples - Positioners}
1017*/
1018/*!
1019 \since 5.6
1020 \qmlproperty real QtQuick::Row::padding
1021 \qmlproperty real QtQuick::Row::topPadding
1022 \qmlproperty real QtQuick::Row::leftPadding
1023 \qmlproperty real QtQuick::Row::bottomPadding
1024 \qmlproperty real QtQuick::Row::rightPadding
1025
1026 These properties hold the padding around the content.
1027*/
1028/*!
1029 \qmlproperty Transition QtQuick::Row::populate
1030
1031 This property holds the transition to be run for items that are part of
1032 this positioner at the time of its creation. The transition is run when the positioner
1033 is first created.
1034
1035 The transition can use the \l ViewTransition property to access more details about
1036 the item that is being added. See the \l ViewTransition documentation for more details
1037 and examples on using these transitions.
1038
1039 \sa add, ViewTransition, {Qt Quick Examples - Positioners}
1040*/
1041/*!
1042 \qmlproperty Transition QtQuick::Row::add
1043
1044 This property holds the transition to be run for items that are added to this
1045 positioner. For a positioner, this applies to:
1046
1047 \list
1048 \li Items that are created or reparented as a child of the positioner after the
1049 positioner has been created
1050 \li Child items that change their \l Item::visible property from false to true, and thus
1051 are now visible
1052 \endlist
1053
1054 The transition can use the \l ViewTransition property to access more details about
1055 the item that is being added. See the \l ViewTransition documentation for more details
1056 and examples on using these transitions.
1057
1058 \note This transition is not applied to the items that are already part of the positioner
1059 at the time of its creation. In this case, the \l populate transition is applied instead.
1060
1061 \sa populate, ViewTransition, {Qt Quick Examples - Positioners}
1062*/
1063/*!
1064 \qmlproperty Transition QtQuick::Row::move
1065
1066 This property holds the transition to run for items that have moved within the
1067 positioner. For a positioner, this applies to:
1068
1069 \list
1070 \li Child items that move when they are displaced due to the addition, removal or
1071 rearrangement of other items in the positioner
1072 \li Child items that are repositioned due to the resizing of other items in the positioner
1073 \endlist
1074
1075 The transition can use the \l ViewTransition property to access more details about
1076 the item that is being moved. Note, however, that for this move transition, the
1077 ViewTransition.targetIndexes and ViewTransition.targetItems lists are only set when
1078 this transition is triggered by the addition of other items in the positioner; in other
1079 cases, these lists will be empty. See the \l ViewTransition documentation for more details
1080 and examples on using these transitions.
1081
1082 \sa add, populate, ViewTransition, {Qt Quick Examples - Positioners}
1083*/
1084/*!
1085 \qmlproperty real QtQuick::Row::spacing
1086
1087 The spacing is the amount in pixels left empty between adjacent
1088 items. The default spacing is 0.
1089
1090 \sa Grid::spacing
1091*/
1092/*!
1093 \qmlmethod QtQuick::Row::forceLayout()
1094 \since 5.9
1095
1096 Row typically positions its children once per frame. This means that
1097 inside script blocks it is possible for the underlying children to have changed,
1098 but the Row to have not yet been updated accordingly.
1099
1100 This method forces the Row to immediately respond to any outstanding
1101 changes in its children.
1102
1103 \b Note: methods in general should only be called after the Component has completed.
1104*/
1105/*!
1106 \qmlsignal QtQuick::Row::positioningComplete()
1107 \since 5.9
1108
1109 This signal is emitted when positioning has been completed.
1110*/
1111
1112class QQuickRowPrivate : public QQuickBasePositionerPrivate
1113{
1114 Q_DECLARE_PUBLIC(QQuickRow)
1115
1116public:
1117 QQuickRowPrivate()
1118 : QQuickBasePositionerPrivate()
1119 {}
1120
1121 void effectiveLayoutDirectionChange() override
1122 {
1123 Q_Q(QQuickRow);
1124 // For RTL layout the positioning changes when the width changes.
1125 if (getEffectiveLayoutDirection(positioner: q) == Qt::RightToLeft)
1126 addItemChangeListener(listener: this, types: QQuickItemPrivate::Geometry);
1127 else
1128 removeItemChangeListener(this, types: QQuickItemPrivate::Geometry);
1129 // Don't postpone, as it might be the only trigger for visible changes.
1130 q->prePositioning();
1131 emit q->effectiveLayoutDirectionChanged();
1132 }
1133};
1134
1135QQuickRow::QQuickRow(QQuickItem *parent)
1136: QQuickBasePositioner(*new QQuickRowPrivate, Horizontal, parent)
1137{
1138}
1139/*!
1140 \qmlproperty enumeration QtQuick::Row::layoutDirection
1141
1142 This property holds the layoutDirection of the row.
1143
1144 Possible values:
1145
1146 \value Qt.LeftToRight (default) Items are laid out from left to right. If the width of the row is
1147 explicitly set, the left anchor remains to the left of the row.
1148 \value Qt.RightToLeft Items are laid out from right to left. If the width of the row is
1149 explicitly set, the right anchor remains to the right of the row.
1150
1151 \sa Grid::layoutDirection, Flow::layoutDirection
1152*/
1153
1154Qt::LayoutDirection QQuickRow::layoutDirection() const
1155{
1156 return QQuickBasePositionerPrivate::getLayoutDirection(positioner: this);
1157}
1158
1159void QQuickRow::setLayoutDirection(Qt::LayoutDirection layoutDirection)
1160{
1161 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate* >(QQuickBasePositionerPrivate::get(item: this));
1162 if (d->layoutDirection != layoutDirection) {
1163 d->layoutDirection = layoutDirection;
1164 emit layoutDirectionChanged();
1165 d->effectiveLayoutDirectionChange();
1166 }
1167}
1168/*!
1169 \qmlproperty enumeration QtQuick::Row::effectiveLayoutDirection
1170 This property holds the effective layout direction of the row.
1171
1172 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1173 the visual layout direction of the row positioner will be mirrored. However, the
1174 property \l {Row::layoutDirection}{layoutDirection} will remain unchanged.
1175
1176 \sa Row::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1177*/
1178
1179Qt::LayoutDirection QQuickRow::effectiveLayoutDirection() const
1180{
1181 return QQuickBasePositionerPrivate::getEffectiveLayoutDirection(positioner: this);
1182}
1183
1184void QQuickRow::doPositioning(QSizeF *contentSize)
1185{
1186 //Precondition: All items in the positioned list have a valid item pointer and should be positioned
1187 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate* >(QQuickBasePositionerPrivate::get(item: this));
1188 qreal hoffset1 = leftPadding();
1189 qreal hoffset2 = rightPadding();
1190 if (!d->isLeftToRight())
1191 qSwap(value1&: hoffset1, value2&: hoffset2);
1192 qreal hoffset = hoffset1;
1193 const qreal padding = topPadding() + bottomPadding();
1194 contentSize->setHeight(qMax(a: contentSize->height(), b: padding));
1195
1196 QList<qreal> hoffsets;
1197 for (PositionedItem &child : positionedItems) {
1198 if (d->isLeftToRight()) {
1199 positionItem(x: hoffset, y: child.itemY() + topPadding() - child.topPadding, target: &child);
1200 child.updatePadding(lp: leftPadding(), tp: topPadding(), rp: rightPadding(), bp: bottomPadding());
1201 } else {
1202 hoffsets << hoffset;
1203 }
1204
1205 contentSize->setHeight(qMax(a: contentSize->height(), b: child.item->height() + padding));
1206
1207 hoffset += child.item->width();
1208 hoffset += spacing();
1209 }
1210
1211 if (hoffset - hoffset1 != 0)//If we positioned any items, undo the extra spacing from the last item
1212 hoffset -= spacing();
1213 contentSize->setWidth(hoffset + hoffset2);
1214
1215 if (d->isLeftToRight())
1216 return;
1217
1218 //Right to Left layout
1219 qreal end = 0;
1220 if (!widthValid())
1221 end = contentSize->width();
1222 else
1223 end = width();
1224
1225 int acc = 0;
1226 for (PositionedItem &child : positionedItems) {
1227 hoffset = end - hoffsets[acc++] - child.item->width();
1228 positionItem(x: hoffset, y: child.itemY() + topPadding() - child.topPadding, target: &child);
1229 child.updatePadding(lp: leftPadding(), tp: topPadding(), rp: rightPadding(), bp: bottomPadding());
1230 }
1231}
1232
1233void QQuickRow::reportConflictingAnchors()
1234{
1235 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate*>(QQuickBasePositionerPrivate::get(item: this));
1236 for (const PositionedItem &child : positionedItems) {
1237 if (child.item) {
1238 QQuickAnchors *anchors = QQuickItemPrivate::get(item: static_cast<QQuickItem *>(child.item))->_anchors;
1239 if (anchors) {
1240 QQuickAnchors::Anchors usedAnchors = anchors->usedAnchors();
1241 if (usedAnchors & QQuickAnchors::LeftAnchor ||
1242 usedAnchors & QQuickAnchors::RightAnchor ||
1243 usedAnchors & QQuickAnchors::HCenterAnchor ||
1244 anchors->fill() || anchors->centerIn()) {
1245 d->anchorConflict = true;
1246 break;
1247 }
1248 }
1249 }
1250 }
1251 if (d->anchorConflict)
1252 qmlWarning(me: this) << "Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row."
1253 << " Row will not function.";
1254}
1255
1256/*!
1257 \qmltype Grid
1258 \nativetype QQuickGrid
1259 \inqmlmodule QtQuick
1260 \inherits Item
1261 \ingroup qtquick-positioners
1262 \brief Positions its children in grid formation.
1263
1264 Grid is a type that positions its child items in grid formation.
1265
1266 A Grid creates a grid of cells that is large enough to hold all of its
1267 child items, and places these items in the cells from left to right
1268 and top to bottom. Each item is positioned at the top-left corner of its
1269 cell with position (0, 0).
1270
1271 A Grid defaults to four columns, and creates as many rows as are necessary to
1272 fit all of its child items. The number of rows and columns can be constrained
1273 by setting the \l rows and \l columns properties.
1274
1275 For example, below is a Grid that contains five rectangles of various sizes:
1276
1277 \snippet qml/grid/grid.qml document
1278
1279 The Grid automatically positions the child items in a grid formation:
1280
1281 \image gridLayout_example.png
1282
1283 If an item within a Grid is not \l {Item::}{visible}, or if it has a width or
1284 height of 0, the item will not be laid out and it will not be visible within the
1285 column. Also, since a Grid automatically positions its children, a child
1286 item within a Grid should not set its \l {Item::x}{x} or \l {Item::y}{y} positions
1287 or anchor itself with any of the \l {Item::anchors}{anchor} properties.
1288
1289 For more information on using Grid and other related positioner-types, see
1290 \l{Item Positioners}.
1291
1292
1293 \sa Flow, Row, Column, Positioner, GridLayout, {Qt Quick Examples - Positioners}
1294*/
1295/*!
1296 \since 5.6
1297 \qmlproperty real QtQuick::Grid::padding
1298 \qmlproperty real QtQuick::Grid::topPadding
1299 \qmlproperty real QtQuick::Grid::leftPadding
1300 \qmlproperty real QtQuick::Grid::bottomPadding
1301 \qmlproperty real QtQuick::Grid::rightPadding
1302
1303 These properties hold the padding around the content.
1304*/
1305/*!
1306 \qmlproperty Transition QtQuick::Grid::populate
1307
1308 This property holds the transition to be run for items that are part of
1309 this positioner at the time of its creation. The transition is run when the positioner
1310 is first created.
1311
1312 The transition can use the \l ViewTransition property to access more details about
1313 the item that is being added. See the \l ViewTransition documentation for more details
1314 and examples on using these transitions.
1315
1316 \sa add, ViewTransition, {Qt Quick Examples - Positioners}
1317*/
1318/*!
1319 \qmlproperty Transition QtQuick::Grid::add
1320
1321 This property holds the transition to be run for items that are added to this
1322 positioner. For a positioner, this applies to:
1323
1324 \list
1325 \li Items that are created or reparented as a child of the positioner after the
1326 positioner has been created
1327 \li Child items that change their \l Item::visible property from false to true, and thus
1328 are now visible
1329 \endlist
1330
1331 The transition can use the \l ViewTransition property to access more details about
1332 the item that is being added. See the \l ViewTransition documentation for more details
1333 and examples on using these transitions.
1334
1335 \note This transition is not applied to the items that are already part of the positioner
1336 at the time of its creation. In this case, the \l populate transition is applied instead.
1337
1338 \sa populate, ViewTransition, {Qt Quick Examples - Positioners}
1339*/
1340/*!
1341 \qmlproperty Transition QtQuick::Grid::move
1342
1343 This property holds the transition to run for items that have moved within the
1344 positioner. For a positioner, this applies to:
1345
1346 \list
1347 \li Child items that move when they are displaced due to the addition, removal or
1348 rearrangement of other items in the positioner
1349 \li Child items that are repositioned due to the resizing of other items in the positioner
1350 \endlist
1351
1352 The transition can use the \l ViewTransition property to access more details about
1353 the item that is being moved. Note, however, that for this move transition, the
1354 ViewTransition.targetIndexes and ViewTransition.targetItems lists are only set when
1355 this transition is triggered by the addition of other items in the positioner; in other
1356 cases, these lists will be empty. See the \l ViewTransition documentation for more details
1357 and examples on using these transitions.
1358
1359 \sa add, populate, ViewTransition, {Qt Quick Examples - Positioners}
1360*/
1361/*!
1362 \qmlproperty real QtQuick::Grid::spacing
1363
1364 The spacing is the amount in pixels left empty between adjacent
1365 items. The amount of spacing applied will be the same in the
1366 horizontal and vertical directions. The default spacing is 0.
1367
1368 The below example places a Grid containing a red, a blue and a
1369 green rectangle on a gray background. The area the grid positioner
1370 occupies is colored white. The positioner on the left has the
1371 no spacing (the default), and the positioner on the right has
1372 a spacing of 6.
1373
1374 \inlineimage qml-grid-no-spacing.png
1375 \inlineimage qml-grid-spacing.png
1376
1377 \sa rows, columns
1378*/
1379/*!
1380 \qmlmethod QtQuick::Grid::forceLayout()
1381 \since 5.9
1382
1383 Grid typically positions its children once per frame. This means that
1384 inside script blocks it is possible for the underlying children to have changed,
1385 but the Grid to have not yet been updated accordingly.
1386
1387 This method forces the Grid to immediately respond to any outstanding
1388 changes in its children.
1389
1390 \b Note: methods in general should only be called after the Component has completed.
1391*/
1392/*!
1393 \qmlsignal QtQuick::Grid::positioningComplete()
1394 \since 5.9
1395
1396 This signal is emitted when positioning has been completed.
1397*/
1398
1399class QQuickGridPrivate : public QQuickBasePositionerPrivate
1400{
1401 Q_DECLARE_PUBLIC(QQuickGrid)
1402
1403public:
1404 QQuickGridPrivate()
1405 : QQuickBasePositionerPrivate()
1406 {}
1407
1408 void effectiveLayoutDirectionChange() override
1409 {
1410 Q_Q(QQuickGrid);
1411 // For RTL layout the positioning changes when the width changes.
1412 if (getEffectiveLayoutDirection(positioner: q) == Qt::RightToLeft)
1413 addItemChangeListener(listener: this, types: QQuickItemPrivate::Geometry);
1414 else
1415 removeItemChangeListener(this, types: QQuickItemPrivate::Geometry);
1416 // Don't postpone, as it might be the only trigger for visible changes.
1417 q->prePositioning();
1418 emit q->effectiveLayoutDirectionChanged();
1419 emit q->effectiveHorizontalAlignmentChanged(alignment: q->effectiveHAlign());
1420 }
1421};
1422
1423QQuickGrid::QQuickGrid(QQuickItem *parent)
1424 : QQuickBasePositioner(*new QQuickGridPrivate, Both, parent)
1425 , m_rows(-1)
1426 , m_columns(-1)
1427 , m_rowSpacing(-1)
1428 , m_columnSpacing(-1)
1429 , m_useRowSpacing(false)
1430 , m_useColumnSpacing(false)
1431 , m_flow(LeftToRight)
1432 , m_hItemAlign(AlignLeft)
1433 , m_vItemAlign(AlignTop)
1434{
1435}
1436
1437/*!
1438 \qmlproperty int QtQuick::Grid::columns
1439
1440 This property holds the number of columns in the grid. The default
1441 number of columns is 4.
1442
1443 If the grid does not have enough items to fill the specified
1444 number of columns, some columns will be of zero width.
1445*/
1446
1447/*!
1448 \qmlproperty int QtQuick::Grid::rows
1449 This property holds the number of rows in the grid.
1450
1451 If the grid does not have enough items to fill the specified
1452 number of rows, some rows will be of zero width.
1453*/
1454
1455void QQuickGrid::setColumns(const int columns)
1456{
1457 if (columns == m_columns)
1458 return;
1459 m_columns = columns;
1460 prePositioning();
1461 emit columnsChanged();
1462}
1463
1464void QQuickGrid::setRows(const int rows)
1465{
1466 if (rows == m_rows)
1467 return;
1468 m_rows = rows;
1469 prePositioning();
1470 emit rowsChanged();
1471}
1472
1473/*!
1474 \qmlproperty enumeration QtQuick::Grid::flow
1475 This property holds the flow of the layout.
1476
1477 Possible values are:
1478
1479 \list
1480 \li Grid.LeftToRight (default) - Items are positioned next to
1481 each other in the \l layoutDirection, then wrapped to the next line.
1482 \li Grid.TopToBottom - Items are positioned next to each
1483 other from top to bottom, then wrapped to the next column.
1484 \endlist
1485*/
1486QQuickGrid::Flow QQuickGrid::flow() const
1487{
1488 return m_flow;
1489}
1490
1491void QQuickGrid::setFlow(Flow flow)
1492{
1493 if (m_flow != flow) {
1494 m_flow = flow;
1495 prePositioning();
1496 emit flowChanged();
1497 }
1498}
1499
1500/*!
1501 \qmlproperty real QtQuick::Grid::rowSpacing
1502
1503 This property holds the spacing in pixels between rows.
1504
1505 If this property is not set, then spacing is used for the row spacing.
1506
1507 By default this property is not set.
1508
1509 \sa columnSpacing
1510 \since 5.0
1511*/
1512void QQuickGrid::setRowSpacing(const qreal rowSpacing)
1513{
1514 if (rowSpacing == m_rowSpacing)
1515 return;
1516 m_rowSpacing = rowSpacing;
1517 m_useRowSpacing = true;
1518 prePositioning();
1519 emit rowSpacingChanged();
1520}
1521
1522/*!
1523 \qmlproperty real QtQuick::Grid::columnSpacing
1524
1525 This property holds the spacing in pixels between columns.
1526
1527 If this property is not set, then spacing is used for the column spacing.
1528
1529 By default this property is not set.
1530
1531 \sa rowSpacing
1532 \since 5.0
1533*/
1534void QQuickGrid::setColumnSpacing(const qreal columnSpacing)
1535{
1536 if (columnSpacing == m_columnSpacing)
1537 return;
1538 m_columnSpacing = columnSpacing;
1539 m_useColumnSpacing = true;
1540 prePositioning();
1541 emit columnSpacingChanged();
1542}
1543
1544/*!
1545 \qmlproperty enumeration QtQuick::Grid::layoutDirection
1546
1547 This property holds the layout direction of the layout.
1548
1549 Possible values are:
1550
1551 \list
1552 \li Qt.LeftToRight (default) - Items are positioned from the top to bottom,
1553 and left to right. The flow direction is dependent on the
1554 \l Grid::flow property.
1555 \li Qt.RightToLeft - Items are positioned from the top to bottom,
1556 and right to left. The flow direction is dependent on the
1557 \l Grid::flow property.
1558 \endlist
1559
1560 \sa Flow::layoutDirection, Row::layoutDirection
1561*/
1562Qt::LayoutDirection QQuickGrid::layoutDirection() const
1563{
1564 return QQuickBasePositionerPrivate::getLayoutDirection(positioner: this);
1565}
1566
1567void QQuickGrid::setLayoutDirection(Qt::LayoutDirection layoutDirection)
1568{
1569 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate*>(QQuickBasePositionerPrivate::get(item: this));
1570 if (d->layoutDirection != layoutDirection) {
1571 d->layoutDirection = layoutDirection;
1572 emit layoutDirectionChanged();
1573 d->effectiveLayoutDirectionChange();
1574 }
1575}
1576
1577/*!
1578 \qmlproperty enumeration QtQuick::Grid::effectiveLayoutDirection
1579 This property holds the effective layout direction of the grid.
1580
1581 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1582 the visual layout direction of the grid positioner will be mirrored. However, the
1583 property \l {Grid::layoutDirection}{layoutDirection} will remain unchanged.
1584
1585 \sa Grid::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1586*/
1587Qt::LayoutDirection QQuickGrid::effectiveLayoutDirection() const
1588{
1589 return QQuickBasePositionerPrivate::getEffectiveLayoutDirection(positioner: this);
1590}
1591
1592/*!
1593 \qmlproperty enumeration QtQuick::Grid::horizontalItemAlignment
1594 \qmlproperty enumeration QtQuick::Grid::verticalItemAlignment
1595 \qmlproperty enumeration QtQuick::Grid::effectiveHorizontalItemAlignment
1596 \since 5.1
1597
1598 Sets the horizontal and vertical alignment of items in the Grid. By default,
1599 the items are vertically aligned to the top. Horizontal
1600 alignment follows the layoutDirection of the Grid, for example when having a layoutDirection
1601 from LeftToRight, the items will be aligned on the left.
1602
1603 The valid values for \c horizontalItemAlignment are, \c Grid.AlignLeft, \c Grid.AlignRight and
1604 \c Grid.AlignHCenter.
1605
1606 The valid values for \c verticalItemAlignment are \c Grid.AlignTop, \c Grid.AlignBottom
1607 and \c Grid.AlignVCenter.
1608
1609 The below images show three examples of how to align items.
1610
1611 \table
1612 \row
1613 \li
1614 \li \inlineimage gridLayout_aligntopleft.png
1615 \li \inlineimage gridLayout_aligntop.png
1616 \li \inlineimage gridLayout_aligncenter.png
1617 \row
1618 \li Horizontal alignment
1619 \li AlignLeft
1620 \li AlignHCenter
1621 \li AlignHCenter
1622 \row
1623 \li Vertical alignment
1624 \li AlignTop
1625 \li AlignTop
1626 \li AlignVCenter
1627 \endtable
1628
1629
1630 When mirroring the layout using either the attached property LayoutMirroring::enabled or
1631 by setting the layoutDirection, the horizontal alignment of items will be mirrored as well.
1632 However, the property \c horizontalItemAlignment will remain unchanged.
1633 To query the effective horizontal alignment of items, use the read-only property
1634 \c effectiveHorizontalItemAlignment.
1635
1636 \sa Grid::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1637*/
1638QQuickGrid::HAlignment QQuickGrid::hItemAlign() const
1639{
1640 return m_hItemAlign;
1641}
1642void QQuickGrid::setHItemAlign(HAlignment align)
1643{
1644 if (m_hItemAlign != align) {
1645 m_hItemAlign = align;
1646 prePositioning();
1647 emit horizontalAlignmentChanged(alignment: align);
1648 emit effectiveHorizontalAlignmentChanged(alignment: effectiveHAlign());
1649 }
1650}
1651
1652QQuickGrid::HAlignment QQuickGrid::effectiveHAlign() const
1653{
1654 HAlignment effectiveAlignment = m_hItemAlign;
1655 if (effectiveLayoutDirection() == Qt::RightToLeft) {
1656 switch (hItemAlign()) {
1657 case AlignLeft:
1658 effectiveAlignment = AlignRight;
1659 break;
1660 case AlignRight:
1661 effectiveAlignment = AlignLeft;
1662 break;
1663 default:
1664 break;
1665 }
1666 }
1667 return effectiveAlignment;
1668}
1669
1670
1671QQuickGrid::VAlignment QQuickGrid::vItemAlign() const
1672{
1673 return m_vItemAlign;
1674}
1675void QQuickGrid::setVItemAlign(VAlignment align)
1676{
1677 if (m_vItemAlign != align) {
1678 m_vItemAlign = align;
1679 prePositioning();
1680 emit verticalAlignmentChanged(alignment: align);
1681 }
1682}
1683
1684void QQuickGrid::doPositioning(QSizeF *contentSize)
1685{
1686 //Precondition: All items in the positioned list have a valid item pointer and should be positioned
1687 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate*>(QQuickBasePositionerPrivate::get(item: this));
1688 int c = m_columns;
1689 int r = m_rows;
1690 const int numVisible = int(positionedItems.size());
1691
1692 if (m_columns <= 0 && m_rows <= 0) {
1693 c = 4;
1694 r = (numVisible+3)/4;
1695 } else if (m_rows <= 0) {
1696 r = (numVisible+(m_columns-1))/m_columns;
1697 } else if (m_columns <= 0) {
1698 c = (numVisible+(m_rows-1))/m_rows;
1699 }
1700
1701 if (r == 0 || c == 0) {
1702 contentSize->setHeight(topPadding() + bottomPadding());
1703 contentSize->setWidth(leftPadding() + rightPadding());
1704 return; //Nothing else to do
1705 }
1706
1707 if (numVisible > r * c) {
1708 qmlWarning(me: this) << "Grid contains more visible items (" << numVisible << ") than rows*columns (" << r * c << ")";
1709 }
1710
1711 QList<qreal> maxColWidth;
1712 QList<qreal> maxRowHeight;
1713 int childIndex =0;
1714 if (m_flow == LeftToRight) {
1715 for (int i = 0; i < r; i++) {
1716 for (int j = 0; j < c; j++) {
1717 if (j == 0)
1718 maxRowHeight << 0;
1719 if (i == 0)
1720 maxColWidth << 0;
1721
1722 if (childIndex == numVisible)
1723 break;
1724
1725 const PositionedItem &child = positionedItems[childIndex++];
1726 if (child.item->width() > maxColWidth[j])
1727 maxColWidth[j] = child.item->width();
1728 if (child.item->height() > maxRowHeight[i])
1729 maxRowHeight[i] = child.item->height();
1730 }
1731 }
1732 } else {
1733 for (int j = 0; j < c; j++) {
1734 for (int i = 0; i < r; i++) {
1735 if (j == 0)
1736 maxRowHeight << 0;
1737 if (i == 0)
1738 maxColWidth << 0;
1739
1740 if (childIndex == numVisible)
1741 break;
1742
1743 const PositionedItem &child = positionedItems[childIndex++];
1744 if (child.item->width() > maxColWidth[j])
1745 maxColWidth[j] = child.item->width();
1746 if (child.item->height() > maxRowHeight[i])
1747 maxRowHeight[i] = child.item->height();
1748 }
1749 }
1750 }
1751
1752 qreal columnSpacing = m_useColumnSpacing ? m_columnSpacing : spacing();
1753 qreal rowSpacing = m_useRowSpacing ? m_rowSpacing : spacing();
1754
1755 qreal widthSum = 0;
1756 for (int j = 0; j < maxColWidth.size(); j++) {
1757 if (j)
1758 widthSum += columnSpacing;
1759 widthSum += maxColWidth[j];
1760 }
1761 widthSum += leftPadding() + rightPadding();
1762
1763 qreal heightSum = 0;
1764 for (int i = 0; i < maxRowHeight.size(); i++) {
1765 if (i)
1766 heightSum += rowSpacing;
1767 heightSum += maxRowHeight[i];
1768 }
1769 heightSum += topPadding() + bottomPadding();
1770
1771 contentSize->setHeight(heightSum);
1772 contentSize->setWidth(widthSum);
1773
1774 int end = 0;
1775 if (widthValid())
1776 end = width();
1777 else
1778 end = widthSum;
1779
1780 qreal xoffset = leftPadding();
1781 if (!d->isLeftToRight())
1782 xoffset = end - rightPadding();
1783 qreal yoffset = topPadding();
1784 int curRow =0;
1785 int curCol =0;
1786 for (PositionedItem &child : positionedItems) {
1787 qreal childXOffset = xoffset;
1788
1789 if (effectiveHAlign() == AlignRight)
1790 childXOffset += maxColWidth[curCol] - child.item->width();
1791 else if (hItemAlign() == AlignHCenter)
1792 childXOffset += (maxColWidth[curCol] - child.item->width())/2.0;
1793
1794 if (!d->isLeftToRight())
1795 childXOffset -= maxColWidth[curCol];
1796
1797 qreal alignYOffset = yoffset;
1798 if (m_vItemAlign == AlignVCenter)
1799 alignYOffset += (maxRowHeight[curRow] - child.item->height())/2.0;
1800 else if (m_vItemAlign == AlignBottom)
1801 alignYOffset += maxRowHeight[curRow] - child.item->height();
1802
1803 positionItem(x: childXOffset, y: alignYOffset, target: &child);
1804 child.updatePadding(lp: leftPadding(), tp: topPadding(), rp: rightPadding(), bp: bottomPadding());
1805
1806 if (m_flow == LeftToRight) {
1807 if (d->isLeftToRight())
1808 xoffset += maxColWidth[curCol]+columnSpacing;
1809 else
1810 xoffset -= maxColWidth[curCol]+columnSpacing;
1811 curCol++;
1812 curCol %= c;
1813 if (!curCol) {
1814 yoffset += maxRowHeight[curRow]+rowSpacing;
1815 if (d->isLeftToRight())
1816 xoffset = leftPadding();
1817 else
1818 xoffset = end - rightPadding();
1819 curRow++;
1820 if (curRow>=r)
1821 break;
1822 }
1823 } else {
1824 yoffset += maxRowHeight[curRow]+rowSpacing;
1825 curRow++;
1826 curRow %= r;
1827 if (!curRow) {
1828 if (d->isLeftToRight())
1829 xoffset += maxColWidth[curCol]+columnSpacing;
1830 else
1831 xoffset -= maxColWidth[curCol]+columnSpacing;
1832 yoffset = topPadding();
1833 curCol++;
1834 if (curCol>=c)
1835 break;
1836 }
1837 }
1838 }
1839}
1840
1841void QQuickGrid::reportConflictingAnchors()
1842{
1843 QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate*>(QQuickBasePositionerPrivate::get(item: this));
1844 for (const PositionedItem &child : positionedItems) {
1845 if (child.item) {
1846 QQuickAnchors *anchors = QQuickItemPrivate::get(item: static_cast<QQuickItem *>(child.item))->_anchors;
1847 if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) {
1848 d->anchorConflict = true;
1849 break;
1850 }
1851 }
1852 }
1853 if (d->anchorConflict)
1854 qmlWarning(me: this) << "Cannot specify anchors for items inside Grid." << " Grid will not function.";
1855}
1856
1857/*!
1858 \qmltype Flow
1859 \nativetype QQuickFlow
1860 \inqmlmodule QtQuick
1861 \inherits Item
1862 \ingroup qtquick-positioners
1863 \brief Positions its children side by side, wrapping as necessary.
1864
1865 The Flow item positions its child items like words on a page, wrapping them
1866 to create rows or columns of items.
1867
1868 Below is a Flow that contains various \l Text items:
1869
1870 \snippet qml/flow.qml flow item
1871
1872 The Flow item automatically positions the child \l Text items side by
1873 side, wrapping as necessary:
1874
1875 \image qml-flow-snippet.png
1876
1877 If an item within a Flow is not \l {Item::}{visible}, or if it has a width or
1878 height of 0, the item will not be laid out and it will not be visible within the
1879 Flow. Also, since a Flow automatically positions its children, a child
1880 item within a Flow should not set its \l {Item::x}{x} or \l {Item::y}{y} positions
1881 or anchor itself with any of the \l {Item::anchors}{anchor} properties.
1882
1883 For more information on using Flow and other related positioner-types, see
1884 \l{Item Positioners}.
1885
1886 \sa Column, Row, Grid, Positioner, {Qt Quick Examples - Positioners}
1887*/
1888/*!
1889 \since 5.6
1890 \qmlproperty real QtQuick::Flow::padding
1891 \qmlproperty real QtQuick::Flow::topPadding
1892 \qmlproperty real QtQuick::Flow::leftPadding
1893 \qmlproperty real QtQuick::Flow::bottomPadding
1894 \qmlproperty real QtQuick::Flow::rightPadding
1895
1896 These properties hold the padding around the content.
1897*/
1898/*!
1899 \qmlproperty Transition QtQuick::Flow::populate
1900
1901 This property holds the transition to be run for items that are part of
1902 this positioner at the time of its creation. The transition is run when the positioner
1903 is first created.
1904
1905 The transition can use the \l ViewTransition property to access more details about
1906 the item that is being added. See the \l ViewTransition documentation for more details
1907 and examples on using these transitions.
1908
1909 \sa add, ViewTransition, {Qt Quick Examples - Positioners}
1910*/
1911/*!
1912 \qmlproperty Transition QtQuick::Flow::add
1913
1914 This property holds the transition to be run for items that are added to this
1915 positioner. For a positioner, this applies to:
1916
1917 \list
1918 \li Items that are created or reparented as a child of the positioner after the
1919 positioner has been created
1920 \li Child items that change their \l Item::visible property from false to true, and thus
1921 are now visible
1922 \endlist
1923
1924 The transition can use the \l ViewTransition property to access more details about
1925 the item that is being added. See the \l ViewTransition documentation for more details
1926 and examples on using these transitions.
1927
1928 \note This transition is not applied to the items that are already part of the positioner
1929 at the time of its creation. In this case, the \l populate transition is applied instead.
1930
1931 \sa populate, ViewTransition, {Qt Quick Examples - Positioners}
1932*/
1933/*!
1934 \qmlproperty Transition QtQuick::Flow::move
1935
1936 This property holds the transition to run for items that have moved within the
1937 positioner. For a positioner, this applies to:
1938
1939 \list
1940 \li Child items that move when they are displaced due to the addition, removal or
1941 rearrangement of other items in the positioner
1942 \li Child items that are repositioned due to the resizing of other items in the positioner
1943 \endlist
1944
1945 The transition can use the \l ViewTransition property to access more details about
1946 the item that is being moved. Note, however, that for this move transition, the
1947 ViewTransition.targetIndexes and ViewTransition.targetItems lists are only set when
1948 this transition is triggered by the addition of other items in the positioner; in other
1949 cases, these lists will be empty. See the \l ViewTransition documentation for more details
1950 and examples on using these transitions.
1951
1952 \sa add, populate, ViewTransition, {Qt Quick Examples - Positioners}
1953*/
1954/*!
1955 \qmlproperty real QtQuick::Flow::spacing
1956
1957 spacing is the amount in pixels left empty between each adjacent
1958 item, and defaults to 0.
1959
1960 \sa Grid::spacing
1961*/
1962/*!
1963 \qmlmethod QtQuick::Flow::forceLayout()
1964 \since 5.9
1965
1966 Flow typically positions its children once per frame. This means that
1967 inside script blocks it is possible for the underlying children to have changed,
1968 but the Flow to have not yet been updated accordingly.
1969
1970 This method forces the Flow to immediately respond to any outstanding
1971 changes in its children.
1972
1973
1974 \b Note: methods in general should only be called after the Component has completed.
1975*/
1976/*!
1977 \qmlsignal QtQuick::Flow::positioningComplete()
1978 \since 5.9
1979
1980 This signal is emitted when positioning has been completed.
1981*/
1982
1983class QQuickFlowPrivate : public QQuickBasePositionerPrivate
1984{
1985 Q_DECLARE_PUBLIC(QQuickFlow)
1986
1987public:
1988 QQuickFlowPrivate()
1989 : QQuickBasePositionerPrivate(), flow(QQuickFlow::LeftToRight)
1990 {}
1991
1992 void effectiveLayoutDirectionChange() override
1993 {
1994 Q_Q(QQuickFlow);
1995 // Don't postpone, as it might be the only trigger for visible changes.
1996 q->prePositioning();
1997 emit q->effectiveLayoutDirectionChanged();
1998 }
1999
2000 QQuickFlow::Flow flow;
2001};
2002
2003QQuickFlow::QQuickFlow(QQuickItem *parent)
2004: QQuickBasePositioner(*(new QQuickFlowPrivate), Both, parent)
2005{
2006 Q_D(QQuickFlow);
2007 // Flow layout requires relayout if its own size changes too.
2008 d->addItemChangeListener(listener: d, types: QQuickItemPrivate::Geometry);
2009}
2010
2011/*!
2012 \qmlproperty enumeration QtQuick::Flow::flow
2013 This property holds the flow of the layout.
2014
2015 Possible values are:
2016
2017 \list
2018 \li Flow.LeftToRight (default) - Items are positioned next to
2019 to each other according to the \l layoutDirection until the width of the Flow
2020 is exceeded, then wrapped to the next line.
2021 \li Flow.TopToBottom - Items are positioned next to each
2022 other from top to bottom until the height of the Flow is exceeded,
2023 then wrapped to the next column.
2024 \endlist
2025*/
2026QQuickFlow::Flow QQuickFlow::flow() const
2027{
2028 Q_D(const QQuickFlow);
2029 return d->flow;
2030}
2031
2032void QQuickFlow::setFlow(Flow flow)
2033{
2034 Q_D(QQuickFlow);
2035 if (d->flow != flow) {
2036 d->flow = flow;
2037 prePositioning();
2038 emit flowChanged();
2039 }
2040}
2041
2042/*!
2043 \qmlproperty enumeration QtQuick::Flow::layoutDirection
2044
2045 This property holds the layout direction of the layout.
2046
2047 Possible values are:
2048
2049 \list
2050 \li Qt.LeftToRight (default) - Items are positioned from the top to bottom,
2051 and left to right. The flow direction is dependent on the
2052 \l Flow::flow property.
2053 \li Qt.RightToLeft - Items are positioned from the top to bottom,
2054 and right to left. The flow direction is dependent on the
2055 \l Flow::flow property.
2056 \endlist
2057
2058 \sa Grid::layoutDirection, Row::layoutDirection
2059*/
2060
2061Qt::LayoutDirection QQuickFlow::layoutDirection() const
2062{
2063 Q_D(const QQuickFlow);
2064 return d->layoutDirection;
2065}
2066
2067void QQuickFlow::setLayoutDirection(Qt::LayoutDirection layoutDirection)
2068{
2069 Q_D(QQuickFlow);
2070 if (d->layoutDirection != layoutDirection) {
2071 d->layoutDirection = layoutDirection;
2072 emit layoutDirectionChanged();
2073 d->effectiveLayoutDirectionChange();
2074 }
2075}
2076
2077/*!
2078 \qmlproperty enumeration QtQuick::Flow::effectiveLayoutDirection
2079 This property holds the effective layout direction of the flow.
2080
2081 When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2082 the visual layout direction of the grid positioner will be mirrored. However, the
2083 property \l {Flow::layoutDirection}{layoutDirection} will remain unchanged.
2084
2085 \sa Flow::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2086*/
2087
2088Qt::LayoutDirection QQuickFlow::effectiveLayoutDirection() const
2089{
2090 return QQuickBasePositionerPrivate::getEffectiveLayoutDirection(positioner: this);
2091}
2092
2093void QQuickFlow::doPositioning(QSizeF *contentSize)
2094{
2095 //Precondition: All items in the positioned list have a valid item pointer and should be positioned
2096 Q_D(QQuickFlow);
2097
2098 qreal hoffset1 = leftPadding();
2099 qreal hoffset2 = rightPadding();
2100 if (!d->isLeftToRight())
2101 qSwap(value1&: hoffset1, value2&: hoffset2);
2102 qreal hoffset = hoffset1;
2103 const qreal voffset1 = topPadding();
2104 qreal voffset = voffset1;
2105 qreal linemax = 0;
2106 QList<qreal> hoffsets;
2107 contentSize->setWidth(qMax(a: contentSize->width(), b: hoffset1 + hoffset2));
2108 contentSize->setHeight(qMax(a: contentSize->height(), b: voffset + bottomPadding()));
2109
2110 for (PositionedItem &child : positionedItems) {
2111 if (d->flow == LeftToRight) {
2112 if (widthValid() && hoffset != hoffset1 && hoffset + child.item->width() + hoffset2 > width()) {
2113 hoffset = hoffset1;
2114 voffset += linemax + spacing();
2115 linemax = 0;
2116 }
2117 } else {
2118 if (heightValid() && voffset != voffset1 && voffset + child.item->height() + bottomPadding() > height()) {
2119 voffset = voffset1;
2120 hoffset += linemax + spacing();
2121 linemax = 0;
2122 }
2123 }
2124
2125 if (d->isLeftToRight()) {
2126 positionItem(x: hoffset, y: voffset, target: &child);
2127 child.updatePadding(lp: leftPadding(), tp: topPadding(), rp: rightPadding(), bp: bottomPadding());
2128 } else {
2129 hoffsets << hoffset;
2130 positionItemY(y: voffset, target: &child);
2131 child.topPadding = topPadding();
2132 child.bottomPadding = bottomPadding();
2133 }
2134
2135 contentSize->setWidth(qMax(a: contentSize->width(), b: hoffset + child.item->width() + hoffset2));
2136 contentSize->setHeight(qMax(a: contentSize->height(), b: voffset + child.item->height() + bottomPadding()));
2137
2138 if (d->flow == LeftToRight) {
2139 hoffset += child.item->width();
2140 hoffset += spacing();
2141 linemax = qMax(a: linemax, b: child.item->height());
2142 } else {
2143 voffset += child.item->height();
2144 voffset += spacing();
2145 linemax = qMax(a: linemax, b: child.item->width());
2146 }
2147 }
2148
2149 if (d->isLeftToRight())
2150 return;
2151
2152 qreal end;
2153 if (widthValid())
2154 end = width();
2155 else
2156 end = contentSize->width();
2157 int acc = 0;
2158 for (PositionedItem &child : positionedItems) {
2159 hoffset = end - hoffsets[acc++] - child.item->width();
2160 positionItemX(x: hoffset, target: &child);
2161 child.leftPadding = leftPadding();
2162 child.rightPadding = rightPadding();
2163 }
2164}
2165
2166void QQuickFlow::reportConflictingAnchors()
2167{
2168 Q_D(QQuickFlow);
2169 for (const PositionedItem &child : positionedItems) {
2170 if (child.item) {
2171 QQuickAnchors *anchors = QQuickItemPrivate::get(item: static_cast<QQuickItem *>(child.item))->_anchors;
2172 if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) {
2173 d->anchorConflict = true;
2174 break;
2175 }
2176 }
2177 }
2178 if (d->anchorConflict)
2179 qmlWarning(me: this) << "Cannot specify anchors for items inside Flow." << " Flow will not function.";
2180}
2181
2182QT_END_NAMESPACE
2183
2184#include "moc_qquickpositioners_p.cpp"
2185

source code of qtdeclarative/src/quick/items/qquickpositioners.cpp