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 "qquicklayout_p.h"
5#include <QEvent>
6#include <QtCore/qcoreapplication.h>
7#include <QtCore/private/qnumeric_p.h>
8#include <QtCore/qstack.h>
9#include <QtCore/qmath.h>
10#include <QtQml/qqmlinfo.h>
11#include <limits>
12
13/*!
14 \qmltype Layout
15 //! \nativetype QQuickLayoutAttached
16 \inqmlmodule QtQuick.Layouts
17 \ingroup layouts
18 \brief Provides attached properties for items pushed onto a \l GridLayout,
19 \l RowLayout or \l ColumnLayout.
20
21 An object of type Layout is attached to children of the layout to provide layout specific
22 information about the item.
23 The properties of the attached object influence how the layout will arrange the items.
24
25 For instance, you can specify \l minimumWidth, \l preferredWidth, and
26 \l maximumWidth if the default values are not satisfactory.
27
28 When a layout is resized, items may grow or shrink. Due to this, items have a
29 \l{Layout::minimumWidth}{minimum size}, \l{Layout::preferredWidth}{preferred size} and a
30 \l{Layout::maximumWidth}{maximum size}.
31
32 If minimum size has not been explicitly specified on an item, the size is set to \c 0.
33 If maximum size has not been explicitly specified on an item, the size is set to
34 \c Number.POSITIVE_INFINITY.
35
36 For layouts, the implicit minimum and maximum sizes depend on the content of the layouts.
37
38 The \l fillWidth and \l fillHeight properties can either be \c true or \c false. If they are \c
39 false, the item's size will be fixed to its preferred size. Otherwise, it will grow or shrink
40 between its minimum and maximum size as the layout is resized. If there are multiple items
41 with \l fillWidth (or \l fillHeight) set to \c true, the layout will grow or shrink the items
42 relative to the ratio of their preferred size.
43
44 For more details on the layout algorithm, see also the \l {Qt Quick Layouts Overview}.
45
46 \note Do not bind to the x, y, width, or height properties of items in a layout,
47 as this would conflict with the goals of Layout, and can also cause binding loops.
48 The width and height properties are used by the layout engine to store the current
49 size of items as calculated from the minimum/preferred/maximum attached properties,
50 and can be ovewritten each time the items are laid out. Use
51 \l {Layout::preferredWidth}{Layout.preferredWidth} and
52 \l {Layout::preferredHeight}{Layout.preferredHeight}, or \l {Item::}{implicitWidth}
53 and \l {Item::}{implicitHeight} to specify the preferred size of items.
54
55 \sa GridLayout
56 \sa RowLayout
57 \sa ColumnLayout
58*/
59
60QT_BEGIN_NAMESPACE
61
62Q_LOGGING_CATEGORY(lcQuickLayouts, "qt.quick.layouts")
63
64QQuickLayoutAttached::QQuickLayoutAttached(QObject *parent)
65 : QObject(parent),
66 m_minimumWidth(0),
67 m_minimumHeight(0),
68 m_preferredWidth(-1),
69 m_preferredHeight(-1),
70 m_maximumWidth(std::numeric_limits<qreal>::infinity()),
71 m_maximumHeight(std::numeric_limits<qreal>::infinity()),
72 m_defaultMargins(0),
73 m_fallbackWidth(-1),
74 m_fallbackHeight(-1),
75 m_row(-1),
76 m_column(-1),
77 m_rowSpan(1),
78 m_columnSpan(1),
79 m_fillWidth(false),
80 m_fillHeight(false),
81 m_isFillWidthSet(false),
82 m_isFillHeightSet(false),
83 m_isUseDefaultSizePolicySet(false),
84 m_useDefaultSizePolicy(QQuickLayout::SizePolicyExplicit),
85 m_isMinimumWidthSet(false),
86 m_isMinimumHeightSet(false),
87 m_isMaximumWidthSet(false),
88 m_isMaximumHeightSet(false),
89 m_changesNotificationEnabled(true),
90 m_isMarginsSet(false),
91 m_isLeftMarginSet(false),
92 m_isTopMarginSet(false),
93 m_isRightMarginSet(false),
94 m_isBottomMarginSet(false),
95 m_isAlignmentSet(false),
96 m_horizontalStretch(-1),
97 m_verticalStretch(-1)
98{
99
100}
101
102/*!
103 \qmlattachedproperty real Layout::minimumWidth
104
105 This property holds the minimum width of an item in a layout.
106 The default value is the item's implicit minimum width.
107
108 If the item is a layout, the implicit minimum width will be the minimum width the layout can
109 have without any of its items shrinking below their minimum width.
110 The implicit minimum width for any other item is \c 0.
111
112 Setting this value to -1 will reset the width back to its implicit minimum width.
113
114
115 \sa preferredWidth
116 \sa maximumWidth
117*/
118void QQuickLayoutAttached::setMinimumWidth(qreal width)
119{
120 if (qt_is_nan(d: width))
121 return;
122 m_isMinimumWidthSet = width >= 0;
123 if (m_minimumWidth == width)
124 return;
125
126 m_minimumWidth = width;
127 invalidateItem();
128 emit minimumWidthChanged();
129}
130
131/*!
132 \qmlattachedproperty real Layout::minimumHeight
133
134 This property holds the minimum height of an item in a layout.
135 The default value is the item's implicit minimum height.
136
137 If the item is a layout, the implicit minimum height will be the minimum height the layout can
138 have without any of its items shrinking below their minimum height.
139 The implicit minimum height for any other item is \c 0.
140
141 Setting this value to -1 will reset the height back to its implicit minimum height.
142
143 \sa preferredHeight
144 \sa maximumHeight
145*/
146void QQuickLayoutAttached::setMinimumHeight(qreal height)
147{
148 if (qt_is_nan(d: height))
149 return;
150 m_isMinimumHeightSet = height >= 0;
151 if (m_minimumHeight == height)
152 return;
153
154 m_minimumHeight = height;
155 invalidateItem();
156 emit minimumHeightChanged();
157}
158
159/*!
160 \qmlattachedproperty real Layout::preferredWidth
161
162 This property holds the preferred width of an item in a layout.
163 If the preferred width is \c -1 it will be ignored, and the layout
164 will use \l{Item::implicitWidth}{implicitWidth} instead.
165 The default is \c -1.
166
167 \sa minimumWidth
168 \sa maximumWidth
169*/
170void QQuickLayoutAttached::setPreferredWidth(qreal width)
171{
172 if (qt_is_nan(d: width) || m_preferredWidth == width)
173 return;
174
175 m_preferredWidth = width;
176 invalidateItem();
177 emit preferredWidthChanged();
178}
179
180/*!
181 \qmlattachedproperty real Layout::preferredHeight
182
183 This property holds the preferred height of an item in a layout.
184 If the preferred height is \c -1 it will be ignored, and the layout
185 will use \l{Item::implicitHeight}{implicitHeight} instead.
186 The default is \c -1.
187
188 \sa minimumHeight
189 \sa maximumHeight
190*/
191void QQuickLayoutAttached::setPreferredHeight(qreal height)
192{
193 if (qt_is_nan(d: height) || m_preferredHeight == height)
194 return;
195
196 m_preferredHeight = height;
197 invalidateItem();
198 emit preferredHeightChanged();
199}
200
201/*!
202 \qmlattachedproperty real Layout::maximumWidth
203
204 This property holds the maximum width of an item in a layout.
205 The default value is the item's implicit maximum width.
206
207 If the item is a layout, the implicit maximum width will be the maximum width the layout can
208 have without any of its items growing beyond their maximum width.
209 The implicit maximum width for any other item is \c Number.POSITIVE_INFINITY.
210
211 Setting this value to \c -1 will reset the width back to its implicit maximum width.
212
213 \sa minimumWidth
214 \sa preferredWidth
215*/
216void QQuickLayoutAttached::setMaximumWidth(qreal width)
217{
218 if (qt_is_nan(d: width))
219 return;
220 m_isMaximumWidthSet = width >= 0;
221 if (m_maximumWidth == width)
222 return;
223
224 m_maximumWidth = width;
225 invalidateItem();
226 emit maximumWidthChanged();
227}
228
229/*!
230 \qmlattachedproperty real Layout::maximumHeight
231
232 The default value is the item's implicit maximum height.
233
234 If the item is a layout, the implicit maximum height will be the maximum height the layout can
235 have without any of its items growing beyond their maximum height.
236 The implicit maximum height for any other item is \c Number.POSITIVE_INFINITY.
237
238 Setting this value to \c -1 will reset the height back to its implicit maximum height.
239
240 \sa minimumHeight
241 \sa preferredHeight
242*/
243void QQuickLayoutAttached::setMaximumHeight(qreal height)
244{
245 if (qt_is_nan(d: height))
246 return;
247 m_isMaximumHeightSet = height >= 0;
248 if (m_maximumHeight == height)
249 return;
250
251 m_maximumHeight = height;
252 invalidateItem();
253 emit maximumHeightChanged();
254}
255
256void QQuickLayoutAttached::setMinimumImplicitSize(const QSizeF &sz)
257{
258 bool emitWidthChanged = false;
259 bool emitHeightChanged = false;
260 if (!m_isMinimumWidthSet && m_minimumWidth != sz.width()) {
261 m_minimumWidth = sz.width();
262 emitWidthChanged = true;
263 }
264 if (!m_isMinimumHeightSet && m_minimumHeight != sz.height()) {
265 m_minimumHeight = sz.height();
266 emitHeightChanged = true;
267 }
268 // Only invalidate the item once, and make sure we emit signal changed after the call to
269 // invalidateItem()
270 if (emitWidthChanged || emitHeightChanged) {
271 invalidateItem();
272 if (emitWidthChanged)
273 emit minimumWidthChanged();
274 if (emitHeightChanged)
275 emit minimumHeightChanged();
276 }
277}
278
279void QQuickLayoutAttached::setMaximumImplicitSize(const QSizeF &sz)
280{
281 bool emitWidthChanged = false;
282 bool emitHeightChanged = false;
283 if (!m_isMaximumWidthSet && m_maximumWidth != sz.width()) {
284 m_maximumWidth = sz.width();
285 emitWidthChanged = true;
286 }
287 if (!m_isMaximumHeightSet && m_maximumHeight != sz.height()) {
288 m_maximumHeight = sz.height();
289 emitHeightChanged = true;
290 }
291 // Only invalidate the item once, and make sure we emit changed signal after the call to
292 // invalidateItem()
293 if (emitWidthChanged || emitHeightChanged) {
294 invalidateItem();
295 if (emitWidthChanged)
296 emit maximumWidthChanged();
297 if (emitHeightChanged)
298 emit maximumHeightChanged();
299 }
300}
301
302/*!
303 \qmlattachedproperty bool Layout::fillWidth
304
305 If this property is \c true, the item will be as wide as possible while respecting
306 the given constraints. If the property is \c false, the item will have a fixed width
307 set to the preferred width.
308 The default depends on implicit (built-in) size policy of item.
309
310 \sa fillHeight
311*/
312void QQuickLayoutAttached::setFillWidth(bool fill)
313{
314 bool oldFillWidth = fillWidth();
315 m_isFillWidthSet = true;
316 m_fillWidth = fill;
317 if (oldFillWidth != fill) {
318 invalidateItem();
319 emit fillWidthChanged();
320 }
321}
322
323/*!
324 \qmlattachedproperty bool Layout::fillHeight
325
326 If this property is \c true, the item will be as tall as possible while respecting
327 the given constraints. If the property is \c false, the item will have a fixed height
328 set to the preferred height.
329 The default depends on implicit (built-in) size policy of the item.
330
331 \sa fillWidth
332*/
333void QQuickLayoutAttached::setFillHeight(bool fill)
334{
335 bool oldFillHeight = fillHeight();
336 m_isFillHeightSet = true;
337 m_fillHeight = fill;
338 if (oldFillHeight != fill) {
339 invalidateItem();
340 emit fillHeightChanged();
341 }
342}
343
344/*!
345 \qmlattachedproperty enumeration Layout::useDefaultSizePolicy
346 \since 6.8
347
348 This property allows the user to configure the layout size policy at the component
349 level.
350
351 The default value will be inherited by querying the application attribute
352 \l Qt::AA_QtQuickUseDefaultSizePolicy. You can use this property to override that value.
353
354 \value Layout.SizePolicyImplicit
355 The item in the layout uses implicit or built-in size policy
356 \value Layout.SizePolicyExplicit
357 The item in the layout doesn't use implicit size policies.
358*/
359void QQuickLayoutAttached::setUseDefaultSizePolicy(QQuickLayout::SizePolicy sizePolicy)
360{
361 m_isUseDefaultSizePolicySet = true;
362 if (m_useDefaultSizePolicy != sizePolicy) {
363 m_useDefaultSizePolicy = sizePolicy;
364 emit useDefaultSizePolicyChanged();
365 }
366}
367
368/*!
369 \qmlattachedproperty int Layout::row
370
371 This property allows you to specify the row position of an item in a \l GridLayout.
372
373 If both \l column and this property are not set, it is up to the layout to assign a cell to the item.
374
375 The default value is \c 0.
376
377 \sa column
378 \sa rowSpan
379*/
380void QQuickLayoutAttached::setRow(int row)
381{
382 if (row >= 0 && row != m_row) {
383 m_row = row;
384 invalidateItem();
385 emit rowChanged();
386 }
387}
388
389/*!
390 \qmlattachedproperty int Layout::column
391
392 This property allows you to specify the column position of an item in a \l GridLayout.
393
394 If both \l row and this property are not set, it is up to the layout to assign a cell to the item.
395
396 The default value is \c 0.
397
398 \sa row
399 \sa columnSpan
400*/
401void QQuickLayoutAttached::setColumn(int column)
402{
403 if (column >= 0 && column != m_column) {
404 m_column = column;
405 invalidateItem();
406 emit columnChanged();
407 }
408}
409
410
411/*!
412 \qmlattachedproperty Qt.Alignment Layout::alignment
413
414 This property allows you to specify the alignment of an item within the cell(s) it occupies.
415
416 The default value is \c 0, which means it will be \c{Qt.AlignVCenter | Qt.AlignLeft}.
417 These defaults also apply if only a horizontal or vertical flag is specified:
418 if only a horizontal flag is specified, the default vertical flag will be
419 \c Qt.AlignVCenter, and if only a vertical flag is specified, the default
420 horizontal flag will be \c Qt.AlignLeft.
421
422 A valid alignment is a combination of the following flags:
423 \list
424 \li Qt::AlignLeft
425 \li Qt::AlignHCenter
426 \li Qt::AlignRight
427 \li Qt::AlignTop
428 \li Qt::AlignVCenter
429 \li Qt::AlignBottom
430 \li Qt::AlignBaseline
431 \endlist
432
433*/
434void QQuickLayoutAttached::setAlignment(Qt::Alignment align)
435{
436 m_isAlignmentSet = true;
437 if (align != m_alignment) {
438 m_alignment = align;
439 if (QQuickLayout *layout = parentLayout()) {
440 layout->setAlignment(item: item(), align);
441 invalidateItem();
442 }
443 emit alignmentChanged();
444 }
445}
446
447/*!
448 \qmlattachedproperty int Layout::horizontalStretchFactor
449
450 This property allows you to specify the horizontal stretch factor. By default, two identical
451 items arranged in a linear layout will have the same size, but if the first item has a
452 stretch factor of 1 and the second item has a stretch factor of 2, the first item will \e
453 aim to get 1/3 of the available space, and the second will \e aim to get 2/3 of the available
454 space. Note that, whether they become exactly 1/3 and 2/3 of the available space depends on
455 their size hints. This is because when e.g a horizontal layout is shown in its minimum width
456 all its child items will consequently also have their minimum width.
457
458 Likewise, when a horizontal layout has its preferred width, all child items will have their
459 preferred widths, and when a horizontal layout has its maximum width, all child items will have
460 their maximum widths. This strategy is applied regardless of what the individual stretch
461 factors are. As a consequence of this, stretch factors will only determine the growth rate of
462 child items \e between the preferredWidth and maximumWidth range.
463
464 The default value is \c -1, which means that no stretch factor is applied.
465
466 \note This requires that Layout::fillWidth is set to true
467
468 \since Qt 6.5
469
470 \sa verticalStretchFactor
471*/
472void QQuickLayoutAttached::setHorizontalStretchFactor(int factor)
473{
474 if (factor != m_horizontalStretch) {
475 m_horizontalStretch = factor;
476 if (QQuickLayout *layout = parentLayout()) {
477 layout->setStretchFactor(item: item(), stretchFactor: factor, orient: Qt::Horizontal);
478 invalidateItem();
479 }
480 emit horizontalStretchFactorChanged();
481 }
482}
483
484/*!
485 \qmlattachedproperty int Layout::verticalStretchFactor
486
487 This property allows you to specify the vertical stretch factor. By default, two identical
488 items arranged in a linear layout will have the same size, but if the first item has a
489 stretch factor of 1 and the second item has a stretch factor of 2, the first item will \e
490 aim to get 1/3 of the available space, and the second will \e aim to get 2/3 of the available
491 space. Note that, whether they become exactly 1/3 and 2/3 of the available space depends on
492 their size hints. This is because when e.g a vertical layout is shown in its minimum height
493 all its child items will consequently also have their minimum height.
494
495 Likewise, when a vertical layout has its preferred height, all child items will have their
496 preferred heights, and when a vertical layout has its maximum height, all child items will have
497 their maximum heights. This strategy is applied regardless of what the individual stretch
498 factors are. As a consequence of this, stretch factors will only determine the growth rate of
499 child items \e between the preferredHeight and maximumHeight range.
500
501 The default value is \c -1, which means that no stretch factor is applied.
502
503 \note This requires that Layout::fillHeight is set to true
504
505 \since Qt 6.5
506
507 \sa horizontalStretchFactor
508*/
509void QQuickLayoutAttached::setVerticalStretchFactor(int factor)
510{
511 if (factor != m_verticalStretch) {
512 m_verticalStretch = factor;
513 if (QQuickLayout *layout = parentLayout()) {
514 layout->setStretchFactor(item: item(), stretchFactor: factor, orient: Qt::Vertical);
515 invalidateItem();
516 }
517 emit verticalStretchFactorChanged();
518 }
519}
520
521/*!
522 \qmlattachedproperty real Layout::margins
523
524 Sets the margins outside of an item to all have the same value. The item
525 itself does not evaluate its own margins. It is the parent's responsibility
526 to decide if it wants to evaluate the margins.
527
528 Specifically, margins are only evaluated by ColumnLayout, RowLayout,
529 GridLayout, and other layout-like containers, such as SplitView, where the
530 effective cell size of an item will be increased as the margins are
531 increased.
532
533 Therefore, if an item with margins is a child of another \c Item, its
534 position, size and implicit size will remain unchanged.
535
536 Combining margins with alignment will align the item \e including its
537 margins. For instance, a vertically-centered Item with a top margin of \c 1
538 and a bottom margin of \c 9 will cause the Items effective alignment within
539 the cell to be 4 pixels above the center.
540
541 The default value is \c 0.
542
543 \sa leftMargin
544 \sa topMargin
545 \sa rightMargin
546 \sa bottomMargin
547
548 \since QtQuick.Layouts 1.2
549*/
550void QQuickLayoutAttached::setMargins(qreal m)
551{
552 m_isMarginsSet = true;
553 if (m == m_defaultMargins)
554 return;
555
556 m_defaultMargins = m;
557 invalidateItem();
558 if (!m_isLeftMarginSet && m_margins.left() != m)
559 emit leftMarginChanged();
560 if (!m_isTopMarginSet && m_margins.top() != m)
561 emit topMarginChanged();
562 if (!m_isRightMarginSet && m_margins.right() != m)
563 emit rightMarginChanged();
564 if (!m_isBottomMarginSet && m_margins.bottom() != m)
565 emit bottomMarginChanged();
566 emit marginsChanged();
567}
568
569/*!
570 \qmlattachedproperty real Layout::leftMargin
571
572 Specifies the left margin outside of an item.
573 If the value is not set, it will use the value from \l margins.
574
575 \sa margins
576
577 \since QtQuick.Layouts 1.2
578*/
579void QQuickLayoutAttached::setLeftMargin(qreal m)
580{
581 const bool changed = leftMargin() != m;
582 m_margins.setLeft(m);
583 m_isLeftMarginSet = true;
584 if (changed) {
585 invalidateItem();
586 emit leftMarginChanged();
587 }
588}
589
590void QQuickLayoutAttached::resetLeftMargin()
591{
592 const bool changed = m_isLeftMarginSet && (m_defaultMargins != m_margins.left());
593 m_isLeftMarginSet = false;
594 if (changed) {
595 invalidateItem();
596 emit leftMarginChanged();
597 }
598}
599
600/*!
601 \qmlattachedproperty real Layout::topMargin
602
603 Specifies the top margin outside of an item.
604 If the value is not set, it will use the value from \l margins.
605
606 \sa margins
607
608 \since QtQuick.Layouts 1.2
609*/
610void QQuickLayoutAttached::setTopMargin(qreal m)
611{
612 const bool changed = topMargin() != m;
613 m_margins.setTop(m);
614 m_isTopMarginSet = true;
615 if (changed) {
616 invalidateItem();
617 emit topMarginChanged();
618 }
619}
620
621void QQuickLayoutAttached::resetTopMargin()
622{
623 const bool changed = m_isTopMarginSet && (m_defaultMargins != m_margins.top());
624 m_isTopMarginSet = false;
625 if (changed) {
626 invalidateItem();
627 emit topMarginChanged();
628 }
629}
630
631/*!
632 \qmlattachedproperty real Layout::rightMargin
633
634 Specifies the right margin outside of an item.
635 If the value is not set, it will use the value from \l margins.
636
637 \sa margins
638
639 \since QtQuick.Layouts 1.2
640*/
641void QQuickLayoutAttached::setRightMargin(qreal m)
642{
643 const bool changed = rightMargin() != m;
644 m_margins.setRight(m);
645 m_isRightMarginSet = true;
646 if (changed) {
647 invalidateItem();
648 emit rightMarginChanged();
649 }
650}
651
652void QQuickLayoutAttached::resetRightMargin()
653{
654 const bool changed = m_isRightMarginSet && (m_defaultMargins != m_margins.right());
655 m_isRightMarginSet = false;
656 if (changed) {
657 invalidateItem();
658 emit rightMarginChanged();
659 }
660}
661
662/*!
663 \qmlattachedproperty real Layout::bottomMargin
664
665 Specifies the bottom margin outside of an item.
666 If the value is not set, it will use the value from \l margins.
667
668 \sa margins
669
670 \since QtQuick.Layouts 1.2
671*/
672void QQuickLayoutAttached::setBottomMargin(qreal m)
673{
674 const bool changed = bottomMargin() != m;
675 m_margins.setBottom(m);
676 m_isBottomMarginSet = true;
677 if (changed) {
678 invalidateItem();
679 emit bottomMarginChanged();
680 }
681}
682
683void QQuickLayoutAttached::resetBottomMargin()
684{
685 const bool changed = m_isBottomMarginSet && (m_defaultMargins != m_margins.bottom());
686 m_isBottomMarginSet = false;
687 if (changed) {
688 invalidateItem();
689 emit bottomMarginChanged();
690 }
691}
692
693
694/*!
695 \qmlattachedproperty int Layout::rowSpan
696
697 This property allows you to specify the row span of an item in a \l GridLayout.
698
699 The default value is \c 1.
700
701 \sa columnSpan
702 \sa row
703*/
704void QQuickLayoutAttached::setRowSpan(int span)
705{
706 if (span != m_rowSpan) {
707 m_rowSpan = span;
708 invalidateItem();
709 emit rowSpanChanged();
710 }
711}
712
713
714/*!
715 \qmlattachedproperty int Layout::columnSpan
716
717 This property allows you to specify the column span of an item in a \l GridLayout.
718
719 The default value is \c 1.
720
721 \sa rowSpan
722 \sa column
723*/
724void QQuickLayoutAttached::setColumnSpan(int span)
725{
726 if (span != m_columnSpan) {
727 m_columnSpan = span;
728 invalidateItem();
729 emit columnSpanChanged();
730 }
731}
732
733
734qreal QQuickLayoutAttached::sizeHint(Qt::SizeHint which, Qt::Orientation orientation) const
735{
736 qreal result = 0;
737 if (QQuickLayout *layout = qobject_cast<QQuickLayout *>(object: item())) {
738 const QSizeF sz = layout->sizeHint(whichSizeHint: which);
739 result = (orientation == Qt::Horizontal ? sz.width() : sz.height());
740 } else {
741 if (which == Qt::MaximumSize)
742 result = std::numeric_limits<qreal>::infinity();
743 }
744 return result;
745}
746
747void QQuickLayoutAttached::invalidateItem()
748{
749 qCDebug(lcQuickLayouts) << "QQuickLayoutAttached::invalidateItem";
750 if (QQuickLayout *layout = parentLayout()) {
751 layout->invalidate(childItem: item());
752 }
753}
754
755QQuickLayout *QQuickLayoutAttached::parentLayout() const
756{
757 QQuickItem *parentItem = item();
758 if (parentItem) {
759 parentItem = parentItem->parentItem();
760 return qobject_cast<QQuickLayout *>(object: parentItem);
761 } else {
762 qmlWarning(me: parent()) << "Layout attached property must be attached to an object deriving from Item";
763 }
764 return nullptr;
765}
766
767QQuickItem *QQuickLayoutAttached::item() const
768{
769 return qobject_cast<QQuickItem *>(o: parent());
770}
771
772void QQuickLayoutPrivate::applySizeHints() const
773{
774 Q_Q(const QQuickLayout);
775
776 QQuickLayout *that = const_cast<QQuickLayout*>(q);
777 QQuickLayoutAttached *info = attachedLayoutObject(item: that, create: true);
778
779 const QSizeF min = q->sizeHint(whichSizeHint: Qt::MinimumSize);
780 const QSizeF max = q->sizeHint(whichSizeHint: Qt::MaximumSize);
781 const QSizeF pref = q->sizeHint(whichSizeHint: Qt::PreferredSize);
782 info->setMinimumImplicitSize(min);
783 info->setMaximumImplicitSize(max);
784 that->setImplicitSize(pref.width(), pref.height());
785}
786
787QQuickLayout::QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent)
788 : QQuickItem(dd, parent)
789 , m_inUpdatePolish(false)
790 , m_polishInsideUpdatePolish(0)
791{
792}
793
794static QQuickItemPrivate::ChangeTypes changeTypes =
795 QQuickItemPrivate::SiblingOrder
796 | QQuickItemPrivate::ImplicitWidth
797 | QQuickItemPrivate::ImplicitHeight
798 | QQuickItemPrivate::Destroyed
799 | QQuickItemPrivate::Visibility;
800
801QQuickLayout::~QQuickLayout()
802{
803 d_func()->m_isReady = false;
804
805 const auto childItems = d_func()->childItems;
806 for (QQuickItem *child : childItems)
807 QQuickItemPrivate::get(item: child)->removeItemChangeListener(this, types: changeTypes);
808}
809
810QQuickLayoutAttached *QQuickLayout::qmlAttachedProperties(QObject *object)
811{
812 return new QQuickLayoutAttached(object);
813}
814
815void QQuickLayout::updatePolish()
816{
817 qCDebug(lcQuickLayouts) << "updatePolish() ENTERING" << this;
818 m_inUpdatePolish = true;
819
820 // Might have become "undirty" before we reach this updatePolish()
821 // (e.g. if somebody queried for implicitWidth it will immediately
822 // calculate size hints)
823 // Note that we need to call ensureLayoutItemsUpdated() *before* we query width() and height(),
824 // because width()/height() might return their implicitWidth/implicitHeight (e.g. for a layout
825 // with no explicitly specified size, (nor anchors.fill: parent))
826 ensureLayoutItemsUpdated(options: QQuickLayout::ApplySizeHints | QQuickLayout::Recursive);
827 rearrange(QSizeF(width(), height()));
828 m_inUpdatePolish = false;
829 qCDebug(lcQuickLayouts) << "updatePolish() LEAVING" << this;
830}
831
832void QQuickLayout::componentComplete()
833{
834 Q_D(QQuickLayout);
835 d->m_disableRearrange = true;
836 QQuickItem::componentComplete(); // will call our geometryChange(), (where isComponentComplete() == true)
837 d->m_disableRearrange = false;
838 d->m_isReady = true;
839}
840
841void QQuickLayout::maybeSubscribeToBaseLineOffsetChanges(QQuickItem *item)
842{
843 QQuickLayoutAttached *info = attachedLayoutObject(item, create: false);
844 if (info) {
845 if (info->alignment() == Qt::AlignBaseline && static_cast<QQuickLayout*>(item->parentItem()) == this) {
846 qmlobject_connect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem()));
847 } else {
848 qmlobject_disconnect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem()));
849 }
850 }
851}
852
853void QQuickLayout::invalidate(QQuickItem * /*childItem*/)
854{
855 Q_D(QQuickLayout);
856 if (invalidated())
857 return;
858
859 qCDebug(lcQuickLayouts) << "QQuickLayout::invalidate()" << this;
860 d->m_dirty = true;
861 d->m_dirtyArrangement = true;
862
863 if (!qobject_cast<QQuickLayout *>(object: parentItem())) {
864 polish();
865
866 if (m_inUpdatePolish) {
867 if (++m_polishInsideUpdatePolish > 2)
868 // allow at most two consecutive loops in order to respond to height-for-width
869 // (e.g QQuickText changes implicitHeight when its width gets changed)
870 qCDebug(lcQuickLayouts) << "Layout polish loop detected for " << this
871 << ". The polish request will still be scheduled.";
872 } else {
873 m_polishInsideUpdatePolish = 0;
874 }
875 }
876}
877
878bool QQuickLayout::shouldIgnoreItem(QQuickItem *child) const
879{
880 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(item: child);
881 bool ignoreItem = !childPrivate->explicitVisible;
882 if (!ignoreItem && childPrivate->isTransparentForPositioner())
883 ignoreItem = true;
884 return ignoreItem;
885}
886
887void QQuickLayout::checkAnchors(QQuickItem *item) const
888{
889 QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
890 if (anchors && anchors->activeDirections())
891 qmlWarning(me: item) << "Detected anchors on an item that is managed by a layout. This is undefined behavior; use Layout.alignment instead.";
892}
893
894void QQuickLayout::ensureLayoutItemsUpdated(EnsureLayoutItemsUpdatedOptions options) const
895{
896 Q_D(const QQuickLayout);
897 if (!invalidated())
898 return;
899 qCDebug(lcQuickLayouts) << "ENTER QQuickLayout::ensureLayoutItemsUpdated()" << this << options;
900 QQuickLayoutPrivate *priv = const_cast<QQuickLayoutPrivate*>(d);
901
902 // breadth-first
903 // must update the root first, and continue towards the leaf nodes.
904 // Otherwise, we wouldn't know which children to traverse to
905 const_cast<QQuickLayout*>(this)->updateLayoutItems();
906
907 // make invalidate() return true
908 d->m_dirty = false;
909
910 if (options & Recursive) {
911 for (int i = 0; i < itemCount(); ++i) {
912 QQuickItem *itm = itemAt(index: i);
913 if (QQuickLayout *lay = qobject_cast<QQuickLayout*>(object: itm)) {
914 lay->ensureLayoutItemsUpdated(options);
915 }
916 }
917 }
918
919 // size hints are updated depth-first (parent size hints depends on their childrens size hints)
920 if (options & ApplySizeHints)
921 priv->applySizeHints();
922 qCDebug(lcQuickLayouts) << "LEAVE QQuickLayout::ensureLayoutItemsUpdated()" << this;
923}
924
925
926void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value)
927{
928 if (change == ItemChildAddedChange) {
929 Q_D(QQuickLayout);
930 QQuickItem *item = value.item;
931 maybeSubscribeToBaseLineOffsetChanges(item);
932 QQuickItemPrivate::get(item)->addItemChangeListener(listener: this, types: changeTypes);
933 d->m_hasItemChangeListeners = true;
934 qCDebug(lcQuickLayouts) << "ChildAdded" << item;
935 if (isReady())
936 invalidate();
937 } else if (change == ItemChildRemovedChange) {
938 QQuickItem *item = value.item;
939 maybeSubscribeToBaseLineOffsetChanges(item);
940 QQuickItemPrivate::get(item)->removeItemChangeListener(this, types: changeTypes);
941 qCDebug(lcQuickLayouts) << "ChildRemoved" << item;
942 if (isReady())
943 invalidate();
944 }
945 QQuickItem::itemChange(change, value);
946}
947
948void QQuickLayout::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
949{
950 Q_D(QQuickLayout);
951 qCDebug(lcQuickLayouts) << "QQuickLayout::geometryChange"
952 << oldGeometry << "-->" << newGeometry;
953
954 QQuickItem::geometryChange(newGeometry, oldGeometry);
955
956 if ((invalidated() && !qobject_cast<QQuickLayout *>(object: parentItem())) ||
957 d->m_disableRearrange || !isReady())
958 return;
959
960 // The geometryChange call above might recursively update the
961 // geometry of this layout, via item change listeners, in which
962 // case the recursive call has already rearranged the layout for
963 // the new size. We don't want to rearrange here based on the old
964 // 'new geometry', as that would revert the most up to date layout.
965 const qreal w = d->width.valueBypassingBindings();
966 const qreal h = d->height.valueBypassingBindings();
967 const QSizeF currentSize(w, h);
968 if (currentSize != newGeometry.size()) {
969 qCDebug(lcQuickLayouts) << "QQuickItem::geometryChange resulted"
970 << "in size change from" << newGeometry.size() << "to"
971 << currentSize << "; layout should already be up to date.";
972 return;
973 }
974
975 rearrange(newGeometry.size());
976}
977
978void QQuickLayout::invalidateSenderItem()
979{
980 if (!isReady())
981 return;
982 QQuickItem *item = static_cast<QQuickItem *>(sender());
983 Q_ASSERT(item);
984 invalidate(item);
985}
986
987bool QQuickLayout::isReady() const
988{
989 return d_func()->m_isReady;
990}
991
992/*!
993 * \brief QQuickLayout::deactivateRecur
994 * \internal
995 *
996 * Call this from the dtor of the top-level layout.
997 * Otherwise, it will trigger lots of unneeded item change listeners (itemVisibleChanged()) for all its descendants
998 * that will have its impact thrown away.
999 */
1000void QQuickLayout::deactivateRecur()
1001{
1002 if (d_func()->m_hasItemChangeListeners) {
1003 ensureLayoutItemsUpdated();
1004 for (int i = 0; i < itemCount(); ++i) {
1005 QQuickItem *item = itemAt(index: i);
1006 // When deleting a layout with children, there is no reason for the children to inform the layout that their
1007 // e.g. visibility got changed. The layout already knows that all its children will eventually become invisible, so
1008 // we therefore remove its change listener.
1009 QQuickItemPrivate::get(item)->removeItemChangeListener(this, types: changeTypes);
1010 if (QQuickLayout *layout = qobject_cast<QQuickLayout*>(object: item))
1011 layout->deactivateRecur();
1012 }
1013 d_func()->m_hasItemChangeListeners = false;
1014 }
1015}
1016
1017bool QQuickLayout::invalidated() const
1018{
1019 return d_func()->m_dirty;
1020}
1021
1022bool QQuickLayout::invalidatedArrangement() const
1023{
1024 return d_func()->m_dirtyArrangement;
1025}
1026
1027bool QQuickLayout::isMirrored() const
1028{
1029 return d_func()->isMirrored();
1030}
1031
1032void QQuickLayout::itemSiblingOrderChanged(QQuickItem *item)
1033{
1034 Q_UNUSED(item);
1035 invalidate();
1036}
1037
1038void QQuickLayout::itemImplicitWidthChanged(QQuickItem *item)
1039{
1040 if (!isReady() || item->signalsBlocked())
1041 return;
1042 invalidate(item);
1043}
1044
1045void QQuickLayout::itemImplicitHeightChanged(QQuickItem *item)
1046{
1047 if (!isReady() || item->signalsBlocked())
1048 return;
1049 invalidate(item);
1050}
1051
1052void QQuickLayout::itemDestroyed(QQuickItem *item)
1053{
1054 Q_UNUSED(item);
1055}
1056
1057void QQuickLayout::itemVisibilityChanged(QQuickItem *item)
1058{
1059 Q_UNUSED(item);
1060}
1061
1062void QQuickLayout::rearrange(const QSizeF &/*size*/)
1063{
1064 d_func()->m_dirtyArrangement = false;
1065}
1066
1067
1068/*
1069 The layout engine assumes:
1070 1. minimum <= preferred <= maximum
1071 2. descent is within minimum and maximum bounds (### verify)
1072
1073 This function helps to ensure that by the following rules (in the following order):
1074 1. If minimum > maximum, set minimum = maximum
1075 2. Clamp preferred to be between the [minimum,maximum] range.
1076 3. If descent > minimum, set descent = minimum (### verify if this is correct, it might
1077 need some refinements to multiline texts)
1078
1079 If any values are "not set" (i.e. negative), they will be left untouched, so that we
1080 know which values needs to be fetched from the implicit hints (not user hints).
1081 */
1082static void normalizeHints(qreal &minimum, qreal &preferred, qreal &maximum, qreal &descent)
1083{
1084 if (minimum >= 0 && maximum >= 0 && minimum > maximum)
1085 minimum = maximum;
1086
1087 if (preferred >= 0) {
1088 if (minimum >= 0 && preferred < minimum) {
1089 preferred = minimum;
1090 } else if (maximum >= 0 && preferred > maximum) {
1091 preferred = maximum;
1092 }
1093 }
1094
1095 if (minimum >= 0 && descent > minimum)
1096 descent = minimum;
1097}
1098
1099static void boundSize(QSizeF &result, const QSizeF &size)
1100{
1101 if (size.width() >= 0 && size.width() < result.width())
1102 result.setWidth(size.width());
1103 if (size.height() >= 0 && size.height() < result.height())
1104 result.setHeight(size.height());
1105}
1106
1107static void expandSize(QSizeF &result, const QSizeF &size)
1108{
1109 if (size.width() >= 0 && size.width() > result.width())
1110 result.setWidth(size.width());
1111 if (size.height() >= 0 && size.height() > result.height())
1112 result.setHeight(size.height());
1113}
1114
1115static inline void combineHints(qreal &current, qreal fallbackHint)
1116{
1117 if (current < 0)
1118 current = fallbackHint;
1119}
1120
1121static inline void combineSize(QSizeF &result, const QSizeF &fallbackSize)
1122{
1123 combineHints(current&: result.rwidth(), fallbackHint: fallbackSize.width());
1124 combineHints(current&: result.rheight(), fallbackHint: fallbackSize.height());
1125}
1126
1127static inline void combineImplicitHints(QQuickLayoutAttached *info, Qt::SizeHint which, QSizeF *size)
1128{
1129 if (!info) return;
1130
1131 Q_ASSERT(which == Qt::MinimumSize || which == Qt::MaximumSize);
1132
1133 const QSizeF constraint(which == Qt::MinimumSize
1134 ? QSizeF(info->minimumWidth(), info->minimumHeight())
1135 : QSizeF(info->maximumWidth(), info->maximumHeight()));
1136
1137 if (!info->isExtentExplicitlySet(o: Qt::Horizontal, whichSize: which))
1138 combineHints(current&: size->rwidth(), fallbackHint: constraint.width());
1139 if (!info->isExtentExplicitlySet(o: Qt::Vertical, whichSize: which))
1140 combineHints(current&: size->rheight(), fallbackHint: constraint.height());
1141}
1142
1143typedef qreal (QQuickLayoutAttached::*SizeGetter)() const;
1144
1145/*!
1146 \internal
1147 Note: Can potentially return the attached QQuickLayoutAttached object through \a attachedInfo.
1148
1149 It is like this is because it enables it to be reused.
1150
1151 The goal of this function is to return the effective minimum, preferred and maximum size hints
1152 that the layout will use for this item.
1153 This function takes care of gathering all explicitly set size hints, normalizes them so
1154 that min < pref < max.
1155 Further, the hints _not_explicitly_ set will then be initialized with the implicit size hints,
1156 which is usually derived from the content of the layouts (or items).
1157
1158 The following table illustrates the preference of the properties used for measuring layout
1159 items. If present, the USER properties will be preferred. If USER properties are not present,
1160 the HINT properties will be preferred. Finally, the FALLBACK properties will be used as an
1161 ultimate fallback.
1162
1163 Note that one can query if the value of Layout.minimumWidth or Layout.maximumWidth has been
1164 explicitly or implicitly set with QQuickLayoutAttached::isExtentExplicitlySet(). This
1165 determines if it should be used as a USER or as a HINT value.
1166
1167 Fractional size hints will be ceiled to the closest integer. This is in order to give some
1168 slack when the items are snapped to the pixel grid.
1169
1170 | *Minimum* | *Preferred* | *Maximum* |
1171+----------------+----------------------+-----------------------+--------------------------+
1172|USER (explicit) | Layout.minimumWidth | Layout.preferredWidth | Layout.maximumWidth |
1173|HINT (implicit) | Layout.minimumWidth | implicitWidth | Layout.maximumWidth |
1174|FALLBACK | 0 | width | Number.POSITIVE_INFINITY |
1175+----------------+----------------------+-----------------------+--------------------------+
1176 */
1177void QQuickLayout::effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **attachedInfo, bool useFallbackToWidthOrHeight)
1178{
1179 for (int i = 0; i < Qt::NSizeHints; ++i)
1180 cachedSizeHints[i] = QSizeF();
1181 QQuickLayoutAttached *info = attachedLayoutObject(item, create: false);
1182 // First, retrieve the user-specified hints from the attached "Layout." properties
1183 if (info) {
1184 struct Getters {
1185 SizeGetter call[NSizes];
1186 };
1187
1188 static Getters horGetters = {
1189 .call: {&QQuickLayoutAttached::minimumWidth, &QQuickLayoutAttached::preferredWidth, &QQuickLayoutAttached::maximumWidth},
1190 };
1191
1192 static Getters verGetters = {
1193 .call: {&QQuickLayoutAttached::minimumHeight, &QQuickLayoutAttached::preferredHeight, &QQuickLayoutAttached::maximumHeight}
1194 };
1195 for (int i = 0; i < NSizes; ++i) {
1196 SizeGetter getter = horGetters.call[i];
1197 Q_ASSERT(getter);
1198
1199 if (info->isExtentExplicitlySet(o: Qt::Horizontal, whichSize: (Qt::SizeHint)i))
1200 cachedSizeHints[i].setWidth((info->*getter)());
1201
1202 getter = verGetters.call[i];
1203 Q_ASSERT(getter);
1204 if (info->isExtentExplicitlySet(o: Qt::Vertical, whichSize: (Qt::SizeHint)i))
1205 cachedSizeHints[i].setHeight((info->*getter)());
1206 }
1207 }
1208
1209 QSizeF &minS = cachedSizeHints[Qt::MinimumSize];
1210 QSizeF &prefS = cachedSizeHints[Qt::PreferredSize];
1211 QSizeF &maxS = cachedSizeHints[Qt::MaximumSize];
1212 QSizeF &descentS = cachedSizeHints[Qt::MinimumDescent];
1213
1214 // For instance, will normalize the following user-set hints
1215 // from: [10, 5, 60]
1216 // to: [10, 10, 60]
1217 normalizeHints(minimum&: minS.rwidth(), preferred&: prefS.rwidth(), maximum&: maxS.rwidth(), descent&: descentS.rwidth());
1218 normalizeHints(minimum&: minS.rheight(), preferred&: prefS.rheight(), maximum&: maxS.rheight(), descent&: descentS.rheight());
1219
1220 // All explicit values gathered, now continue to gather the implicit sizes
1221
1222 //--- GATHER MAXIMUM SIZE HINTS ---
1223 combineImplicitHints(info, which: Qt::MaximumSize, size: &maxS);
1224 combineSize(result&: maxS, fallbackSize: QSizeF(std::numeric_limits<qreal>::infinity(), std::numeric_limits<qreal>::infinity()));
1225 // implicit max or min sizes should not limit an explicitly set preferred size
1226 expandSize(result&: maxS, size: prefS);
1227 expandSize(result&: maxS, size: minS);
1228
1229 //--- GATHER MINIMUM SIZE HINTS ---
1230 combineImplicitHints(info, which: Qt::MinimumSize, size: &minS);
1231 expandSize(result&: minS, size: QSizeF(0,0));
1232 boundSize(result&: minS, size: prefS);
1233 boundSize(result&: minS, size: maxS);
1234
1235 //--- GATHER PREFERRED SIZE HINTS ---
1236 // First, from implicitWidth/Height
1237 qreal &prefWidth = prefS.rwidth();
1238 qreal &prefHeight = prefS.rheight();
1239 if (prefWidth < 0 && item->implicitWidth() > 0)
1240 prefWidth = qCeil(v: item->implicitWidth());
1241 if (prefHeight < 0 && item->implicitHeight() > 0)
1242 prefHeight = qCeil(v: item->implicitHeight());
1243
1244 // If that fails, make an ultimate fallback to width/height
1245 if (useFallbackToWidthOrHeight && !prefS.isValid()) {
1246 /* If we want to support using width/height as preferred size hints in
1247 layouts, (which we think most people expect), we only want to use the
1248 initial width.
1249 This is because the width will change due to layout rearrangement,
1250 and the preferred width should return the same value, regardless of
1251 the current width.
1252 We therefore store this initial width in the attached layout object
1253 and reuse it if needed rather than querying the width another time.
1254 That means we need to ensure that an Layout attached object is available
1255 by creating one if necessary.
1256 */
1257 if (!info)
1258 info = attachedLayoutObject(item);
1259
1260 auto updatePreferredSizes = [](qreal &cachedSize, qreal &attachedSize, qreal size) {
1261 if (cachedSize < 0) {
1262 if (attachedSize < 0)
1263 attachedSize = size;
1264
1265 cachedSize = attachedSize;
1266 }
1267 };
1268 updatePreferredSizes(prefWidth, info->m_fallbackWidth, item->width());
1269 updatePreferredSizes(prefHeight, info->m_fallbackHeight, item->height());
1270 }
1271
1272 // Normalize again after the implicit hints have been gathered
1273 expandSize(result&: prefS, size: minS);
1274 boundSize(result&: prefS, size: maxS);
1275
1276 //--- GATHER DESCENT
1277 // Minimum descent is only applicable for the effective minimum height,
1278 // so we gather the descent last.
1279 const qreal minimumDescent = minS.height() - item->baselineOffset();
1280 descentS.setHeight(minimumDescent);
1281
1282 if (info) {
1283 QMarginsF margins = info->qMargins();
1284 QSizeF extraMargins(margins.left() + margins.right(), margins.top() + margins.bottom());
1285 minS += extraMargins;
1286 prefS += extraMargins;
1287 maxS += extraMargins;
1288 descentS += extraMargins;
1289 }
1290 if (attachedInfo)
1291 *attachedInfo = info;
1292}
1293
1294/*!
1295 \internal
1296
1297 Assumes \a info is set (if the object has an attached property)
1298 */
1299QLayoutPolicy::Policy QQuickLayout::effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info)
1300{
1301 QLayoutPolicy::Policy pol{QLayoutPolicy::Fixed};
1302 bool isSet = false;
1303 if (info) {
1304 if (orientation == Qt::Horizontal) {
1305 isSet = info->isFillWidthSet();
1306 if (isSet && info->fillWidth())
1307 pol = QLayoutPolicy::Preferred;
1308 } else {
1309 isSet = info->isFillHeightSet();
1310 if (isSet && info->fillHeight())
1311 pol = QLayoutPolicy::Preferred;
1312 }
1313 }
1314 if (!isSet && item) {
1315 auto effectiveUseDefaultSizePolicy = [info]() {
1316 return info ? info->useDefaultSizePolicy() == QQuickLayout::SizePolicyImplicit
1317 : QGuiApplication::testAttribute(attribute: Qt::AA_QtQuickUseDefaultSizePolicy);
1318 };
1319 if (qobject_cast<QQuickLayout*>(object: item)) {
1320 pol = QLayoutPolicy::Preferred;
1321 } else if (effectiveUseDefaultSizePolicy()) {
1322 QLayoutPolicy sizePolicy = QQuickItemPrivate::get(item)->sizePolicy();
1323 pol = (orientation == Qt::Horizontal) ? sizePolicy.horizontalPolicy() : sizePolicy.verticalPolicy();
1324 }
1325 }
1326
1327 return pol;
1328}
1329
1330void QQuickLayout::_q_dumpLayoutTree() const
1331{
1332 QString buf;
1333 dumpLayoutTreeRecursive(level: 0, buf);
1334 qDebug(msg: "\n%s", qPrintable(buf));
1335}
1336
1337void QQuickLayout::dumpLayoutTreeRecursive(int level, QString &buf) const
1338{
1339 auto formatLine = [&level](const char *fmt) -> QString {
1340 QString ss(level *4, QLatin1Char(' '));
1341 return ss + QLatin1String(fmt) + QLatin1Char('\n');
1342 };
1343
1344 auto f2s = [](qreal f) {
1345 return QString::number(f);
1346 };
1347 auto b2s = [](bool b) {
1348 static const char *strBool[] = {"false", "true"};
1349 return QLatin1String(strBool[int(b)]);
1350 };
1351
1352 buf += formatLine("%1 {").arg(a: QQmlMetaType::prettyTypeName(object: this));
1353 ++level;
1354 buf += formatLine("// Effective calculated values:");
1355 buf += formatLine("sizeHintDirty: %2").arg(a: invalidated());
1356 QSizeF min = sizeHint(whichSizeHint: Qt::MinimumSize);
1357 buf += formatLine("sizeHint.min : [%1, %2]").arg(a: f2s(min.width()), fieldWidth: 5).arg(a: min.height(), fieldWidth: 5);
1358 QSizeF pref = sizeHint(whichSizeHint: Qt::PreferredSize);
1359 buf += formatLine("sizeHint.pref: [%1, %2]").arg(a: pref.width(), fieldWidth: 5).arg(a: pref.height(), fieldWidth: 5);
1360 QSizeF max = sizeHint(whichSizeHint: Qt::MaximumSize);
1361 buf += formatLine("sizeHint.max : [%1, %2]").arg(a: f2s(max.width()), fieldWidth: 5).arg(a: f2s(max.height()), fieldWidth: 5);
1362
1363 for (QQuickItem *item : childItems()) {
1364 buf += QLatin1Char('\n');
1365 if (QQuickLayout *childLayout = qobject_cast<QQuickLayout*>(object: item)) {
1366 childLayout->dumpLayoutTreeRecursive(level, buf);
1367 } else {
1368 buf += formatLine("%1 {").arg(a: QQmlMetaType::prettyTypeName(object: item));
1369 ++level;
1370 if (item->implicitWidth() > 0)
1371 buf += formatLine("implicitWidth: %1").arg(a: f2s(item->implicitWidth()));
1372 if (item->implicitHeight() > 0)
1373 buf += formatLine("implicitHeight: %1").arg(a: f2s(item->implicitHeight()));
1374 QSizeF min;
1375 QSizeF pref;
1376 QSizeF max;
1377 QQuickLayoutAttached *info = attachedLayoutObject(item, create: false);
1378 if (info) {
1379 min = QSizeF(info->minimumWidth(), info->minimumHeight());
1380 pref = QSizeF(info->preferredWidth(), info->preferredHeight());
1381 max = QSizeF(info->maximumWidth(), info->maximumHeight());
1382 if (info->isExtentExplicitlySet(o: Qt::Horizontal, whichSize: Qt::MinimumSize))
1383 buf += formatLine("Layout.minimumWidth: %1").arg(a: f2s(min.width()));
1384 if (info->isExtentExplicitlySet(o: Qt::Vertical, whichSize: Qt::MinimumSize))
1385 buf += formatLine("Layout.minimumHeight: %1").arg(a: f2s(min.height()));
1386 if (pref.width() >= 0)
1387 buf += formatLine("Layout.preferredWidth: %1").arg(a: f2s(pref.width()));
1388 if (pref.height() >= 0)
1389 buf += formatLine("Layout.preferredHeight: %1").arg(a: f2s(pref.height()));
1390 if (info->isExtentExplicitlySet(o: Qt::Horizontal, whichSize: Qt::MaximumSize))
1391 buf += formatLine("Layout.maximumWidth: %1").arg(a: f2s(max.width()));
1392 if (info->isExtentExplicitlySet(o: Qt::Vertical, whichSize: Qt::MaximumSize))
1393 buf += formatLine("Layout.maximumHeight: %1").arg(a: f2s(max.height()));
1394
1395 if (info->isFillWidthSet())
1396 buf += formatLine("Layout.fillWidth: %1").arg(a: b2s(info->fillWidth()));
1397 if (info->isFillHeightSet())
1398 buf += formatLine("Layout.fillHeight: %1").arg(a: b2s(info->fillHeight()));
1399 }
1400 --level;
1401 buf += formatLine("}");
1402 }
1403 }
1404 --level;
1405 buf += formatLine("}");
1406}
1407
1408QT_END_NAMESPACE
1409
1410#include "moc_qquicklayout_p.cpp"
1411

source code of qtdeclarative/src/quicklayouts/qquicklayout.cpp