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

source code of qtdeclarative/src/imports/layouts/qquicklayout.cpp