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 //! \nativetype 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 //! \nativetype 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 //! \nativetype 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 (childItem) {
365 if (d->m_rearranging) {
366 if (!d->m_invalidateAfterRearrange.contains(t: childItem))
367 d->m_invalidateAfterRearrange << childItem;
368 return;
369 }
370 if (QQuickGridLayoutItem *layoutItem = d->engine.findLayoutItem(layoutItem: childItem)) {
371 layoutItem->invalidate();
372 }
373 }
374
375 // invalidate engine
376 d->engine.invalidate();
377
378 qCDebug(lcQuickLayouts) << "calling QQuickLayout::invalidate();";
379 QQuickLayout::invalidate();
380
381 if (auto *parentLayout = qobject_cast<QQuickLayout *>(object: parentItem()))
382 parentLayout->invalidate(childItem: this);
383 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::invalidate() LEAVING" << this;
384}
385
386void QQuickGridLayoutBase::updateLayoutItems()
387{
388 Q_D(QQuickGridLayoutBase);
389 if (!isReady())
390 return;
391
392 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::updateLayoutItems ENTERING" << this;
393 d->engine.deleteItems();
394 insertLayoutItems();
395 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::updateLayoutItems() LEAVING" << this;
396}
397
398QQuickItem *QQuickGridLayoutBase::itemAt(int index) const
399{
400 Q_D(const QQuickGridLayoutBase);
401 return static_cast<QQuickGridLayoutItem*>(d->engine.itemAt(index))->layoutItem();
402}
403
404int QQuickGridLayoutBase::itemCount() const
405{
406 Q_D(const QQuickGridLayoutBase);
407 return d->engine.itemCount();
408}
409
410void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem)
411{
412 Q_D(QQuickGridLayoutBase);
413 const int index = gridItem->firstRow(orientation: d->orientation);
414 d->engine.removeItem(item: gridItem);
415 d->engine.removeRows(row: index, count: 1, orientation: d->orientation);
416}
417
418void QQuickGridLayoutBase::itemDestroyed(QQuickItem *item)
419{
420 if (!isReady())
421 return;
422 Q_D(QQuickGridLayoutBase);
423 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::itemDestroyed";
424 if (QQuickGridLayoutItem *gridItem = d->engine.findLayoutItem(layoutItem: item)) {
425 removeGridItem(gridItem);
426 delete gridItem;
427 invalidate();
428 }
429}
430
431void QQuickGridLayoutBase::itemVisibilityChanged(QQuickItem *item)
432{
433 Q_UNUSED(item);
434
435 if (!isReady())
436 return;
437 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::itemVisibilityChanged()";
438 invalidate(childItem: item);
439}
440
441void QQuickGridLayoutBase::rearrange(const QSizeF &size)
442{
443 Q_D(QQuickGridLayoutBase);
444 if (!isReady() || !size.isValid())
445 return;
446
447 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::rearrange" << d->m_recurRearrangeCounter << this;
448 const auto refCounter = qScopeGuard(f: [&d] {
449 --(d->m_recurRearrangeCounter);
450 });
451 if (d->m_recurRearrangeCounter++ == 2) {
452 // allow a recursive depth of two in order to respond to height-for-width
453 // (e.g QQuickText changes implicitHeight when its width gets changed)
454 qmlWarning(me: this)
455 << "Qt Quick Layouts: Detected recursive rearrange. Aborting after two iterations.";
456 return;
457 }
458
459 // Should normally not be needed, but there might be an incoming window resize event that we
460 // will process before we process updatePolish()
461 ensureLayoutItemsUpdated(options: QQuickLayout::ApplySizeHints | QQuickLayout::Recursive);
462
463 d->m_rearranging = true;
464 qCDebug(lcQuickLayouts) << objectName() << "QQuickGridLayoutBase::rearrange()" << size;
465 Qt::LayoutDirection visualDir = effectiveLayoutDirection();
466 d->engine.setVisualDirection(visualDir);
467
468 /*
469 qreal left, top, right, bottom;
470 left = top = right = bottom = 0; // ### support for margins?
471 if (visualDir == Qt::RightToLeft)
472 qSwap(left, right);
473 */
474
475 // Set m_dirty to false in case size hint changes during arrangement.
476 // This could happen if there is a binding like implicitWidth: height
477 QQuickLayout::rearrange(size);
478 d->engine.setGeometries(contentsGeometry: QRectF(QPointF(0,0), size), styleInfo: d->styleInfo);
479 d->m_rearranging = false;
480
481 for (auto childItem : std::as_const(t&: d->m_invalidateAfterRearrange))
482 invalidate(childItem);
483 d->m_invalidateAfterRearrange.clear();
484}
485
486/**********************************
487 **
488 ** QQuickGridLayout
489 **
490 **/
491QQuickGridLayout::QQuickGridLayout(QQuickItem *parent /* = nullptr*/)
492 : QQuickGridLayoutBase(*new QQuickGridLayoutPrivate, Qt::Horizontal, parent)
493{
494}
495
496/*!
497 \qmlproperty real GridLayout::columnSpacing
498
499 This property holds the spacing between each column.
500 The default value is \c 5.
501*/
502qreal QQuickGridLayout::columnSpacing() const
503{
504 Q_D(const QQuickGridLayout);
505 return d->engine.spacing(orientation: Qt::Horizontal, styleInfo: d->styleInfo);
506}
507
508void QQuickGridLayout::setColumnSpacing(qreal spacing)
509{
510 Q_D(QQuickGridLayout);
511 if (qt_is_nan(d: spacing) || columnSpacing() == spacing)
512 return;
513
514 d->engine.setSpacing(spacing, orientations: Qt::Horizontal);
515 invalidate();
516 emit columnSpacingChanged();
517}
518
519/*!
520 \qmlproperty real GridLayout::rowSpacing
521
522 This property holds the spacing between each row.
523 The default value is \c 5.
524*/
525qreal QQuickGridLayout::rowSpacing() const
526{
527 Q_D(const QQuickGridLayout);
528 return d->engine.spacing(orientation: Qt::Vertical, styleInfo: d->styleInfo);
529}
530
531void QQuickGridLayout::setRowSpacing(qreal spacing)
532{
533 Q_D(QQuickGridLayout);
534 if (qt_is_nan(d: spacing) || rowSpacing() == spacing)
535 return;
536
537 d->engine.setSpacing(spacing, orientations: Qt::Vertical);
538 invalidate();
539 emit rowSpacingChanged();
540}
541
542/*!
543 \qmlproperty int GridLayout::columns
544
545 This property holds the column limit for items positioned if \l flow is
546 \c GridLayout.LeftToRight.
547 The default value is that there is no limit.
548*/
549int QQuickGridLayout::columns() const
550{
551 Q_D(const QQuickGridLayout);
552 return d->columns;
553}
554
555void QQuickGridLayout::setColumns(int columns)
556{
557 Q_D(QQuickGridLayout);
558 if (d->columns == columns)
559 return;
560 d->columns = columns;
561 invalidate();
562 emit columnsChanged();
563}
564
565
566/*!
567 \qmlproperty int GridLayout::rows
568
569 This property holds the row limit for items positioned if \l flow is \c GridLayout.TopToBottom.
570 The default value is that there is no limit.
571*/
572int QQuickGridLayout::rows() const
573{
574 Q_D(const QQuickGridLayout);
575 return d->rows;
576}
577
578void QQuickGridLayout::setRows(int rows)
579{
580 Q_D(QQuickGridLayout);
581 if (d->rows == rows)
582 return;
583 d->rows = rows;
584 invalidate();
585 emit rowsChanged();
586}
587
588
589/*!
590 \qmlproperty enumeration GridLayout::flow
591
592 This property holds the flow direction of items that does not have an explicit cell
593 position set.
594 It is used together with the \l columns or \l rows property, where
595 they specify when flow is reset to the next row or column respectively.
596
597 Possible values are:
598
599 \value GridLayout.LeftToRight
600 (default) Items are positioned next to each other, then wrapped to the next line.
601 \value GridLayout.TopToBottom
602 Items are positioned next to each other from top to bottom, then wrapped to the next column.
603
604 \sa rows
605 \sa columns
606*/
607QQuickGridLayout::Flow QQuickGridLayout::flow() const
608{
609 Q_D(const QQuickGridLayout);
610 return d->flow;
611}
612
613void QQuickGridLayout::setFlow(QQuickGridLayout::Flow flow)
614{
615 Q_D(QQuickGridLayout);
616 if (d->flow == flow)
617 return;
618 d->flow = flow;
619 // If flow is changed, the layout needs to be repopulated
620 invalidate();
621 emit flowChanged();
622}
623
624/*!
625 \qmlproperty bool GridLayout::uniformCellWidths
626 \since QtQuick.Layouts 6.6
627
628 If this property is set to \c true, the layout will force all cells to have
629 a uniform width. The layout aims to respect
630 \l{Layout::minimumWidth}{Layout.minimumWidth},
631 \l{Layout::preferredWidth}{Layout.preferredWidth} and
632 \l{Layout::maximumWidth}{Layout.maximumWidth} in this mode but might make
633 compromisses to fullfill the requirements of all items.
634
635 Default value is \c false.
636
637 \sa GridLayout::uniformCellHeights, RowLayout::uniformCellSizes, ColumnLayout::uniformCellSizes
638*/
639bool QQuickGridLayout::uniformCellWidths() const
640{
641 Q_D(const QQuickGridLayout);
642 return d->engine.uniformCellWidths();
643}
644
645void QQuickGridLayout::setUniformCellWidths(bool uniformCellWidths)
646{
647 Q_D(QQuickGridLayout);
648 if (d->engine.uniformCellWidths() == uniformCellWidths)
649 return;
650 d->engine.setUniformCellWidths(uniformCellWidths);
651 invalidate();
652 emit uniformCellWidthsChanged();
653}
654
655/*!
656 \qmlproperty bool GridLayout::uniformCellHeights
657 \since QtQuick.Layouts 6.6
658
659 If this property is set to \c true, the layout will force all cells to have an
660 uniform Height. The layout aims to respect
661 \l{Layout::minimumHeight}{Layout.minimumHeight},
662 \l{Layout::preferredHeight}{Layout.preferredHeight} and
663 \l{Layout::maximumHeight}{Layout.maximumHeight} in this mode but might make
664 compromisses to fullfill the requirements of all items.
665
666 Default value is \c false.
667
668 \sa GridLayout::uniformCellWidths, RowLayout::uniformCellSizes, ColumnLayout::uniformCellSizes
669*/
670bool QQuickGridLayout::uniformCellHeights() const
671{
672 Q_D(const QQuickGridLayout);
673 return d->engine.uniformCellHeights();
674}
675
676void QQuickGridLayout::setUniformCellHeights(bool uniformCellHeights)
677{
678 Q_D(QQuickGridLayout);
679 if (d->engine.uniformCellHeights() == uniformCellHeights)
680 return;
681 d->engine.setUniformCellHeights(uniformCellHeights);
682 invalidate();
683 emit uniformCellHeightsChanged();
684}
685
686
687void QQuickGridLayout::insertLayoutItems()
688{
689 Q_D(QQuickGridLayout);
690
691 int nextCellPos[2] = {0,0};
692 int &nextColumn = nextCellPos[0];
693 int &nextRow = nextCellPos[1];
694
695 const QSize gridSize(columns(), rows());
696 const int flowOrientation = flow();
697 int &flowColumn = nextCellPos[flowOrientation];
698 int &flowRow = nextCellPos[1 - flowOrientation];
699 int flowBound = (flowOrientation == QQuickGridLayout::LeftToRight) ? columns() : rows();
700
701 if (flowBound < 0)
702 flowBound = std::numeric_limits<int>::max();
703
704 const auto items = childItems();
705 for (QQuickItem *child : items) {
706 checkAnchors(item: child);
707 // Will skip all items with effective maximum width/height == 0
708 if (shouldIgnoreItem(child))
709 continue;
710 QQuickLayoutAttached *info = attachedLayoutObject(item: child, create: false);
711
712 Qt::Alignment alignment;
713 int hStretch = -1;
714 int vStretch = -1;
715 int row = -1;
716 int column = -1;
717 int span[2] = {1,1};
718 int &columnSpan = span[0];
719 int &rowSpan = span[1];
720
721 if (info) {
722 if (info->isRowSet() || info->isColumnSet()) {
723 // If row is specified and column is not specified (or vice versa),
724 // the unspecified component of the cell position should default to 0
725 // The getters do this for us, as they will return 0 for an
726 // unset (or negative) value.
727 // In addition, if \a rows is less than Layout.row, treat Layout.row as if it was not set
728 // This will basically make it find the next available cell (potentially wrapping to
729 // the next line). Likewise for an invalid Layout.column
730
731 if (gridSize.height() >= 0 && row >= gridSize.height()) {
732 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());
733 } else {
734 row = info->row();
735 }
736
737 if (gridSize.width() >= 0 && info->column() >= gridSize.width()) {
738 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());
739 } else {
740 column = info->column();
741 }
742 }
743 rowSpan = info->rowSpan();
744 columnSpan = info->columnSpan();
745 if (columnSpan < 1) {
746 qmlWarning(me: child) << "Layout: invalid column span: " << columnSpan;
747 return;
748
749 } else if (rowSpan < 1) {
750 qmlWarning(me: child) << "Layout: invalid row span: " << rowSpan;
751 return;
752 }
753 alignment = info->alignment();
754 hStretch = info->horizontalStretchFactor();
755 if (hStretch >= 0 && !info->fillWidth())
756 qmlWarning(me: child) << "horizontalStretchFactor requires fillWidth to also be set to true";
757 vStretch = info->verticalStretchFactor();
758 if (vStretch >= 0 && !info->fillHeight())
759 qmlWarning(me: child) << "verticalStretchFactor requires fillHeight to also be set to true";
760 }
761
762 Q_ASSERT(columnSpan >= 1);
763 Q_ASSERT(rowSpan >= 1);
764 const int sp = span[flowOrientation];
765 if (sp > flowBound)
766 return;
767
768 if (row >= 0)
769 nextRow = row;
770 if (column >= 0)
771 nextColumn = column;
772
773 if (row < 0 || column < 0) {
774 /* if row or column is not specified, find next position by
775 advancing in the flow direction until there is a cell that
776 can accept the item.
777
778 The acceptance rules are pretty simple, but complexity arises
779 when an item requires several cells (due to spans):
780 1. Check if the cells that the item will require
781 does not extend beyond columns (for LeftToRight) or
782 rows (for TopToBottom).
783 2. Check if the cells that the item will require is not already
784 taken by another item.
785 */
786 bool cellAcceptsItem;
787 while (true) {
788 // Check if the item does not span beyond the layout bound
789 cellAcceptsItem = (flowColumn + sp) <= flowBound;
790
791 // Check if all the required cells are not taken
792 for (int rs = 0; cellAcceptsItem && rs < rowSpan; ++rs) {
793 for (int cs = 0; cellAcceptsItem && cs < columnSpan; ++cs) {
794 if (d->engine.itemAt(row: nextRow + rs, column: nextColumn + cs)) {
795 cellAcceptsItem = false;
796 }
797 }
798 }
799 if (cellAcceptsItem)
800 break;
801 ++flowColumn;
802 if (flowColumn == flowBound) {
803 flowColumn = 0;
804 ++flowRow;
805 }
806 }
807 }
808 column = nextColumn;
809 row = nextRow;
810 QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, row, column, rowSpan, columnSpan, alignment);
811 if (hStretch >= 0)
812 layoutItem->setStretchFactor(stretch: hStretch, orientation: Qt::Horizontal);
813 if (vStretch >= 0)
814 layoutItem->setStretchFactor(stretch: vStretch, orientation: Qt::Vertical);
815
816 d->engine.insertItem(item: layoutItem, index: -1);
817 }
818}
819
820/**********************************
821 **
822 ** QQuickLinearLayout
823 **
824 **/
825QQuickLinearLayout::QQuickLinearLayout(Qt::Orientation orientation,
826 QQuickItem *parent /*= nullptr*/)
827 : QQuickGridLayoutBase(*new QQuickLinearLayoutPrivate, orientation, parent)
828{
829}
830
831/*!
832 \qmlproperty enumeration RowLayout::layoutDirection
833 \since QtQuick.Layouts 1.1
834
835 This property holds the layout direction of the row layout - it controls whether items are laid
836 out from left to right or right to left. If \c Qt.RightToLeft is specified,
837 left-aligned items will be right-aligned and right-aligned items will be left-aligned.
838
839 Possible values:
840
841 \value Qt.LeftToRight (default) Items are laid out from left to right.
842 \value Qt.RightToLeft Items are laid out from right to left
843
844 \sa GridLayout::layoutDirection, ColumnLayout::layoutDirection
845*/
846/*!
847 \qmlproperty enumeration ColumnLayout::layoutDirection
848 \since QtQuick.Layouts 1.1
849
850 This property holds the layout direction of the column layout - it controls whether items are laid
851 out from left to right or right to left. If \c Qt.RightToLeft is specified,
852 left-aligned items will be right-aligned and right-aligned items will be left-aligned.
853
854 Possible values:
855
856 \value Qt.LeftToRight (default) Items are laid out from left to right.
857 \value Qt.RightToLeft Items are laid out from right to left
858
859 \sa GridLayout::layoutDirection, RowLayout::layoutDirection
860*/
861
862/*!
863 \qmlproperty bool RowLayout::uniformCellSizes
864 \since QtQuick.Layouts 6.6
865
866 If this property is set to \c true, the layout will force all cells to have
867 a uniform size.
868
869 \sa GridLayout::uniformCellWidths, GridLayout::uniformCellHeights, ColumnLayout::uniformCellSizes
870*/
871/*!
872 \qmlproperty bool ColumnLayout::uniformCellSizes
873 \since QtQuick.Layouts 6.6
874
875 If this property is set to \c true, the layout will force all cells to have
876 a uniform size.
877
878 \sa GridLayout::uniformCellWidths, GridLayout::uniformCellHeights, RowLayout::uniformCellSizes
879*/
880bool QQuickLinearLayout::uniformCellSizes() const
881{
882 Q_D(const QQuickLinearLayout);
883 Q_ASSERT(d->engine.uniformCellWidths() == d->engine.uniformCellHeights());
884 return d->engine.uniformCellWidths();
885}
886
887void QQuickLinearLayout::setUniformCellSizes(bool uniformCellSizes)
888{
889 Q_D(QQuickLinearLayout);
890 Q_ASSERT(d->engine.uniformCellWidths() == d->engine.uniformCellHeights());
891 if (d->engine.uniformCellHeights() == uniformCellSizes)
892 return;
893 d->engine.setUniformCellWidths(uniformCellSizes);
894 d->engine.setUniformCellHeights(uniformCellSizes);
895 invalidate();
896 emit uniformCellSizesChanged();
897}
898
899
900/*!
901 \qmlproperty real RowLayout::spacing
902
903 This property holds the spacing between each cell.
904 The default value is \c 5.
905*/
906/*!
907 \qmlproperty real ColumnLayout::spacing
908
909 This property holds the spacing between each cell.
910 The default value is \c 5.
911*/
912
913qreal QQuickLinearLayout::spacing() const
914{
915 Q_D(const QQuickLinearLayout);
916 return d->engine.spacing(orientation: d->orientation, styleInfo: d->styleInfo);
917}
918
919void QQuickLinearLayout::setSpacing(qreal space)
920{
921 Q_D(QQuickLinearLayout);
922 if (qt_is_nan(d: space) || spacing() == space)
923 return;
924
925 d->engine.setSpacing(spacing: space, orientations: Qt::Horizontal | Qt::Vertical);
926 invalidate();
927 emit spacingChanged();
928}
929
930void QQuickLinearLayout::insertLayoutItems()
931{
932 Q_D(QQuickLinearLayout);
933 const auto items = childItems();
934 for (QQuickItem *child : items) {
935 Q_ASSERT(child);
936 checkAnchors(item: child);
937
938 // Will skip all items with effective maximum width/height == 0
939 if (shouldIgnoreItem(child))
940 continue;
941 QQuickLayoutAttached *info = attachedLayoutObject(item: child, create: false);
942
943 Qt::Alignment alignment;
944 int hStretch = -1;
945 int vStretch = -1;
946 bool fillWidth = false;
947 bool fillHeight = false;
948 if (info) {
949 alignment = info->alignment();
950 hStretch = info->horizontalStretchFactor();
951 vStretch = info->verticalStretchFactor();
952 fillWidth = info->fillWidth();
953 fillHeight = info->fillHeight();
954 }
955
956 const int index = d->engine.rowCount(orientation: d->orientation);
957 d->engine.insertRow(row: index, orientation: d->orientation);
958
959 int gridRow = 0;
960 int gridColumn = index;
961 if (d->orientation == Qt::Vertical)
962 qSwap(value1&: gridRow, value2&: gridColumn);
963 QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, gridRow, gridColumn, 1, 1, alignment);
964
965 if (hStretch >= 0) {
966 if (!fillWidth)
967 qmlWarning(me: child) << "horizontalStretchFactor requires fillWidth to also be set to true";
968 layoutItem->setStretchFactor(stretch: hStretch, orientation: Qt::Horizontal);
969 }
970 if (vStretch >= 0) {
971 if (!fillHeight)
972 qmlWarning(me: child) << "verticalStretchFactor requires fillHeight to also be set to true";
973 layoutItem->setStretchFactor(stretch: vStretch, orientation: Qt::Vertical);
974 }
975 d->engine.insertItem(item: layoutItem, index);
976 }
977}
978
979QT_END_NAMESPACE
980
981#include "moc_qquicklinearlayout_p.cpp"
982

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