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 "qquicklinearlayout_p.h"
5#include "qquickgridlayoutengine_p.h"
6#include "qquicklayoutstyleinfo_p.h"
7#include <QtCore/private/qnumeric_p.h>
8#include <QtQml/qqmlinfo.h>
9#include "qdebug.h"
10#include <limits>
11
12/*!
13 \qmltype RowLayout
14 //! \instantiates QQuickRowLayout
15 \inherits Item
16 \inqmlmodule QtQuick.Layouts
17 \ingroup layouts
18 \brief Identical to \l GridLayout, but having only one row.
19
20 To be able to use this type more efficiently, it is recommended that you
21 understand the general mechanism of the Qt Quick Layouts module. Refer to
22 \l{Qt Quick Layouts Overview} for more information.
23
24 It is available as a convenience for developers, as it offers a cleaner API.
25
26 Items in a RowLayout support these attached properties:
27 \list
28 \input layout.qdocinc attached-properties
29 \endlist
30
31 \image rowlayout.png
32
33 \code
34 RowLayout {
35 id: layout
36 anchors.fill: parent
37 spacing: 6
38 Rectangle {
39 color: 'teal'
40 Layout.fillWidth: true
41 Layout.minimumWidth: 50
42 Layout.preferredWidth: 100
43 Layout.maximumWidth: 300
44 Layout.minimumHeight: 150
45 Text {
46 anchors.centerIn: parent
47 text: parent.width + 'x' + parent.height
48 }
49 }
50 Rectangle {
51 color: 'plum'
52 Layout.fillWidth: true
53 Layout.minimumWidth: 100
54 Layout.preferredWidth: 200
55 Layout.preferredHeight: 100
56 Text {
57 anchors.centerIn: parent
58 text: parent.width + 'x' + parent.height
59 }
60 }
61 }
62 \endcode
63
64 Read more about attached properties \l{QML Object Attributes}{here}.
65 \sa ColumnLayout
66 \sa GridLayout
67 \sa StackLayout
68 \sa Row
69 \sa {Qt Quick Layouts Overview}
70*/
71
72/*!
73 \qmltype ColumnLayout
74 //! \instantiates QQuickColumnLayout
75 \inherits Item
76 \inqmlmodule QtQuick.Layouts
77 \ingroup layouts
78 \brief Identical to \l GridLayout, but having only one column.
79
80 To be able to use this type more efficiently, it is recommended that you
81 understand the general mechanism of the Qt Quick Layouts module. Refer to
82 \l{Qt Quick Layouts Overview} for more information.
83
84 It is available as a convenience for developers, as it offers a cleaner API.
85
86 Items in a ColumnLayout support these attached properties:
87 \list
88 \input layout.qdocinc attached-properties
89 \endlist
90
91 \image columnlayout.png
92
93 \code
94 ColumnLayout{
95 spacing: 2
96
97 Rectangle {
98 Layout.alignment: Qt.AlignCenter
99 color: "red"
100 Layout.preferredWidth: 40
101 Layout.preferredHeight: 40
102 }
103
104 Rectangle {
105 Layout.alignment: Qt.AlignRight
106 color: "green"
107 Layout.preferredWidth: 40
108 Layout.preferredHeight: 70
109 }
110
111 Rectangle {
112 Layout.alignment: Qt.AlignBottom
113 Layout.fillHeight: true
114 color: "blue"
115 Layout.preferredWidth: 70
116 Layout.preferredHeight: 40
117 }
118 }
119 \endcode
120
121 Read more about attached properties \l{QML Object Attributes}{here}.
122
123 \sa RowLayout
124 \sa GridLayout
125 \sa StackLayout
126 \sa Column
127 \sa {Qt Quick Layouts Overview}
128*/
129
130
131/*!
132 \qmltype GridLayout
133 //! \instantiates QQuickGridLayout
134 \inherits Item
135 \inqmlmodule QtQuick.Layouts
136 \ingroup layouts
137 \brief Provides a way of dynamically arranging items in a grid.
138
139 To be able to use this type more efficiently, it is recommended that you
140 understand the general mechanism of the Qt Quick Layouts module. Refer to
141 \l{Qt Quick Layouts Overview} for more information.
142
143 If the GridLayout is resized, all items in the layout will be rearranged. It is similar
144 to the widget-based QGridLayout. All visible children of the GridLayout element will belong to
145 the layout. If you want a layout with just one row or one column, you can use the
146 \l RowLayout or \l ColumnLayout. These offer a bit more convenient API, and improve
147 readability.
148
149 By default items will be arranged according to the \l flow property. The default value of
150 the \l flow property is \c GridLayout.LeftToRight.
151
152 If the \l columns property is specified, it will be treated as a maximum limit of how many
153 columns the layout can have, before the auto-positioning wraps back to the beginning of the
154 next row. The \l columns property is only used when \l flow is \c GridLayout.LeftToRight.
155
156 \image gridlayout.png
157
158 \code
159 GridLayout {
160 id: grid
161 columns: 3
162
163 Text { text: "Three"; font.bold: true; }
164 Text { text: "words"; color: "red" }
165 Text { text: "in"; font.underline: true }
166 Text { text: "a"; font.pixelSize: 20 }
167 Text { text: "row"; font.strikeout: true }
168 }
169 \endcode
170
171 The \l rows property works in a similar way, but items are auto-positioned vertically. The \l
172 rows property is only used when \l flow is \c GridLayout.TopToBottom.
173
174 You can specify which cell you want an item to occupy by setting the
175 \l{Layout::row}{Layout.row} and \l{Layout::column}{Layout.column} properties. You can also
176 specify the row span or column span by setting the \l{Layout::rowSpan}{Layout.rowSpan} or
177 \l{Layout::columnSpan}{Layout.columnSpan} properties.
178
179
180 Items in a GridLayout support these attached properties:
181 \list
182 \li \l{Layout::row}{Layout.row}
183 \li \l{Layout::column}{Layout.column}
184 \li \l{Layout::rowSpan}{Layout.rowSpan}
185 \li \l{Layout::columnSpan}{Layout.columnSpan}
186 \input layout.qdocinc attached-properties
187 \endlist
188
189 Read more about attached properties \l{QML Object Attributes}{here}.
190
191 \sa RowLayout
192 \sa ColumnLayout
193 \sa StackLayout
194 \sa Grid
195 \sa {Qt Quick Layouts Overview}
196*/
197
198
199
200QT_BEGIN_NAMESPACE
201
202QQuickGridLayoutBase::QQuickGridLayoutBase()
203 : QQuickLayout(*new QQuickGridLayoutBasePrivate)
204{
205
206}
207
208QQuickGridLayoutBase::QQuickGridLayoutBase(QQuickGridLayoutBasePrivate &dd,
209 Qt::Orientation orientation,
210 QQuickItem *parent /*= nullptr */)
211 : QQuickLayout(dd, parent)
212{
213 Q_D(QQuickGridLayoutBase);
214 d->orientation = orientation;
215 d->styleInfo = new QQuickLayoutStyleInfo;
216}
217
218Qt::Orientation QQuickGridLayoutBase::orientation() const
219{
220 Q_D(const QQuickGridLayoutBase);
221 return d->orientation;
222}
223
224void QQuickGridLayoutBase::setOrientation(Qt::Orientation orientation)
225{
226 Q_D(QQuickGridLayoutBase);
227 if (d->orientation == orientation)
228 return;
229
230 d->orientation = orientation;
231 invalidate();
232}
233
234QSizeF QQuickGridLayoutBase::sizeHint(Qt::SizeHint whichSizeHint) const
235{
236 Q_D(const QQuickGridLayoutBase);
237 return d->engine.sizeHint(which: whichSizeHint, constraint: QSizeF(), styleInfo: d->styleInfo);
238}
239
240/*!
241 \qmlproperty enumeration GridLayout::layoutDirection
242 \since QtQuick.Layouts 1.1
243
244 This property holds the layout direction of the grid layout - it controls whether items are
245 laid out from left to right or right to left. If \c Qt.RightToLeft is specified,
246 left-aligned items will be right-aligned and right-aligned items will be left-aligned.
247
248 Possible values:
249
250 \value Qt.LeftToRight (default) Items are laid out from left to right.
251 \value Qt.RightToLeft Items are laid out from right to left.
252
253 \sa RowLayout::layoutDirection, ColumnLayout::layoutDirection
254*/
255Qt::LayoutDirection QQuickGridLayoutBase::layoutDirection() const
256{
257 Q_D(const QQuickGridLayoutBase);
258 return d->m_layoutDirection;
259}
260
261void QQuickGridLayoutBase::setLayoutDirection(Qt::LayoutDirection dir)
262{
263 Q_D(QQuickGridLayoutBase);
264 if (d->m_layoutDirection == dir)
265 return;
266 d->m_layoutDirection = dir;
267 invalidate();
268 emit layoutDirectionChanged();
269}
270
271Qt::LayoutDirection QQuickGridLayoutBase::effectiveLayoutDirection() const
272{
273 Q_D(const QQuickGridLayoutBase);
274 return !d->effectiveLayoutMirror == (layoutDirection() == Qt::LeftToRight)
275 ? Qt::LeftToRight : Qt::RightToLeft;
276}
277
278void QQuickGridLayoutBase::setAlignment(QQuickItem *item, Qt::Alignment alignment)
279{
280 Q_D(QQuickGridLayoutBase);
281 d->engine.setAlignment(quickItem: item, alignment);
282 maybeSubscribeToBaseLineOffsetChanges(item);
283}
284
285void QQuickGridLayoutBase::setStretchFactor(QQuickItem *item, int stretchFactor, Qt::Orientation orient)
286{
287 Q_D(QQuickGridLayoutBase);
288 d->engine.setStretchFactor(quickItem: item, stretch: stretchFactor, orientation: orient);
289}
290
291QQuickGridLayoutBase::~QQuickGridLayoutBase()
292{
293 Q_D(QQuickGridLayoutBase);
294
295 // Remove item listeners so we do not act on signalling unnecessarily
296 // (there is no point, as the layout will be torn down anyway).
297 deactivateRecur();
298 delete d->styleInfo;
299}
300
301void QQuickGridLayoutBase::componentComplete()
302{
303 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::componentComplete()" << this << parent();
304 QQuickLayout::componentComplete();
305
306 /* The layout is invalid when it is constructed, but during construction of the layout and
307 its children (in the "static/from QML" case which this is trying to cover) things
308 change and as a consequence invalidate() and ensureLayoutItemsUpdated() might be called.
309 As soon as ensureLayoutItemsUpdated() is called it will set d->dirty = false.
310 However, a subsequent invalidate() will return early if the component is not completed
311 because it knows that componentComplete() will take care of doing the proper layouting
312 (so it won't set d->dirty = true). When we then call ensureLayoutItemsUpdated() again here
313 it sees that its not dirty and assumes everything up-to-date. For those cases we therefore
314 need to call invalidate() in advance
315 */
316 invalidate();
317 ensureLayoutItemsUpdated(options: QQuickLayout::ApplySizeHints);
318
319 QQuickItem *par = parentItem();
320 if (qobject_cast<QQuickLayout*>(object: par))
321 return;
322 rearrange(size: QSizeF(width(), height()));
323 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::componentComplete(). COMPLETED" << this << parent();
324}
325
326/*
327 Invalidation happens like this as a reaction to that a size hint changes on an item "a":
328
329 Suppose we have the following Qml document:
330 RowLayout {
331 id: l1
332 RowLayout {
333 id: l2
334 Item {
335 id: a
336 }
337 Item {
338 id: b
339 }
340 }
341 }
342
343 1. l2->invalidate(a) is called on l2, where item refers to "a".
344 (this will dirty the cached size hints of item "a")
345 2. The layout engine will invalidate:
346 i) invalidate the layout engine
347 ii) dirty the cached size hints of item "l2" (by calling parentLayout()->invalidate(l2)
348 The recursion continues to the topmost layout
349 */
350/*!
351 \internal
352
353 Invalidates \a childItem and this layout.
354 After a call to invalidate, the next call to retrieve e.g. sizeHint will be up-to date.
355 This function will also call QQuickLayout::invalidate(0), to ensure that the parent layout
356 is invalidated.
357 */
358void QQuickGridLayoutBase::invalidate(QQuickItem *childItem)
359{
360 Q_D(QQuickGridLayoutBase);
361 if (!isReady())
362 return;
363 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::invalidate()" << this << ", invalidated:" << invalidated();
364 if (invalidated()) {
365 return;
366 }
367 qCDebug(lcQuickLayouts) << "d->m_rearranging:" << d->m_rearranging;
368 if (d->m_rearranging) {
369 d->m_invalidateAfterRearrange << childItem;
370 return;
371 }
372
373 if (childItem) {
374 if (QQuickGridLayoutItem *layoutItem = d->engine.findLayoutItem(layoutItem: childItem))
375 layoutItem->invalidate();
376 }
377 // invalidate engine
378 d->engine.invalidate();
379
380 qCDebug(lcQuickLayouts) << "calling QQuickLayout::invalidate();";
381 QQuickLayout::invalidate();
382
383 if (QQuickLayout *parentLayout = qobject_cast<QQuickLayout *>(object: parentItem()))
384 parentLayout->invalidate(childItem: this);
385 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::invalidate() LEAVING" << this;
386}
387
388void QQuickGridLayoutBase::updateLayoutItems()
389{
390 Q_D(QQuickGridLayoutBase);
391 if (!isReady())
392 return;
393
394 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::updateLayoutItems ENTERING" << this;
395 d->engine.deleteItems();
396 insertLayoutItems();
397 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::updateLayoutItems() LEAVING" << this;
398}
399
400QQuickItem *QQuickGridLayoutBase::itemAt(int index) const
401{
402 Q_D(const QQuickGridLayoutBase);
403 return static_cast<QQuickGridLayoutItem*>(d->engine.itemAt(index))->layoutItem();
404}
405
406int QQuickGridLayoutBase::itemCount() const
407{
408 Q_D(const QQuickGridLayoutBase);
409 return d->engine.itemCount();
410}
411
412void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem)
413{
414 Q_D(QQuickGridLayoutBase);
415 const int index = gridItem->firstRow(orientation: d->orientation);
416 d->engine.removeItem(item: gridItem);
417 d->engine.removeRows(row: index, count: 1, orientation: d->orientation);
418}
419
420void QQuickGridLayoutBase::itemDestroyed(QQuickItem *item)
421{
422 if (!isReady())
423 return;
424 Q_D(QQuickGridLayoutBase);
425 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::itemDestroyed";
426 if (QQuickGridLayoutItem *gridItem = d->engine.findLayoutItem(layoutItem: item)) {
427 removeGridItem(gridItem);
428 delete gridItem;
429 invalidate();
430 }
431}
432
433void QQuickGridLayoutBase::itemVisibilityChanged(QQuickItem *item)
434{
435 Q_UNUSED(item);
436
437 if (!isReady())
438 return;
439 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::itemVisibilityChanged()";
440 invalidate(childItem: item);
441}
442
443void QQuickGridLayoutBase::rearrange(const QSizeF &size)
444{
445 Q_D(QQuickGridLayoutBase);
446 if (!isReady())
447 return;
448
449 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::rearrange" << d->m_recurRearrangeCounter << this;
450 const auto refCounter = qScopeGuard(f: [&d] {
451 --(d->m_recurRearrangeCounter);
452 });
453 if (d->m_recurRearrangeCounter++ == 2) {
454 // allow a recursive depth of two in order to respond to height-for-width
455 // (e.g QQuickText changes implicitHeight when its width gets changed)
456 qWarning() << "Qt Quick Layouts: Detected recursive rearrange. Aborting after two iterations.";
457 return;
458 }
459
460 // Should normally not be needed, but there might be an incoming window resize event that we
461 // will process before we process updatePolish()
462 ensureLayoutItemsUpdated(options: QQuickLayout::ApplySizeHints | QQuickLayout::Recursive);
463
464 d->m_rearranging = true;
465 qCDebug(lcQuickLayouts) << objectName() << "QQuickGridLayoutBase::rearrange()" << size;
466 Qt::LayoutDirection visualDir = effectiveLayoutDirection();
467 d->engine.setVisualDirection(visualDir);
468
469 /*
470 qreal left, top, right, bottom;
471 left = top = right = bottom = 0; // ### support for margins?
472 if (visualDir == Qt::RightToLeft)
473 qSwap(left, right);
474 */
475
476 // Set m_dirty to false in case size hint changes during arrangement.
477 // This could happen if there is a binding like implicitWidth: height
478 QQuickLayout::rearrange(size);
479 d->engine.setGeometries(contentsGeometry: QRectF(QPointF(0,0), size), styleInfo: d->styleInfo);
480 d->m_rearranging = false;
481
482 for (QQuickItem *invalid : std::as_const(t&: d->m_invalidateAfterRearrange))
483 invalidate(childItem: invalid);
484 d->m_invalidateAfterRearrange.clear();
485}
486
487/**********************************
488 **
489 ** QQuickGridLayout
490 **
491 **/
492QQuickGridLayout::QQuickGridLayout(QQuickItem *parent /* = nullptr*/)
493 : QQuickGridLayoutBase(*new QQuickGridLayoutPrivate, Qt::Horizontal, parent)
494{
495}
496
497/*!
498 \qmlproperty real GridLayout::columnSpacing
499
500 This property holds the spacing between each column.
501 The default value is \c 5.
502*/
503qreal QQuickGridLayout::columnSpacing() const
504{
505 Q_D(const QQuickGridLayout);
506 return d->engine.spacing(orientation: Qt::Horizontal, styleInfo: d->styleInfo);
507}
508
509void QQuickGridLayout::setColumnSpacing(qreal spacing)
510{
511 Q_D(QQuickGridLayout);
512 if (qt_is_nan(d: spacing) || columnSpacing() == spacing)
513 return;
514
515 d->engine.setSpacing(spacing, orientations: Qt::Horizontal);
516 invalidate();
517 emit columnSpacingChanged();
518}
519
520/*!
521 \qmlproperty real GridLayout::rowSpacing
522
523 This property holds the spacing between each row.
524 The default value is \c 5.
525*/
526qreal QQuickGridLayout::rowSpacing() const
527{
528 Q_D(const QQuickGridLayout);
529 return d->engine.spacing(orientation: Qt::Vertical, styleInfo: d->styleInfo);
530}
531
532void QQuickGridLayout::setRowSpacing(qreal spacing)
533{
534 Q_D(QQuickGridLayout);
535 if (qt_is_nan(d: spacing) || rowSpacing() == spacing)
536 return;
537
538 d->engine.setSpacing(spacing, orientations: Qt::Vertical);
539 invalidate();
540 emit rowSpacingChanged();
541}
542
543/*!
544 \qmlproperty int GridLayout::columns
545
546 This property holds the column limit for items positioned if \l flow is
547 \c GridLayout.LeftToRight.
548 The default value is that there is no limit.
549*/
550int QQuickGridLayout::columns() const
551{
552 Q_D(const QQuickGridLayout);
553 return d->columns;
554}
555
556void QQuickGridLayout::setColumns(int columns)
557{
558 Q_D(QQuickGridLayout);
559 if (d->columns == columns)
560 return;
561 d->columns = columns;
562 invalidate();
563 emit columnsChanged();
564}
565
566
567/*!
568 \qmlproperty int GridLayout::rows
569
570 This property holds the row limit for items positioned if \l flow is \c GridLayout.TopToBottom.
571 The default value is that there is no limit.
572*/
573int QQuickGridLayout::rows() const
574{
575 Q_D(const QQuickGridLayout);
576 return d->rows;
577}
578
579void QQuickGridLayout::setRows(int rows)
580{
581 Q_D(QQuickGridLayout);
582 if (d->rows == rows)
583 return;
584 d->rows = rows;
585 invalidate();
586 emit rowsChanged();
587}
588
589
590/*!
591 \qmlproperty enumeration GridLayout::flow
592
593 This property holds the flow direction of items that does not have an explicit cell
594 position set.
595 It is used together with the \l columns or \l rows property, where
596 they specify when flow is reset to the next row or column respectively.
597
598 Possible values are:
599
600 \value GridLayout.LeftToRight
601 (default) Items are positioned next to each other, then wrapped to the next line.
602 \value GridLayout.TopToBottom
603 Items are positioned next to each other from top to bottom, then wrapped to the next column.
604
605 \sa rows
606 \sa columns
607*/
608QQuickGridLayout::Flow QQuickGridLayout::flow() const
609{
610 Q_D(const QQuickGridLayout);
611 return d->flow;
612}
613
614void QQuickGridLayout::setFlow(QQuickGridLayout::Flow flow)
615{
616 Q_D(QQuickGridLayout);
617 if (d->flow == flow)
618 return;
619 d->flow = flow;
620 // If flow is changed, the layout needs to be repopulated
621 invalidate();
622 emit flowChanged();
623}
624
625/*!
626 \qmlproperty bool GridLayout::uniformCellWidths
627 \since QtQuick.Layouts 6.6
628
629 If this property is set to \c true, the layout will force all cells to have
630 a uniform width. The layout aims to respect
631 \l{Layout::minimumWidth}{Layout.minimumWidth},
632 \l{Layout::preferredWidth}{Layout.preferredWidth} and
633 \l{Layout::maximumWidth}{Layout.maximumWidth} in this mode but might make
634 compromisses to fullfill the requirements of all items.
635
636 Default value is \c false.
637
638 \note This API is considered tech preview and may change or be removed in future versions of
639 Qt.
640
641 \sa GridLayout::uniformCellHeights, RowLayout::uniformCellSizes, ColumnLayout::uniformCellSizes
642*/
643bool QQuickGridLayout::uniformCellWidths() const
644{
645 Q_D(const QQuickGridLayout);
646 return d->engine.uniformCellWidths();
647}
648
649void QQuickGridLayout::setUniformCellWidths(bool uniformCellWidths)
650{
651 Q_D(QQuickGridLayout);
652 if (d->engine.uniformCellWidths() == uniformCellWidths)
653 return;
654 d->engine.setUniformCellWidths(uniformCellWidths);
655 invalidate();
656 emit uniformCellWidthsChanged();
657}
658
659/*!
660 \qmlproperty bool GridLayout::uniformCellHeights
661 \since QtQuick.Layouts 6.6
662
663 If this property is set to \c true, the layout will force all cells to have an
664 uniform Height. The layout aims to respect
665 \l{Layout::minimumHeight}{Layout.minimumHeight},
666 \l{Layout::preferredHeight}{Layout.preferredHeight} and
667 \l{Layout::maximumHeight}{Layout.maximumHeight} in this mode but might make
668 compromisses to fullfill the requirements of all items.
669
670 Default value is \c false.
671
672 \note This API is considered tech preview and may change or be removed in future versions of
673 Qt.
674
675 \sa GridLayout::uniformCellWidths, RowLayout::uniformCellSizes, ColumnLayout::uniformCellSizes
676*/
677bool QQuickGridLayout::uniformCellHeights() const
678{
679 Q_D(const QQuickGridLayout);
680 return d->engine.uniformCellHeights();
681}
682
683void QQuickGridLayout::setUniformCellHeights(bool uniformCellHeights)
684{
685 Q_D(QQuickGridLayout);
686 if (d->engine.uniformCellHeights() == uniformCellHeights)
687 return;
688 d->engine.setUniformCellHeights(uniformCellHeights);
689 invalidate();
690 emit uniformCellHeightsChanged();
691}
692
693
694void QQuickGridLayout::insertLayoutItems()
695{
696 Q_D(QQuickGridLayout);
697
698 int nextCellPos[2] = {0,0};
699 int &nextColumn = nextCellPos[0];
700 int &nextRow = nextCellPos[1];
701
702 const QSize gridSize(columns(), rows());
703 const int flowOrientation = flow();
704 int &flowColumn = nextCellPos[flowOrientation];
705 int &flowRow = nextCellPos[1 - flowOrientation];
706 int flowBound = (flowOrientation == QQuickGridLayout::LeftToRight) ? columns() : rows();
707
708 if (flowBound < 0)
709 flowBound = std::numeric_limits<int>::max();
710
711 const auto items = childItems();
712 for (QQuickItem *child : items) {
713 checkAnchors(item: child);
714 // Will skip all items with effective maximum width/height == 0
715 if (shouldIgnoreItem(child))
716 continue;
717 QQuickLayoutAttached *info = attachedLayoutObject(item: child, create: false);
718
719 Qt::Alignment alignment;
720 int hStretch = -1;
721 int vStretch = -1;
722 int row = -1;
723 int column = -1;
724 int span[2] = {1,1};
725 int &columnSpan = span[0];
726 int &rowSpan = span[1];
727
728 if (info) {
729 if (info->isRowSet() || info->isColumnSet()) {
730 // If row is specified and column is not specified (or vice versa),
731 // the unspecified component of the cell position should default to 0
732 // The getters do this for us, as they will return 0 for an
733 // unset (or negative) value.
734 // In addition, if \a rows is less than Layout.row, treat Layout.row as if it was not set
735 // This will basically make it find the next available cell (potentially wrapping to
736 // the next line). Likewise for an invalid Layout.column
737
738 if (gridSize.height() >= 0 && row >= gridSize.height()) {
739 qmlWarning(me: child) << QString::fromLatin1(ba: "Layout: row (%1) should be less than the number of rows (%2)").arg(a: info->row()).arg(a: rows());
740 } else {
741 row = info->row();
742 }
743
744 if (gridSize.width() >= 0 && info->column() >= gridSize.width()) {
745 qmlWarning(me: child) << QString::fromLatin1(ba: "Layout: column (%1) should be less than the number of columns (%2)").arg(a: info->column()).arg(a: columns());
746 } else {
747 column = info->column();
748 }
749 }
750 rowSpan = info->rowSpan();
751 columnSpan = info->columnSpan();
752 if (columnSpan < 1) {
753 qmlWarning(me: child) << "Layout: invalid column span: " << columnSpan;
754 return;
755
756 } else if (rowSpan < 1) {
757 qmlWarning(me: child) << "Layout: invalid row span: " << rowSpan;
758 return;
759 }
760 alignment = info->alignment();
761 hStretch = info->horizontalStretchFactor();
762 if (hStretch >= 0 && !info->fillWidth())
763 qmlWarning(me: child) << "horizontalStretchFactor requires fillWidth to also be set to true";
764 vStretch = info->verticalStretchFactor();
765 if (vStretch >= 0 && !info->fillHeight())
766 qmlWarning(me: child) << "verticalStretchFactor requires fillHeight to also be set to true";
767 }
768
769 Q_ASSERT(columnSpan >= 1);
770 Q_ASSERT(rowSpan >= 1);
771 const int sp = span[flowOrientation];
772 if (sp > flowBound)
773 return;
774
775 if (row >= 0)
776 nextRow = row;
777 if (column >= 0)
778 nextColumn = column;
779
780 if (row < 0 || column < 0) {
781 /* if row or column is not specified, find next position by
782 advancing in the flow direction until there is a cell that
783 can accept the item.
784
785 The acceptance rules are pretty simple, but complexity arises
786 when an item requires several cells (due to spans):
787 1. Check if the cells that the item will require
788 does not extend beyond columns (for LeftToRight) or
789 rows (for TopToBottom).
790 2. Check if the cells that the item will require is not already
791 taken by another item.
792 */
793 bool cellAcceptsItem;
794 while (true) {
795 // Check if the item does not span beyond the layout bound
796 cellAcceptsItem = (flowColumn + sp) <= flowBound;
797
798 // Check if all the required cells are not taken
799 for (int rs = 0; cellAcceptsItem && rs < rowSpan; ++rs) {
800 for (int cs = 0; cellAcceptsItem && cs < columnSpan; ++cs) {
801 if (d->engine.itemAt(row: nextRow + rs, column: nextColumn + cs)) {
802 cellAcceptsItem = false;
803 }
804 }
805 }
806 if (cellAcceptsItem)
807 break;
808 ++flowColumn;
809 if (flowColumn == flowBound) {
810 flowColumn = 0;
811 ++flowRow;
812 }
813 }
814 }
815 column = nextColumn;
816 row = nextRow;
817 QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, row, column, rowSpan, columnSpan, alignment);
818 if (hStretch >= 0)
819 layoutItem->setStretchFactor(stretch: hStretch, orientation: Qt::Horizontal);
820 if (vStretch >= 0)
821 layoutItem->setStretchFactor(stretch: vStretch, orientation: Qt::Vertical);
822 d->engine.insertItem(item: layoutItem, index: -1);
823 }
824}
825
826/**********************************
827 **
828 ** QQuickLinearLayout
829 **
830 **/
831QQuickLinearLayout::QQuickLinearLayout(Qt::Orientation orientation,
832 QQuickItem *parent /*= nullptr*/)
833 : QQuickGridLayoutBase(*new QQuickLinearLayoutPrivate, orientation, parent)
834{
835}
836
837/*!
838 \qmlproperty enumeration RowLayout::layoutDirection
839 \since QtQuick.Layouts 1.1
840
841 This property holds the layout direction of the row layout - it controls whether items are laid
842 out from left to right or right to left. If \c Qt.RightToLeft is specified,
843 left-aligned items will be right-aligned and right-aligned items will be left-aligned.
844
845 Possible values:
846
847 \value Qt.LeftToRight (default) Items are laid out from left to right.
848 \value Qt.RightToLeft Items are laid out from right to left
849
850 \sa GridLayout::layoutDirection, ColumnLayout::layoutDirection
851*/
852/*!
853 \qmlproperty enumeration ColumnLayout::layoutDirection
854 \since QtQuick.Layouts 1.1
855
856 This property holds the layout direction of the column layout - it controls whether items are laid
857 out from left to right or right to left. If \c Qt.RightToLeft is specified,
858 left-aligned items will be right-aligned and right-aligned items will be left-aligned.
859
860 Possible values:
861
862 \value Qt.LeftToRight (default) Items are laid out from left to right.
863 \value Qt.RightToLeft Items are laid out from right to left
864
865 \sa GridLayout::layoutDirection, RowLayout::layoutDirection
866*/
867
868/*!
869 \qmlproperty bool RowLayout::uniformCellSizes
870 \since QtQuick.Layouts 6.6
871
872 If this property is set to \c true, the layout will force all cells to have
873 a uniform size.
874
875 \note This API is considered tech preview and may change or be removed in future versions of
876 Qt.
877
878 \sa GridLayout::uniformCellWidths, GridLayout::uniformCellHeights, ColumnLayout::uniformCellSizes
879*/
880/*!
881 \qmlproperty bool ColumnLayout::uniformCellSizes
882 \since QtQuick.Layouts 6.6
883
884 If this property is set to \c true, the layout will force all cells to have
885 a uniform size.
886
887 \note This API is considered tech preview and may change or be removed in future versions of
888 Qt.
889
890 \sa GridLayout::uniformCellWidths, GridLayout::uniformCellHeights, RowLayout::uniformCellSizes
891*/
892bool QQuickLinearLayout::uniformCellSizes() const
893{
894 Q_D(const QQuickLinearLayout);
895 Q_ASSERT(d->engine.uniformCellWidths() == d->engine.uniformCellHeights());
896 return d->engine.uniformCellWidths();
897}
898
899void QQuickLinearLayout::setUniformCellSizes(bool uniformCellSizes)
900{
901 Q_D(QQuickLinearLayout);
902 Q_ASSERT(d->engine.uniformCellWidths() == d->engine.uniformCellHeights());
903 if (d->engine.uniformCellHeights() == uniformCellSizes)
904 return;
905 d->engine.setUniformCellWidths(uniformCellSizes);
906 d->engine.setUniformCellHeights(uniformCellSizes);
907 invalidate();
908 emit uniformCellSizesChanged();
909}
910
911
912/*!
913 \qmlproperty real RowLayout::spacing
914
915 This property holds the spacing between each cell.
916 The default value is \c 5.
917*/
918/*!
919 \qmlproperty real ColumnLayout::spacing
920
921 This property holds the spacing between each cell.
922 The default value is \c 5.
923*/
924
925qreal QQuickLinearLayout::spacing() const
926{
927 Q_D(const QQuickLinearLayout);
928 return d->engine.spacing(orientation: d->orientation, styleInfo: d->styleInfo);
929}
930
931void QQuickLinearLayout::setSpacing(qreal space)
932{
933 Q_D(QQuickLinearLayout);
934 if (qt_is_nan(d: space) || spacing() == space)
935 return;
936
937 d->engine.setSpacing(spacing: space, orientations: Qt::Horizontal | Qt::Vertical);
938 invalidate();
939 emit spacingChanged();
940}
941
942void QQuickLinearLayout::insertLayoutItems()
943{
944 Q_D(QQuickLinearLayout);
945 const auto items = childItems();
946 for (QQuickItem *child : items) {
947 Q_ASSERT(child);
948 checkAnchors(item: child);
949
950 // Will skip all items with effective maximum width/height == 0
951 if (shouldIgnoreItem(child))
952 continue;
953 QQuickLayoutAttached *info = attachedLayoutObject(item: child, create: false);
954
955 Qt::Alignment alignment;
956 int hStretch = -1;
957 int vStretch = -1;
958 bool fillWidth = false;
959 bool fillHeight = false;
960 if (info) {
961 alignment = info->alignment();
962 hStretch = info->horizontalStretchFactor();
963 vStretch = info->verticalStretchFactor();
964 fillWidth = info->fillWidth();
965 fillHeight = info->fillHeight();
966 }
967
968 const int index = d->engine.rowCount(orientation: d->orientation);
969 d->engine.insertRow(row: index, orientation: d->orientation);
970
971 int gridRow = 0;
972 int gridColumn = index;
973 if (d->orientation == Qt::Vertical)
974 qSwap(value1&: gridRow, value2&: gridColumn);
975 QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, gridRow, gridColumn, 1, 1, alignment);
976
977 if (hStretch >= 0) {
978 if (!fillWidth)
979 qmlWarning(me: child) << "horizontalStretchFactor requires fillWidth to also be set to true";
980 layoutItem->setStretchFactor(stretch: hStretch, orientation: Qt::Horizontal);
981 }
982 if (vStretch >= 0) {
983 if (!fillHeight)
984 qmlWarning(me: child) << "verticalStretchFactor requires fillHeight to also be set to true";
985 layoutItem->setStretchFactor(stretch: vStretch, orientation: Qt::Vertical);
986 }
987 d->engine.insertItem(item: layoutItem, index);
988 }
989}
990
991QT_END_NAMESPACE
992
993#include "moc_qquicklinearlayout_p.cpp"
994

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