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

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