Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
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
277
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 (childItem) {
365 if (d->m_rearranging) {
366 if (!d->m_invalidateAfterRearrange.contains(childItem))
367 d->m_invalidateAfterRearrange << childItem;
368 return;
369 }
370 if (QQuickGridLayoutItem *layoutItem = d->engine.findLayoutItem(childItem)) {
371 layoutItem->invalidate();
372 }
373 }
374
375 // invalidate engine
376 d->engine.invalidate();
377
378 qCDebug(lcQuickLayouts) << "calling QQuickLayout::invalidate();";
380
381 if (auto *parentLayout = qobject_cast<QQuickLayout *>(parentItem()))
382 parentLayout->invalidate(this);
383 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::invalidate() LEAVING" << this;
384}
385
387{
389 if (!isReady())
390 return;
391
392 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::updateLayoutItems ENTERING" << this;
393 d->engine.deleteItems();
395 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::updateLayoutItems() LEAVING" << this;
396}
397
399{
400 Q_D(const QQuickGridLayoutBase);
401 return static_cast<QQuickGridLayoutItem*>(d->engine.itemAt(index))->layoutItem();
402}
403
405{
406 Q_D(const QQuickGridLayoutBase);
407 return d->engine.itemCount();
408}
409
410void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem)
411{
413 const int index = gridItem->firstRow(d->orientation);
414 d->engine.removeItem(gridItem);
415 d->engine.removeRows(index, 1, d->orientation);
416}
417
419{
420 if (!isReady())
421 return;
423 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::itemDestroyed";
424 if (QQuickGridLayoutItem *gridItem = d->engine.findLayoutItem(item)) {
425 removeGridItem(gridItem);
426 delete gridItem;
427 invalidate();
428 }
429}
430
432{
433 Q_UNUSED(item);
434
435 if (!isReady())
436 return;
437 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::itemVisibilityChanged()";
439}
440
442{
444 if (!isReady() || !size.isValid())
445 return;
446
447 qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::rearrange" << d->m_recurRearrangeCounter << this;
448 const auto refCounter = qScopeGuard([&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()
461
462 d->m_rearranging = true;
463 qCDebug(lcQuickLayouts) << objectName() << "QQuickGridLayoutBase::rearrange()" << size;
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
477 d->engine.setGeometries(QRectF(QPointF(0,0), size), d->styleInfo);
478 d->m_rearranging = false;
479
480 for (auto childItem : std::as_const(d->m_invalidateAfterRearrange))
481 invalidate(childItem);
482 d->m_invalidateAfterRearrange.clear();
483}
484
485/**********************************
486 **
487 ** QQuickGridLayout
488 **
489 **/
491 : QQuickGridLayoutBase(*new QQuickGridLayoutPrivate, Qt::Horizontal, parent)
492{
493}
494
502{
503 Q_D(const QQuickGridLayout);
504 return d->engine.spacing(Qt::Horizontal, d->styleInfo);
505}
506
508{
509 Q_D(QQuickGridLayout);
511 return;
512
513 d->engine.setSpacing(spacing, Qt::Horizontal);
514 invalidate();
516}
517
525{
526 Q_D(const QQuickGridLayout);
527 return d->engine.spacing(Qt::Vertical, d->styleInfo);
528}
529
531{
532 Q_D(QQuickGridLayout);
533 if (qt_is_nan(spacing) || rowSpacing() == spacing)
534 return;
535
536 d->engine.setSpacing(spacing, Qt::Vertical);
537 invalidate();
539}
540
549{
550 Q_D(const QQuickGridLayout);
551 return d->columns;
552}
553
555{
556 Q_D(QQuickGridLayout);
557 if (d->columns == columns)
558 return;
559 d->columns = columns;
560 invalidate();
562}
563
564
572{
573 Q_D(const QQuickGridLayout);
574 return d->rows;
575}
576
578{
579 Q_D(QQuickGridLayout);
580 if (d->rows == rows)
581 return;
582 d->rows = rows;
583 invalidate();
585}
586
587
607{
608 Q_D(const QQuickGridLayout);
609 return d->flow;
610}
611
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();
621}
622
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
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
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) {
706 // Will skip all items with effective maximum width/height == 0
708 continue;
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(child) << QString::fromLatin1("Layout: row (%1) should be less than the number of rows (%2)").arg(info->row()).arg(rows());
732 } else {
733 row = info->row();
734 }
735
736 if (gridSize.width() >= 0 && info->column() >= gridSize.width()) {
737 qmlWarning(child) << QString::fromLatin1("Layout: column (%1) should be less than the number of columns (%2)").arg(info->column()).arg(columns());
738 } else {
739 column = info->column();
740 }
741 }
742 rowSpan = info->rowSpan();
743 columnSpan = info->columnSpan();
744 if (columnSpan < 1) {
745 qmlWarning(child) << "Layout: invalid column span: " << columnSpan;
746 return;
747
748 } else if (rowSpan < 1) {
749 qmlWarning(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(child) << "horizontalStretchFactor requires fillWidth to also be set to true";
756 vStretch = info->verticalStretchFactor();
757 if (vStretch >= 0 && !info->fillHeight())
758 qmlWarning(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(nextRow + rs, 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;
810 if (hStretch >= 0)
811 layoutItem->setStretchFactor(hStretch, Qt::Horizontal);
812 if (vStretch >= 0)
813 layoutItem->setStretchFactor(vStretch, Qt::Vertical);
814
815 d->engine.insertItem(layoutItem, -1);
816 }
817}
818
819/**********************************
820 **
821 ** QQuickLinearLayout
822 **
823 **/
825 QQuickItem *parent /*= nullptr*/)
826 : QQuickGridLayoutBase(*new QQuickLinearLayoutPrivate, orientation, parent)
827{
828}
829
880{
881 Q_D(const QQuickLinearLayout);
882 Q_ASSERT(d->engine.uniformCellWidths() == d->engine.uniformCellHeights());
883 return d->engine.uniformCellWidths();
884}
885
887{
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
913{
914 Q_D(const QQuickLinearLayout);
915 return d->engine.spacing(d->orientation, d->styleInfo);
916}
917
919{
921 if (qt_is_nan(space) || spacing() == space)
922 return;
923
924 d->engine.setSpacing(space, Qt::Horizontal | Qt::Vertical);
925 invalidate();
927}
928
930{
932 const auto items = childItems();
933 for (QQuickItem *child : items) {
936
937 // Will skip all items with effective maximum width/height == 0
939 continue;
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(d->orientation);
956 d->engine.insertRow(index, d->orientation);
957
958 int gridRow = 0;
959 int gridColumn = index;
960 if (d->orientation == Qt::Vertical)
961 qSwap(gridRow, gridColumn);
962 QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, gridRow, gridColumn, 1, 1, alignment);
963
964 if (hStretch >= 0) {
965 if (!fillWidth)
966 qmlWarning(child) << "horizontalStretchFactor requires fillWidth to also be set to true";
967 layoutItem->setStretchFactor(hStretch, Qt::Horizontal);
968 }
969 if (vStretch >= 0) {
970 if (!fillHeight)
971 qmlWarning(child) << "verticalStretchFactor requires fillHeight to also be set to true";
972 layoutItem->setStretchFactor(vStretch, Qt::Vertical);
973 }
974 d->engine.insertItem(layoutItem, index);
975 }
976}
977
979
980#include "moc_qquicklinearlayout_p.cpp"
QString objectName
the name of this object
Definition qobject.h:107
\inmodule QtCore\reentrant
Definition qpoint.h:217
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:63
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:75
QQuickItem * parentItem() const
QQuickItem * parent
\qmlproperty Item QtQuick::Item::parent This property holds the visual parent of the item.
Definition qquickitem.h:67
qreal height
This property holds the height of this item.
Definition qquickitem.h:76
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
QML_ANONYMOUSqreal spacing
void setSpacing(qreal spacing)
QQuickLinearLayout(Qt::Orientation orientation, QQuickItem *parent=nullptr)
void setUniformCellSizes(bool uniformCellSizes)
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore
Definition qsize.h:208
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
qreal spacing
uint alignment
Combined button and popup list for selecting options.
Definition qcompare.h:63
LayoutDirection
@ LeftToRight
@ RightToLeft
Orientation
Definition qnamespace.h:98
@ Horizontal
Definition qnamespace.h:99
@ Vertical
Definition qnamespace.h:100
#define qWarning
Definition qlogging.h:166
#define qCDebug(category,...)
static Q_DECL_CONST_FUNCTION bool qt_is_nan(double d)
Definition qnumeric_p.h:112
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
QT_BEGIN_NAMESPACE constexpr void qSwap(T &value1, T &value2) noexcept(std::is_nothrow_swappable_v< T >)
Definition qswap.h:20
#define sp
#define emit
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:187
QString dir
[11]
QGraphicsItem * item
QList< QTreeWidgetItem * > items
QLayoutItem * child
[0]
QHostInfo info
[0]