Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qquicklinearlayout.cpp
Go to the documentation of this file.
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
7#include <QtCore/private/qnumeric_p.h>
8#include <QtQml/qqmlinfo.h>
9#include "qdebug.h"
10#include <limits>
11
15
75
134
201
204{
205
206}
207
209 Qt::Orientation orientation,
210 QQuickItem *parent /*= nullptr */)
211 : QQuickLayout(dd, parent)
212{
214 d->orientation = orientation;
215 d->styleInfo = new QQuickLayoutStyleInfo;
216}
217
219{
220 Q_D(const QQuickGridLayoutBase);
221 return d->orientation;
222}
223
225{
227 if (d->orientation == orientation)
228 return;
229
230 d->orientation = orientation;
231 invalidate();
232}
233
235{
236 Q_D(const QQuickGridLayoutBase);
237 return d->engine.sizeHint(whichSizeHint, QSizeF(), d->styleInfo);
238}
239
256{
257 Q_D(const QQuickGridLayoutBase);
258 return d->m_layoutDirection;
259}
260
262{
264 if (d->m_layoutDirection == dir)
265 return;
266 d->m_layoutDirection = dir;
267 invalidate();
268 emit layoutDirectionChanged();
269}
270
272{
273 Q_D(const QQuickGridLayoutBase);
274 return !d->effectiveLayoutMirror == (layoutDirection() == Qt::LeftToRight)
276}
277
279{
281 d->engine.setAlignment(item, alignment);
283}
284
286{
288 d->engine.setStretchFactor(item, stretchFactor, orient);
289}
290
292{
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).
298 delete d->styleInfo;
299}
300
302{
303 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::componentComplete()" << this << parent();
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();
318
319 QQuickItem *par = parentItem();
320 if (qobject_cast<QQuickLayout*>(par))
321 return;
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 */
359{
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(childItem))
375 layoutItem->invalidate();
376 }
377 // invalidate engine
378 d->engine.invalidate();
379
380 qCDebug(lcQuickLayouts) << "calling QQuickLayout::invalidate();";
382
383 if (QQuickLayout *parentLayout = qobject_cast<QQuickLayout *>(parentItem()))
384 parentLayout->invalidate(this);
385 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::invalidate() LEAVING" << this;
386}
387
389{
391 if (!isReady())
392 return;
393
394 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::updateLayoutItems ENTERING" << this;
395 d->engine.deleteItems();
397 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::updateLayoutItems() LEAVING" << this;
398}
399
401{
402 Q_D(const QQuickGridLayoutBase);
403 return static_cast<QQuickGridLayoutItem*>(d->engine.itemAt(index))->layoutItem();
404}
405
407{
408 Q_D(const QQuickGridLayoutBase);
409 return d->engine.itemCount();
410}
411
412void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem)
413{
415 const int index = gridItem->firstRow(d->orientation);
416 d->engine.removeItem(gridItem);
417 d->engine.removeRows(index, 1, d->orientation);
418}
419
421{
422 if (!isReady())
423 return;
425 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::itemDestroyed";
426 if (QQuickGridLayoutItem *gridItem = d->engine.findLayoutItem(item)) {
427 removeGridItem(gridItem);
428 delete gridItem;
429 invalidate();
430 }
431}
432
434{
435 Q_UNUSED(item);
436
437 if (!isReady())
438 return;
439 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::itemVisibilityChanged()";
441}
442
444{
446 if (!isReady())
447 return;
448
449 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::rearrange" << d->m_recurRearrangeCounter << this;
450 const auto refCounter = qScopeGuard([&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()
463
464 d->m_rearranging = true;
465 qCDebug(lcQuickLayouts) << objectName() << "QQuickGridLayoutBase::rearrange()" << size;
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
479 d->engine.setGeometries(QRectF(QPointF(0,0), size), d->styleInfo);
480 d->m_rearranging = false;
481
482 for (QQuickItem *invalid : std::as_const(d->m_invalidateAfterRearrange))
483 invalidate(invalid);
484 d->m_invalidateAfterRearrange.clear();
485}
486
487/**********************************
488 **
489 ** QQuickGridLayout
490 **
491 **/
494{
495}
496
504{
505 Q_D(const QQuickGridLayout);
506 return d->engine.spacing(Qt::Horizontal, d->styleInfo);
507}
508
510{
511 Q_D(QQuickGridLayout);
513 return;
514
515 d->engine.setSpacing(spacing, Qt::Horizontal);
516 invalidate();
518}
519
527{
528 Q_D(const QQuickGridLayout);
529 return d->engine.spacing(Qt::Vertical, d->styleInfo);
530}
531
533{
534 Q_D(QQuickGridLayout);
535 if (qt_is_nan(spacing) || rowSpacing() == spacing)
536 return;
537
538 d->engine.setSpacing(spacing, Qt::Vertical);
539 invalidate();
541}
542
551{
552 Q_D(const QQuickGridLayout);
553 return d->columns;
554}
555
557{
558 Q_D(QQuickGridLayout);
559 if (d->columns == columns)
560 return;
561 d->columns = columns;
562 invalidate();
564}
565
566
574{
575 Q_D(const QQuickGridLayout);
576 return d->rows;
577}
578
580{
581 Q_D(QQuickGridLayout);
582 if (d->rows == rows)
583 return;
584 d->rows = rows;
585 invalidate();
587}
588
589
609{
610 Q_D(const QQuickGridLayout);
611 return d->flow;
612}
613
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();
623}
624
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
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
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) {
714 // Will skip all items with effective maximum width/height == 0
716 continue;
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(child) << QString::fromLatin1("Layout: row (%1) should be less than the number of rows (%2)").arg(info->row()).arg(rows());
740 } else {
741 row = info->row();
742 }
743
744 if (gridSize.width() >= 0 && info->column() >= gridSize.width()) {
745 qmlWarning(child) << QString::fromLatin1("Layout: column (%1) should be less than the number of columns (%2)").arg(info->column()).arg(columns());
746 } else {
747 column = info->column();
748 }
749 }
750 rowSpan = info->rowSpan();
751 columnSpan = info->columnSpan();
752 if (columnSpan < 1) {
753 qmlWarning(child) << "Layout: invalid column span: " << columnSpan;
754 return;
755
756 } else if (rowSpan < 1) {
757 qmlWarning(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(child) << "horizontalStretchFactor requires fillWidth to also be set to true";
764 vStretch = info->verticalStretchFactor();
765 if (vStretch >= 0 && !info->fillHeight())
766 qmlWarning(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(nextRow + rs, 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;
818 if (hStretch >= 0)
819 layoutItem->setStretchFactor(hStretch, Qt::Horizontal);
820 if (vStretch >= 0)
821 layoutItem->setStretchFactor(vStretch, Qt::Vertical);
822 d->engine.insertItem(layoutItem, -1);
823 }
824}
825
826/**********************************
827 **
828 ** QQuickLinearLayout
829 **
830 **/
832 QQuickItem *parent /*= nullptr*/)
834{
835}
836
893{
894 Q_D(const QQuickLinearLayout);
895 Q_ASSERT(d->engine.uniformCellWidths() == d->engine.uniformCellHeights());
896 return d->engine.uniformCellWidths();
897}
898
900{
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
926{
927 Q_D(const QQuickLinearLayout);
928 return d->engine.spacing(d->orientation, d->styleInfo);
929}
930
932{
934 if (qt_is_nan(space) || spacing() == space)
935 return;
936
937 d->engine.setSpacing(space, Qt::Horizontal | Qt::Vertical);
938 invalidate();
940}
941
943{
945 const auto items = childItems();
946 for (QQuickItem *child : items) {
949
950 // Will skip all items with effective maximum width/height == 0
952 continue;
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(d->orientation);
969 d->engine.insertRow(index, d->orientation);
970
971 int gridRow = 0;
972 int gridColumn = index;
973 if (d->orientation == Qt::Vertical)
974 qSwap(gridRow, gridColumn);
975 QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, gridRow, gridColumn, 1, 1, alignment);
976
977 if (hStretch >= 0) {
978 if (!fillWidth)
979 qmlWarning(child) << "horizontalStretchFactor requires fillWidth to also be set to true";
980 layoutItem->setStretchFactor(hStretch, Qt::Horizontal);
981 }
982 if (vStretch >= 0) {
983 if (!fillHeight)
984 qmlWarning(child) << "verticalStretchFactor requires fillHeight to also be set to true";
985 layoutItem->setStretchFactor(vStretch, Qt::Vertical);
986 }
987 d->engine.insertItem(layoutItem, index);
988 }
989}
990
992
993#include "moc_qquicklinearlayout_p.cpp"
QString objectName
the name of this object
Definition qobject.h:94
\inmodule QtCore\reentrant
Definition qpoint.h:214
Qt::LayoutDirection layoutDirection
int itemCount() const override
void itemVisibilityChanged(QQuickItem *item) override
Qt::LayoutDirection effectiveLayoutDirection() const
QQuickItem * itemAt(int index) const override
QSizeF sizeHint(Qt::SizeHint whichSizeHint) const override
virtual void insertLayoutItems()
void setLayoutDirection(Qt::LayoutDirection dir)
Qt::Orientation orientation() const
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
void rearrange(const QSizeF &size) override
void itemDestroyed(QQuickItem *item) override
QQuickGridLayoutBase()
Identical to \l GridLayout, but having only one row.
void setStretchFactor(QQuickItem *item, int stretchFactor, Qt::Orientation orient) override
void setAlignment(QQuickItem *item, Qt::Alignment align) override
void invalidate(QQuickItem *childItem=nullptr) override
void updateLayoutItems() override
void setOrientation(Qt::Orientation orientation)
QQuickGridLayout(QQuickItem *parent=nullptr)
void columnSpacingChanged()
void setColumns(int columns)
void setFlow(Flow flow)
void setUniformCellHeights(bool uniformCellHeights)
void setUniformCellWidths(bool uniformCellWidths)
void insertLayoutItems() override
void setRowSpacing(qreal spacing)
void rowSpacingChanged()
void setColumnSpacing(qreal spacing)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
QList< QQuickItem * > childItems() const
Returns the children of this item.
QSizeF size() const
qreal width
This property holds the width of this item.
Definition qquickitem.h:76
QQuickItem * parentItem() const
QQuickItem * parent
\qmlproperty Item QtQuick::Item::parent This property holds the visual parent of the item.
Definition qquickitem.h:68
qreal height
This property holds the height of this item.
Definition qquickitem.h:77
bool invalidated() const
bool isReady() const
void maybeSubscribeToBaseLineOffsetChanges(QQuickItem *item)
virtual void rearrange(const QSizeF &)
virtual void invalidate(QQuickItem *childItem=nullptr)
bool shouldIgnoreItem(QQuickItem *child) const
void checkAnchors(QQuickItem *item) const
void ensureLayoutItemsUpdated(EnsureLayoutItemsUpdatedOptions options={}) const
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
void deactivateRecur()
QQuickLayout::deactivateRecur.
void insertLayoutItems() override
void setSpacing(qreal spacing)
QQuickLinearLayout(Qt::Orientation orientation, QQuickItem *parent=nullptr)
void setUniformCellSizes(bool uniformCellSizes)
\inmodule QtCore\reentrant
Definition qrect.h:483
\inmodule QtCore
Definition qsize.h:207
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8606
qreal spacing
qSwap(pi, e)
uint alignment
Combined button and popup list for selecting options.
LayoutDirection
@ LeftToRight
@ RightToLeft
Orientation
Definition qnamespace.h:97
@ Horizontal
Definition qnamespace.h:98
@ Vertical
Definition qnamespace.h:99
#define qWarning
Definition qlogging.h:162
#define qCDebug(category,...)
static Q_DECL_CONST_FUNCTION bool qt_is_nan(double d)
Definition qnumeric_p.h:106
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLenum GLenum GLsizei void GLsizei void * column
GLenum GLenum GLsizei void * row
GLenum GLenum GLsizei void GLsizei void void * span
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
static void layoutItem(QQuickItem *item, qreal y, qreal width)
QQuickLayoutAttached * attachedLayoutObject(QQuickItem *item, bool create=true)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
#define sp
#define emit
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:92
QFileInfo info(fileName)
[8]
QString dir
[11]
QGraphicsItem * item
QList< QTreeWidgetItem * > items
QLayoutItem * child
[0]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent