| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Quick Layouts module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qquicklinearlayout_p.h" |
| #include "qquickgridlayoutengine_p.h" |
| #include "qquicklayoutstyleinfo_p.h" |
| #include <QtCore/private/qnumeric_p.h> |
| #include <QtQml/qqmlinfo.h> |
| #include "qdebug.h" |
| #include <limits> |
| |
| /*! |
| \qmltype RowLayout |
| \instantiates QQuickRowLayout |
| \inherits Item |
| \inqmlmodule QtQuick.Layouts |
| \ingroup layouts |
| \brief Identical to \l GridLayout, but having only one row. |
| |
| It is available as a convenience for developers, as it offers a cleaner API. |
| |
| Items in a RowLayout support these attached properties: |
| \list |
| \input layout.qdocinc attached-properties |
| \endlist |
| |
| \image rowlayout.png |
| |
| \code |
| RowLayout { |
| id: layout |
| anchors.fill: parent |
| spacing: 6 |
| Rectangle { |
| color: 'teal' |
| Layout.fillWidth: true |
| Layout.minimumWidth: 50 |
| Layout.preferredWidth: 100 |
| Layout.maximumWidth: 300 |
| Layout.minimumHeight: 150 |
| Text { |
| anchors.centerIn: parent |
| text: parent.width + 'x' + parent.height |
| } |
| } |
| Rectangle { |
| color: 'plum' |
| Layout.fillWidth: true |
| Layout.minimumWidth: 100 |
| Layout.preferredWidth: 200 |
| Layout.preferredHeight: 100 |
| Text { |
| anchors.centerIn: parent |
| text: parent.width + 'x' + parent.height |
| } |
| } |
| } |
| \endcode |
| |
| Read more about attached properties \l{QML Object Attributes}{here}. |
| \sa ColumnLayout |
| \sa GridLayout |
| \sa Row |
| */ |
| |
| /*! |
| \qmltype ColumnLayout |
| \instantiates QQuickColumnLayout |
| \inherits Item |
| \inqmlmodule QtQuick.Layouts |
| \ingroup layouts |
| \brief Identical to \l GridLayout, but having only one column. |
| |
| It is available as a convenience for developers, as it offers a cleaner API. |
| |
| Items in a ColumnLayout support these attached properties: |
| \list |
| \input layout.qdocinc attached-properties |
| \endlist |
| |
| \image columnlayout.png |
| |
| \code |
| ColumnLayout{ |
| spacing: 2 |
| |
| Rectangle { |
| Layout.alignment: Qt.AlignCenter |
| color: "red" |
| Layout.preferredWidth: 40 |
| Layout.preferredHeight: 40 |
| } |
| |
| Rectangle { |
| Layout.alignment: Qt.AlignRight |
| color: "green" |
| Layout.preferredWidth: 40 |
| Layout.preferredHeight: 70 |
| } |
| |
| Rectangle { |
| Layout.alignment: Qt.AlignBottom |
| Layout.fillHeight: true |
| color: "blue" |
| Layout.preferredWidth: 70 |
| Layout.preferredHeight: 40 |
| } |
| } |
| \endcode |
| |
| Read more about attached properties \l{QML Object Attributes}{here}. |
| |
| \sa RowLayout |
| \sa GridLayout |
| \sa Column |
| */ |
| |
| |
| /*! |
| \qmltype GridLayout |
| \instantiates QQuickGridLayout |
| \inherits Item |
| \inqmlmodule QtQuick.Layouts |
| \ingroup layouts |
| \brief Provides a way of dynamically arranging items in a grid. |
| |
| |
| |
| If the GridLayout is resized, all items in the layout will be rearranged. It is similar |
| to the widget-based QGridLayout. All visible children of the GridLayout element will belong to |
| the layout. If you want a layout with just one row or one column, you can use the |
| \l RowLayout or \l ColumnLayout. These offer a bit more convenient API, and improve |
| readability. |
| |
| By default items will be arranged according to the \l flow property. The default value of |
| the \l flow property is \c GridLayout.LeftToRight. |
| |
| If the \l columns property is specified, it will be treated as a maximum limit of how many |
| columns the layout can have, before the auto-positioning wraps back to the beginning of the |
| next row. The \l columns property is only used when \l flow is \c GridLayout.LeftToRight. |
| |
| \image gridlayout.png |
| |
| \code |
| GridLayout { |
| id: grid |
| columns: 3 |
| |
| Text { text: "Three"; font.bold: true; } |
| Text { text: "words"; color: "red" } |
| Text { text: "in"; font.underline: true } |
| Text { text: "a"; font.pixelSize: 20 } |
| Text { text: "row"; font.strikeout: true } |
| } |
| \endcode |
| |
| The \l rows property works in a similar way, but items are auto-positioned vertically. The \l |
| rows property is only used when \l flow is \c GridLayout.TopToBottom. |
| |
| You can specify which cell you want an item to occupy by setting the |
| \l{Layout::row}{Layout.row} and \l{Layout::column}{Layout.column} properties. You can also |
| specify the row span or column span by setting the \l{Layout::rowSpan}{Layout.rowSpan} or |
| \l{Layout::columnSpan}{Layout.columnSpan} properties. |
| |
| |
| Items in a GridLayout support these attached properties: |
| \list |
| \li \l{Layout::row}{Layout.row} |
| \li \l{Layout::column}{Layout.column} |
| \li \l{Layout::rowSpan}{Layout.rowSpan} |
| \li \l{Layout::columnSpan}{Layout.columnSpan} |
| \input layout.qdocinc attached-properties |
| \endlist |
| |
| Read more about attached properties \l{QML Object Attributes}{here}. |
| |
| \sa RowLayout |
| \sa ColumnLayout |
| \sa Grid |
| */ |
| |
| |
| |
| QT_BEGIN_NAMESPACE |
| |
| QQuickGridLayoutBase::QQuickGridLayoutBase() |
| : QQuickLayout(*new QQuickGridLayoutBasePrivate) |
| { |
| |
| } |
| |
| QQuickGridLayoutBase::QQuickGridLayoutBase(QQuickGridLayoutBasePrivate &dd, |
| Qt::Orientation orientation, |
| QQuickItem *parent /*= 0*/) |
| : QQuickLayout(dd, parent) |
| { |
| Q_D(QQuickGridLayoutBase); |
| d->orientation = orientation; |
| d->styleInfo = new QQuickLayoutStyleInfo; |
| } |
| |
| Qt::Orientation QQuickGridLayoutBase::orientation() const |
| { |
| Q_D(const QQuickGridLayoutBase); |
| return d->orientation; |
| } |
| |
| void QQuickGridLayoutBase::setOrientation(Qt::Orientation orientation) |
| { |
| Q_D(QQuickGridLayoutBase); |
| if (d->orientation == orientation) |
| return; |
| |
| d->orientation = orientation; |
| invalidate(); |
| } |
| |
| QSizeF QQuickGridLayoutBase::sizeHint(Qt::SizeHint whichSizeHint) const |
| { |
| Q_D(const QQuickGridLayoutBase); |
| ensureLayoutItemsUpdated(); |
| return d->engine.sizeHint(whichSizeHint, QSizeF(), d->styleInfo); |
| } |
| |
| /*! |
| \qmlproperty enumeration GridLayout::layoutDirection |
| \since QtQuick.Layouts 1.1 |
| |
| This property holds the layout direction of the grid layout - it controls whether items are |
| laid out from left to right or right to left. If \c Qt.RightToLeft is specified, |
| left-aligned items will be right-aligned and right-aligned items will be left-aligned. |
| |
| Possible values: |
| |
| \list |
| \li Qt.LeftToRight (default) - Items are laid out from left to right. |
| \li Qt.RightToLeft - Items are laid out from right to left. |
| \endlist |
| |
| \sa RowLayout::layoutDirection, ColumnLayout::layoutDirection |
| */ |
| Qt::LayoutDirection QQuickGridLayoutBase::layoutDirection() const |
| { |
| Q_D(const QQuickGridLayoutBase); |
| return d->m_layoutDirection; |
| } |
| |
| void QQuickGridLayoutBase::setLayoutDirection(Qt::LayoutDirection dir) |
| { |
| Q_D(QQuickGridLayoutBase); |
| if (d->m_layoutDirection == dir) |
| return; |
| d->m_layoutDirection = dir; |
| invalidate(); |
| emit layoutDirectionChanged(); |
| } |
| |
| Qt::LayoutDirection QQuickGridLayoutBase::effectiveLayoutDirection() const |
| { |
| Q_D(const QQuickGridLayoutBase); |
| return !d->effectiveLayoutMirror == (layoutDirection() == Qt::LeftToRight) |
| ? Qt::LeftToRight : Qt::RightToLeft; |
| } |
| |
| void QQuickGridLayoutBase::setAlignment(QQuickItem *item, Qt::Alignment alignment) |
| { |
| Q_D(QQuickGridLayoutBase); |
| d->engine.setAlignment(item, alignment); |
| } |
| |
| QQuickGridLayoutBase::~QQuickGridLayoutBase() |
| { |
| Q_D(QQuickGridLayoutBase); |
| |
| // Remove item listeners so we do not act on signalling unnecessarily |
| // (there is no point, as the layout will be torn down anyway). |
| deactivateRecur(); |
| delete d->styleInfo; |
| } |
| |
| void QQuickGridLayoutBase::componentComplete() |
| { |
| qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::componentComplete()" << this << parent(); |
| QQuickLayout::componentComplete(); |
| |
| /* The layout is invalid when it is constructed, but during construction of the layout and |
| its children (in the "static/from QML" case which this is trying to cover) things |
| change and as a consequence invalidate() and ensureLayoutItemsUpdated() might be called. |
| As soon as ensureLayoutItemsUpdated() is called it will set d->dirty = false. |
| However, a subsequent invalidate() will return early if the component is not completed |
| because it knows that componentComplete() will take care of doing the proper layouting |
| (so it won't set d->dirty = true). When we then call ensureLayoutItemsUpdated() again here |
| it sees that its not dirty and assumes everything up-to-date. For those cases we therefore |
| need to call invalidate() in advance |
| */ |
| invalidate(); |
| ensureLayoutItemsUpdated(); |
| |
| QQuickItem *par = parentItem(); |
| if (qobject_cast<QQuickLayout*>(par)) |
| return; |
| rearrange(QSizeF(width(), height())); |
| qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::componentComplete(). COMPLETED" << this << parent(); |
| } |
| |
| /* |
| Invalidation happens like this as a reaction to that a size hint changes on an item "a": |
| |
| Suppose we have the following Qml document: |
| RowLayout { |
| id: l1 |
| RowLayout { |
| id: l2 |
| Item { |
| id: a |
| } |
| Item { |
| id: b |
| } |
| } |
| } |
| |
| 1. l2->invalidate(a) is called on l2, where item refers to "a". |
| (this will dirty the cached size hints of item "a") |
| 2. The layout engine will invalidate: |
| i) invalidate the layout engine |
| ii) dirty the cached size hints of item "l2" (by calling parentLayout()->invalidate(l2) |
| The recursion continues to the topmost layout |
| */ |
| /*! |
| \internal |
| |
| Invalidates \a childItem and this layout. |
| After a call to invalidate, the next call to retrieve e.g. sizeHint will be up-to date. |
| This function will also call QQuickLayout::invalidate(0), to ensure that the parent layout |
| is invalidated. |
| */ |
| void QQuickGridLayoutBase::invalidate(QQuickItem *childItem) |
| { |
| Q_D(QQuickGridLayoutBase); |
| if (!isReady()) |
| return; |
| qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::invalidate()" << this << ", invalidated:" << invalidated(); |
| if (invalidated()) { |
| return; |
| } |
| qCDebug(lcQuickLayouts) << "d->m_rearranging:" << d->m_rearranging; |
| if (d->m_rearranging) { |
| d->m_invalidateAfterRearrange << childItem; |
| return; |
| } |
| |
| if (childItem) { |
| if (QQuickGridLayoutItem *layoutItem = d->engine.findLayoutItem(childItem)) |
| layoutItem->invalidate(); |
| } |
| // invalidate engine |
| d->engine.invalidate(); |
| |
| qCDebug(lcQuickLayouts) << "calling QQuickLayout::invalidate();"; |
| QQuickLayout::invalidate(); |
| |
| if (QQuickLayout *parentLayout = qobject_cast<QQuickLayout *>(parentItem())) |
| parentLayout->invalidate(this); |
| qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::invalidate() LEAVING" << this; |
| } |
| |
| void QQuickGridLayoutBase::updateLayoutItems() |
| { |
| Q_D(QQuickGridLayoutBase); |
| if (!isReady()) |
| return; |
| if (d->m_rearranging) { |
| d->m_updateAfterRearrange = true; |
| return; |
| } |
| |
| qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::updateLayoutItems ENTERING" << this; |
| d->engine.deleteItems(); |
| insertLayoutItems(); |
| qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::updateLayoutItems() LEAVING" << this; |
| } |
| |
| QQuickItem *QQuickGridLayoutBase::itemAt(int index) const |
| { |
| Q_D(const QQuickGridLayoutBase); |
| qCDebug(lcQuickLayouts).nospace() << "QQuickGridLayoutBase::itemAt(" << index << ")"; |
| ensureLayoutItemsUpdated(); |
| qCDebug(lcQuickLayouts).nospace() << "QQuickGridLayoutBase::itemAt(" << index << ") LEAVING"; |
| return static_cast<QQuickGridLayoutItem*>(d->engine.itemAt(index))->layoutItem(); |
| } |
| |
| int QQuickGridLayoutBase::itemCount() const |
| { |
| Q_D(const QQuickGridLayoutBase); |
| ensureLayoutItemsUpdated(); |
| return d->engine.itemCount(); |
| } |
| |
| void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem) |
| { |
| Q_D(QQuickGridLayoutBase); |
| const int index = gridItem->firstRow(d->orientation); |
| d->engine.removeItem(gridItem); |
| d->engine.removeRows(index, 1, d->orientation); |
| } |
| |
| void QQuickGridLayoutBase::itemDestroyed(QQuickItem *item) |
| { |
| if (!isReady()) |
| return; |
| Q_D(QQuickGridLayoutBase); |
| qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::itemDestroyed"; |
| if (QQuickGridLayoutItem *gridItem = d->engine.findLayoutItem(item)) { |
| removeGridItem(gridItem); |
| delete gridItem; |
| invalidate(); |
| } |
| } |
| |
| void QQuickGridLayoutBase::itemVisibilityChanged(QQuickItem *item) |
| { |
| Q_UNUSED(item); |
| |
| if (!isReady()) |
| return; |
| qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::itemVisibilityChanged()"; |
| invalidate(item); |
| } |
| |
| void QQuickGridLayoutBase::rearrange(const QSizeF &size) |
| { |
| Q_D(QQuickGridLayoutBase); |
| if (!isReady()) |
| return; |
| |
| ensureLayoutItemsUpdated(); |
| |
| qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::rearrange" << d->m_recurRearrangeCounter << this; |
| const auto refCounter = qScopeGuard([&d] { |
| --(d->m_recurRearrangeCounter); |
| }); |
| if (d->m_recurRearrangeCounter++ == 2) { |
| // allow a recursive depth of two in order to respond to height-for-width |
| // (e.g QQuickText changes implicitHeight when its width gets changed) |
| qWarning() << "Qt Quick Layouts: Detected recursive rearrange. Aborting after two iterations."; |
| return; |
| } |
| |
| d->m_rearranging = true; |
| qCDebug(lcQuickLayouts) << objectName() << "QQuickGridLayoutBase::rearrange()" << size; |
| Qt::LayoutDirection visualDir = effectiveLayoutDirection(); |
| d->engine.setVisualDirection(visualDir); |
| |
| /* |
| qreal left, top, right, bottom; |
| left = top = right = bottom = 0; // ### support for margins? |
| if (visualDir == Qt::RightToLeft) |
| qSwap(left, right); |
| */ |
| |
| // Set m_dirty to false in case size hint changes during arrangement. |
| // This could happen if there is a binding like implicitWidth: height |
| QQuickLayout::rearrange(size); |
| d->engine.setGeometries(QRectF(QPointF(0,0), size), d->styleInfo); |
| d->m_rearranging = false; |
| |
| for (QQuickItem *invalid : qAsConst(d->m_invalidateAfterRearrange)) |
| invalidate(invalid); |
| d->m_invalidateAfterRearrange.clear(); |
| |
| if (d->m_updateAfterRearrange) { |
| ensureLayoutItemsUpdated(); |
| d->m_updateAfterRearrange = false; |
| } |
| } |
| |
| /********************************** |
| ** |
| ** QQuickGridLayout |
| ** |
| **/ |
| QQuickGridLayout::QQuickGridLayout(QQuickItem *parent /* = 0*/) |
| : QQuickGridLayoutBase(*new QQuickGridLayoutPrivate, Qt::Horizontal, parent) |
| { |
| } |
| |
| /*! |
| \qmlproperty real GridLayout::columnSpacing |
| |
| This property holds the spacing between each column. |
| The default value is \c 5. |
| */ |
| qreal QQuickGridLayout::columnSpacing() const |
| { |
| Q_D(const QQuickGridLayout); |
| return d->engine.spacing(Qt::Horizontal, d->styleInfo); |
| } |
| |
| void QQuickGridLayout::setColumnSpacing(qreal spacing) |
| { |
| Q_D(QQuickGridLayout); |
| if (qt_is_nan(spacing) || columnSpacing() == spacing) |
| return; |
| |
| d->engine.setSpacing(spacing, Qt::Horizontal); |
| invalidate(); |
| emit columnSpacingChanged(); |
| } |
| |
| /*! |
| \qmlproperty real GridLayout::rowSpacing |
| |
| This property holds the spacing between each row. |
| The default value is \c 5. |
| */ |
| qreal QQuickGridLayout::rowSpacing() const |
| { |
| Q_D(const QQuickGridLayout); |
| return d->engine.spacing(Qt::Vertical, d->styleInfo); |
| } |
| |
| void QQuickGridLayout::setRowSpacing(qreal spacing) |
| { |
| Q_D(QQuickGridLayout); |
| if (qt_is_nan(spacing) || rowSpacing() == spacing) |
| return; |
| |
| d->engine.setSpacing(spacing, Qt::Vertical); |
| invalidate(); |
| emit rowSpacingChanged(); |
| } |
| |
| /*! |
| \qmlproperty int GridLayout::columns |
| |
| This property holds the column limit for items positioned if \l flow is |
| \c GridLayout.LeftToRight. |
| The default value is that there is no limit. |
| */ |
| int QQuickGridLayout::columns() const |
| { |
| Q_D(const QQuickGridLayout); |
| return d->columns; |
| } |
| |
| void QQuickGridLayout::setColumns(int columns) |
| { |
| Q_D(QQuickGridLayout); |
| if (d->columns == columns) |
| return; |
| d->columns = columns; |
| invalidate(); |
| emit columnsChanged(); |
| } |
| |
| |
| /*! |
| \qmlproperty int GridLayout::rows |
| |
| This property holds the row limit for items positioned if \l flow is \c GridLayout.TopToBottom. |
| The default value is that there is no limit. |
| */ |
| int QQuickGridLayout::rows() const |
| { |
| Q_D(const QQuickGridLayout); |
| return d->rows; |
| } |
| |
| void QQuickGridLayout::setRows(int rows) |
| { |
| Q_D(QQuickGridLayout); |
| if (d->rows == rows) |
| return; |
| d->rows = rows; |
| invalidate(); |
| emit rowsChanged(); |
| } |
| |
| |
| /*! |
| \qmlproperty enumeration GridLayout::flow |
| |
| This property holds the flow direction of items that does not have an explicit cell |
| position set. |
| It is used together with the \l columns or \l rows property, where |
| they specify when flow is reset to the next row or column respectively. |
| |
| Possible values are: |
| |
| \list |
| \li GridLayout.LeftToRight (default) - Items are positioned next to |
| each other, then wrapped to the next line. |
| \li GridLayout.TopToBottom - Items are positioned next to each |
| other from top to bottom, then wrapped to the next column. |
| \endlist |
| |
| \sa rows |
| \sa columns |
| */ |
| QQuickGridLayout::Flow QQuickGridLayout::flow() const |
| { |
| Q_D(const QQuickGridLayout); |
| return d->flow; |
| } |
| |
| void QQuickGridLayout::setFlow(QQuickGridLayout::Flow flow) |
| { |
| Q_D(QQuickGridLayout); |
| if (d->flow == flow) |
| return; |
| d->flow = flow; |
| // If flow is changed, the layout needs to be repopulated |
| invalidate(); |
| emit flowChanged(); |
| } |
| |
| void QQuickGridLayout::insertLayoutItems() |
| { |
| Q_D(QQuickGridLayout); |
| |
| int nextCellPos[2] = {0,0}; |
| int &nextColumn = nextCellPos[0]; |
| int &nextRow = nextCellPos[1]; |
| |
| const QSize gridSize(columns(), rows()); |
| const int flowOrientation = flow(); |
| int &flowColumn = nextCellPos[flowOrientation]; |
| int &flowRow = nextCellPos[1 - flowOrientation]; |
| int flowBound = (flowOrientation == QQuickGridLayout::LeftToRight) ? columns() : rows(); |
| |
| if (flowBound < 0) |
| flowBound = std::numeric_limits<int>::max(); |
| |
| QSizeF sizeHints[Qt::NSizeHints]; |
| const auto items = childItems(); |
| for (QQuickItem *child : items) { |
| checkAnchors(child); |
| QQuickLayoutAttached *info = nullptr; |
| |
| // Will skip all items with effective maximum width/height == 0 |
| if (shouldIgnoreItem(child, info, sizeHints)) |
| continue; |
| |
| Qt::Alignment alignment; |
| int row = -1; |
| int column = -1; |
| int span[2] = {1,1}; |
| int &columnSpan = span[0]; |
| int &rowSpan = span[1]; |
| |
| if (info) { |
| if (info->isRowSet() || info->isColumnSet()) { |
| // If row is specified and column is not specified (or vice versa), |
| // the unspecified component of the cell position should default to 0 |
| // The getters do this for us, as they will return 0 for an |
| // unset (or negative) value. |
| // In addition, if \a rows is less than Layout.row, treat Layout.row as if it was not set |
| // This will basically make it find the next available cell (potentially wrapping to |
| // the next line). Likewise for an invalid Layout.column |
| |
| if (gridSize.height() >= 0 && row >= gridSize.height()) { |
| qmlWarning(child) << QString::fromLatin1("Layout: row (%1) should be less than the number of rows (%2)").arg(info->row()).arg(rows()); |
| } else { |
| row = info->row(); |
| } |
| |
| if (gridSize.width() >= 0 && info->column() >= gridSize.width()) { |
| qmlWarning(child) << QString::fromLatin1("Layout: column (%1) should be less than the number of columns (%2)").arg(info->column()).arg(columns()); |
| } else { |
| column = info->column(); |
| } |
| } |
| rowSpan = info->rowSpan(); |
| columnSpan = info->columnSpan(); |
| if (columnSpan < 1) { |
| qmlWarning(child) << "Layout: invalid column span: " << columnSpan; |
| return; |
| |
| } else if (rowSpan < 1) { |
| qmlWarning(child) << "Layout: invalid row span: " << rowSpan; |
| return; |
| } |
| alignment = info->alignment(); |
| } |
| |
| Q_ASSERT(columnSpan >= 1); |
| Q_ASSERT(rowSpan >= 1); |
| const int sp = span[flowOrientation]; |
| if (sp > flowBound) |
| return; |
| |
| if (row >= 0) |
| nextRow = row; |
| if (column >= 0) |
| nextColumn = column; |
| |
| if (row < 0 || column < 0) { |
| /* if row or column is not specified, find next position by |
| advancing in the flow direction until there is a cell that |
| can accept the item. |
| |
| The acceptance rules are pretty simple, but complexity arises |
| when an item requires several cells (due to spans): |
| 1. Check if the cells that the item will require |
| does not extend beyond columns (for LeftToRight) or |
| rows (for TopToBottom). |
| 2. Check if the cells that the item will require is not already |
| taken by another item. |
| */ |
| bool cellAcceptsItem; |
| while (true) { |
| // Check if the item does not span beyond the layout bound |
| cellAcceptsItem = (flowColumn + sp) <= flowBound; |
| |
| // Check if all the required cells are not taken |
| for (int rs = 0; cellAcceptsItem && rs < rowSpan; ++rs) { |
| for (int cs = 0; cellAcceptsItem && cs < columnSpan; ++cs) { |
| if (d->engine.itemAt(nextRow + rs, nextColumn + cs)) { |
| cellAcceptsItem = false; |
| } |
| } |
| } |
| if (cellAcceptsItem) |
| break; |
| ++flowColumn; |
| if (flowColumn == flowBound) { |
| flowColumn = 0; |
| ++flowRow; |
| } |
| } |
| } |
| column = nextColumn; |
| row = nextRow; |
| QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, row, column, rowSpan, columnSpan, alignment); |
| layoutItem->setCachedSizeHints(sizeHints); |
| |
| d->engine.insertItem(layoutItem, -1); |
| } |
| } |
| |
| /********************************** |
| ** |
| ** QQuickLinearLayout |
| ** |
| **/ |
| QQuickLinearLayout::QQuickLinearLayout(Qt::Orientation orientation, |
| QQuickItem *parent /*= 0*/) |
| : QQuickGridLayoutBase(*new QQuickLinearLayoutPrivate, orientation, parent) |
| { |
| } |
| |
| /*! |
| \qmlproperty enumeration RowLayout::layoutDirection |
| \since QtQuick.Layouts 1.1 |
| |
| This property holds the layout direction of the row layout - it controls whether items are laid |
| out from left ro right or right to left. If \c Qt.RightToLeft is specified, |
| left-aligned items will be right-aligned and right-aligned items will be left-aligned. |
| |
| Possible values: |
| |
| \list |
| \li Qt.LeftToRight (default) - Items are laid out from left to right. |
| \li Qt.RightToLeft - Items are laid out from right to left |
| \endlist |
| |
| \sa GridLayout::layoutDirection, ColumnLayout::layoutDirection |
| */ |
| /*! |
| \qmlproperty enumeration ColumnLayout::layoutDirection |
| \since QtQuick.Layouts 1.1 |
| |
| This property holds the layout direction of the column layout - it controls whether items are laid |
| out from left ro right or right to left. If \c Qt.RightToLeft is specified, |
| left-aligned items will be right-aligned and right-aligned items will be left-aligned. |
| |
| Possible values: |
| |
| \list |
| \li Qt.LeftToRight (default) - Items are laid out from left to right. |
| \li Qt.RightToLeft - Items are laid out from right to left |
| \endlist |
| |
| \sa GridLayout::layoutDirection, RowLayout::layoutDirection |
| */ |
| |
| |
| /*! |
| \qmlproperty real RowLayout::spacing |
| |
| This property holds the spacing between each cell. |
| The default value is \c 5. |
| */ |
| /*! |
| \qmlproperty real ColumnLayout::spacing |
| |
| This property holds the spacing between each cell. |
| The default value is \c 5. |
| */ |
| |
| qreal QQuickLinearLayout::spacing() const |
| { |
| Q_D(const QQuickLinearLayout); |
| return d->engine.spacing(d->orientation, d->styleInfo); |
| } |
| |
| void QQuickLinearLayout::setSpacing(qreal space) |
| { |
| Q_D(QQuickLinearLayout); |
| if (qt_is_nan(space) || spacing() == space) |
| return; |
| |
| d->engine.setSpacing(space, Qt::Horizontal | Qt::Vertical); |
| invalidate(); |
| emit spacingChanged(); |
| } |
| |
| void QQuickLinearLayout::insertLayoutItems() |
| { |
| Q_D(QQuickLinearLayout); |
| QSizeF sizeHints[Qt::NSizeHints]; |
| const auto items = childItems(); |
| for (QQuickItem *child : items) { |
| Q_ASSERT(child); |
| checkAnchors(child); |
| QQuickLayoutAttached *info = nullptr; |
| |
| // Will skip all items with effective maximum width/height == 0 |
| if (shouldIgnoreItem(child, info, sizeHints)) |
| continue; |
| |
| Qt::Alignment alignment; |
| if (info) |
| alignment = info->alignment(); |
| |
| const int index = d->engine.rowCount(d->orientation); |
| d->engine.insertRow(index, d->orientation); |
| |
| int gridRow = 0; |
| int gridColumn = index; |
| if (d->orientation == Qt::Vertical) |
| qSwap(gridRow, gridColumn); |
| QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, gridRow, gridColumn, 1, 1, alignment); |
| layoutItem->setCachedSizeHints(sizeHints); |
| d->engine.insertItem(layoutItem, index); |
| } |
| } |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qquicklinearlayout_p.cpp" |