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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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