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

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