aboutsummaryrefslogtreecommitdiffstats
path: root/src/quicklayouts/qquicklayout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quicklayouts/qquicklayout.cpp')
-rw-r--r--src/quicklayouts/qquicklayout.cpp275
1 files changed, 164 insertions, 111 deletions
diff --git a/src/quicklayouts/qquicklayout.cpp b/src/quicklayouts/qquicklayout.cpp
index 7af6bd86af..f38bdfd396 100644
--- a/src/quicklayouts/qquicklayout.cpp
+++ b/src/quicklayouts/qquicklayout.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquicklayout_p.h"
#include <QEvent>
@@ -48,7 +12,7 @@
/*!
\qmltype Layout
- \instantiates QQuickLayoutAttached
+ //! \instantiates QQuickLayoutAttached
\inqmlmodule QtQuick.Layouts
\ingroup layouts
\brief Provides attached properties for items pushed onto a \l GridLayout,
@@ -73,7 +37,11 @@
The \l fillWidth and \l fillHeight properties can either be \c true or \c false. If they are \c
false, the item's size will be fixed to its preferred size. Otherwise, it will grow or shrink
- between its minimum and maximum size as the layout is resized.
+ between its minimum and maximum size as the layout is resized. If there are multiple items
+ with \l fillWidth (or \l fillHeight) set to \c true, the layout will grow or shrink the items
+ relative to the ratio of their preferred size.
+
+ For more details on the layout algorithm, see also the \l {Qt Quick Layouts Overview}.
\note Do not bind to the x, y, width, or height properties of items in a layout,
as this would conflict with the goals of Layout, and can also cause binding loops.
@@ -117,10 +85,14 @@ QQuickLayoutAttached::QQuickLayoutAttached(QObject *parent)
m_isMaximumWidthSet(false),
m_isMaximumHeightSet(false),
m_changesNotificationEnabled(true),
+ m_isMarginsSet(false),
m_isLeftMarginSet(false),
m_isTopMarginSet(false),
m_isRightMarginSet(false),
- m_isBottomMarginSet(false)
+ m_isBottomMarginSet(false),
+ m_isAlignmentSet(false),
+ m_horizontalStretch(-1),
+ m_verticalStretch(-1)
{
}
@@ -331,15 +303,16 @@ void QQuickLayoutAttached::setMaximumImplicitSize(const QSizeF &sz)
If this property is \c true, the item will be as wide as possible while respecting
the given constraints. If the property is \c false, the item will have a fixed width
set to the preferred width.
- The default is \c false, except for layouts themselves, which default to \c true.
+ The default depends on implicit (built-in) size policy of item.
\sa fillHeight
*/
void QQuickLayoutAttached::setFillWidth(bool fill)
{
+ bool oldFillWidth = fillWidth();
m_isFillWidthSet = true;
- if (m_fillWidth != fill) {
- m_fillWidth = fill;
+ m_fillWidth = fill;
+ if (oldFillWidth != fill) {
invalidateItem();
emit fillWidthChanged();
}
@@ -351,15 +324,16 @@ void QQuickLayoutAttached::setFillWidth(bool fill)
If this property is \c true, the item will be as tall as possible while respecting
the given constraints. If the property is \c false, the item will have a fixed height
set to the preferred height.
- The default is \c false, except for layouts themselves, which default to \c true.
+ The default depends on implicit (built-in) size policy of the item.
\sa fillWidth
*/
void QQuickLayoutAttached::setFillHeight(bool fill)
{
+ bool oldFillHeight = fillHeight();
m_isFillHeightSet = true;
- if (m_fillHeight != fill) {
- m_fillHeight = fill;
+ m_fillHeight = fill;
+ if (oldFillHeight != fill) {
invalidateItem();
emit fillHeightChanged();
}
@@ -433,6 +407,7 @@ void QQuickLayoutAttached::setColumn(int column)
*/
void QQuickLayoutAttached::setAlignment(Qt::Alignment align)
{
+ m_isAlignmentSet = true;
if (align != m_alignment) {
m_alignment = align;
if (QQuickLayout *layout = parentLayout()) {
@@ -444,6 +419,80 @@ void QQuickLayoutAttached::setAlignment(Qt::Alignment align)
}
/*!
+ \qmlattachedproperty int Layout::horizontalStretchFactor
+
+ This property allows you to specify the horizontal stretch factor. By default, two identical
+ items arranged in a linear layout will have the same size, but if the first item has a
+ stretch factor of 1 and the second item has a stretch factor of 2, the first item will \e
+ aim to get 1/3 of the available space, and the second will \e aim to get 2/3 of the available
+ space. Note that, whether they become exactly 1/3 and 2/3 of the available space depends on
+ their size hints. This is because when e.g a horizontal layout is shown in its minimum width
+ all its child items will consequently also have their minimum width.
+
+ Likewise, when a horizontal layout has its preferred width, all child items will have their
+ preferred widths, and when a horizontal layout has its maximum width, all child items will have
+ their maximum widths. This strategy is applied regardless of what the individual stretch
+ factors are. As a consequence of this, stretch factors will only determine the growth rate of
+ child items \e between the preferredWidth and maximumWidth range.
+
+ The default value is \c -1, which means that no stretch factor is applied.
+
+ \note This requires that Layout::fillWidth is set to true
+
+ \since Qt 6.5
+
+ \sa verticalStretchFactor
+*/
+void QQuickLayoutAttached::setHorizontalStretchFactor(int factor)
+{
+ if (factor != m_horizontalStretch) {
+ m_horizontalStretch = factor;
+ if (QQuickLayout *layout = parentLayout()) {
+ layout->setStretchFactor(item(), factor, Qt::Horizontal);
+ invalidateItem();
+ }
+ emit horizontalStretchFactorChanged();
+ }
+}
+
+/*!
+ \qmlattachedproperty int Layout::verticalStretchFactor
+
+ This property allows you to specify the vertical stretch factor. By default, two identical
+ items arranged in a linear layout will have the same size, but if the first item has a
+ stretch factor of 1 and the second item has a stretch factor of 2, the first item will \e
+ aim to get 1/3 of the available space, and the second will \e aim to get 2/3 of the available
+ space. Note that, whether they become exactly 1/3 and 2/3 of the available space depends on
+ their size hints. This is because when e.g a vertical layout is shown in its minimum height
+ all its child items will consequently also have their minimum height.
+
+ Likewise, when a vertical layout has its preferred height, all child items will have their
+ preferred heights, and when a vertical layout has its maximum height, all child items will have
+ their maximum heights. This strategy is applied regardless of what the individual stretch
+ factors are. As a consequence of this, stretch factors will only determine the growth rate of
+ child items \e between the preferredHeight and maximumHeight range.
+
+ The default value is \c -1, which means that no stretch factor is applied.
+
+ \note This requires that Layout::fillHeight is set to true
+
+ \since Qt 6.5
+
+ \sa horizontalStretchFactor
+*/
+void QQuickLayoutAttached::setVerticalStretchFactor(int factor)
+{
+ if (factor != m_verticalStretch) {
+ m_verticalStretch = factor;
+ if (QQuickLayout *layout = parentLayout()) {
+ layout->setStretchFactor(item(), factor, Qt::Vertical);
+ invalidateItem();
+ }
+ emit verticalStretchFactorChanged();
+ }
+}
+
+/*!
\qmlattachedproperty real Layout::margins
Sets the margins outside of an item to all have the same value. The item
@@ -474,6 +523,7 @@ void QQuickLayoutAttached::setAlignment(Qt::Alignment align)
*/
void QQuickLayoutAttached::setMargins(qreal m)
{
+ m_isMarginsSet = true;
if (m == m_defaultMargins)
return;
@@ -693,28 +743,10 @@ QQuickItem *QQuickLayoutAttached::item() const
return qobject_cast<QQuickItem *>(parent());
}
-qreal QQuickLayoutPrivate::getImplicitWidth() const
+void QQuickLayoutPrivate::applySizeHints() const
{
Q_Q(const QQuickLayout);
- if (q->invalidated()) {
- QQuickLayoutPrivate *that = const_cast<QQuickLayoutPrivate*>(this);
- that->implicitWidth = q->sizeHint(Qt::PreferredSize).width();
- }
- return implicitWidth;
-}
-qreal QQuickLayoutPrivate::getImplicitHeight() const
-{
- Q_Q(const QQuickLayout);
- if (q->invalidated()) {
- QQuickLayoutPrivate *that = const_cast<QQuickLayoutPrivate*>(this);
- that->implicitHeight = q->sizeHint(Qt::PreferredSize).height();
- }
- return implicitHeight;
-}
-
-void QQuickLayoutPrivate::applySizeHints() const {
- Q_Q(const QQuickLayout);
QQuickLayout *that = const_cast<QQuickLayout*>(q);
QQuickLayoutAttached *info = attachedLayoutObject(that, true);
@@ -762,16 +794,10 @@ void QQuickLayout::updatePolish()
// Might have become "undirty" before we reach this updatePolish()
// (e.g. if somebody queried for implicitWidth it will immediately
// calculate size hints)
- if (invalidated()) {
- // Ensure that all invalidated layouts are synced and valid again. Since
- // ensureLayoutItemsUpdated() will also call applySizeHints(), and sizeHint() will call its
- // childrens sizeHint(), and sizeHint() will call ensureLayoutItemsUpdated(), this will be done
- // recursive as we want.
- // Note that we need to call ensureLayoutItemsUpdated() *before* we query width() and height(),
- // because width()/height() might return their implicitWidth/implicitHeight (e.g. for a layout
- // with no explicitly specified size, (nor anchors.fill: parent))
- ensureLayoutItemsUpdated();
- }
+ // Note that we need to call ensureLayoutItemsUpdated() *before* we query width() and height(),
+ // because width()/height() might return their implicitWidth/implicitHeight (e.g. for a layout
+ // with no explicitly specified size, (nor anchors.fill: parent))
+ ensureLayoutItemsUpdated(QQuickLayout::ApplySizeHints | QQuickLayout::Recursive);
rearrange(QSizeF(width(), height()));
m_inUpdatePolish = false;
qCDebug(lcQuickLayouts) << "updatePolish() LEAVING" << this;
@@ -786,6 +812,18 @@ void QQuickLayout::componentComplete()
d->m_isReady = true;
}
+void QQuickLayout::maybeSubscribeToBaseLineOffsetChanges(QQuickItem *item)
+{
+ QQuickLayoutAttached *info = attachedLayoutObject(item, false);
+ if (info) {
+ if (info->alignment() == Qt::AlignBaseline && static_cast<QQuickLayout*>(item->parentItem()) == this) {
+ qmlobject_connect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem()));
+ } else {
+ qmlobject_disconnect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem()));
+ }
+ }
+}
+
void QQuickLayout::invalidate(QQuickItem * /*childItem*/)
{
Q_D(QQuickLayout);
@@ -797,43 +835,26 @@ void QQuickLayout::invalidate(QQuickItem * /*childItem*/)
d->m_dirtyArrangement = true;
if (!qobject_cast<QQuickLayout *>(parentItem())) {
-
- if (m_inUpdatePolish)
- ++m_polishInsideUpdatePolish;
- else
- m_polishInsideUpdatePolish = 0;
-
- if (m_polishInsideUpdatePolish <= 2) {
- // allow at most two consecutive loops in order to respond to height-for-width
- // (e.g QQuickText changes implicitHeight when its width gets changed)
- qCDebug(lcQuickLayouts) << "QQuickLayout::invalidate(), polish()";
- polish();
+ polish();
+
+ if (m_inUpdatePolish) {
+ if (++m_polishInsideUpdatePolish > 2)
+ // allow at most two consecutive loops in order to respond to height-for-width
+ // (e.g QQuickText changes implicitHeight when its width gets changed)
+ qCDebug(lcQuickLayouts) << "Layout polish loop detected for " << this
+ << ". The polish request will still be scheduled.";
} else {
- qWarning() << "Qt Quick Layouts: Polish loop detected. Aborting after two iterations.";
+ m_polishInsideUpdatePolish = 0;
}
}
}
-bool QQuickLayout::shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&info, QSizeF *sizeHints) const
+bool QQuickLayout::shouldIgnoreItem(QQuickItem *child) const
{
- bool ignoreItem = true;
QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
- if (childPrivate->explicitVisible) {
- effectiveSizeHints_helper(child, sizeHints, &info, true);
- QSizeF effectiveMaxSize = sizeHints[Qt::MaximumSize];
- if (!effectiveMaxSize.isNull()) {
- QSizeF &prefS = sizeHints[Qt::PreferredSize];
- if (effectiveSizePolicy_helper(child, Qt::Horizontal, info) == QLayoutPolicy::Fixed)
- effectiveMaxSize.setWidth(prefS.width());
- if (effectiveSizePolicy_helper(child, Qt::Vertical, info) == QLayoutPolicy::Fixed)
- effectiveMaxSize.setHeight(prefS.height());
- }
- ignoreItem = effectiveMaxSize.isNull();
- }
-
+ bool ignoreItem = !childPrivate->explicitVisible;
if (!ignoreItem && childPrivate->isTransparentForPositioner())
ignoreItem = true;
-
return ignoreItem;
}
@@ -844,14 +865,35 @@ void QQuickLayout::checkAnchors(QQuickItem *item) const
qmlWarning(item) << "Detected anchors on an item that is managed by a layout. This is undefined behavior; use Layout.alignment instead.";
}
-void QQuickLayout::ensureLayoutItemsUpdated() const
+void QQuickLayout::ensureLayoutItemsUpdated(EnsureLayoutItemsUpdatedOptions options) const
{
Q_D(const QQuickLayout);
if (!invalidated())
return;
+ qCDebug(lcQuickLayouts) << "ENTER QQuickLayout::ensureLayoutItemsUpdated()" << this << options;
+ QQuickLayoutPrivate *priv = const_cast<QQuickLayoutPrivate*>(d);
+
+ // breadth-first
+ // must update the root first, and continue towards the leaf nodes.
+ // Otherwise, we wouldn't know which children to traverse to
const_cast<QQuickLayout*>(this)->updateLayoutItems();
+
+ // make invalidate() return true
d->m_dirty = false;
- d->applySizeHints();
+
+ if (options & Recursive) {
+ for (int i = 0; i < itemCount(); ++i) {
+ QQuickItem *itm = itemAt(i);
+ if (QQuickLayout *lay = qobject_cast<QQuickLayout*>(itm)) {
+ lay->ensureLayoutItemsUpdated(options);
+ }
+ }
+ }
+
+ // size hints are updated depth-first (parent size hints depends on their childrens size hints)
+ if (options & ApplySizeHints)
+ priv->applySizeHints();
+ qCDebug(lcQuickLayouts) << "LEAVE QQuickLayout::ensureLayoutItemsUpdated()" << this;
}
@@ -860,7 +902,7 @@ void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value)
if (change == ItemChildAddedChange) {
Q_D(QQuickLayout);
QQuickItem *item = value.item;
- qmlobject_connect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem()));
+ maybeSubscribeToBaseLineOffsetChanges(item);
QQuickItemPrivate::get(item)->addItemChangeListener(this, changeTypes);
d->m_hasItemChangeListeners = true;
qCDebug(lcQuickLayouts) << "ChildAdded" << item;
@@ -868,7 +910,7 @@ void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value)
invalidate();
} else if (change == ItemChildRemovedChange) {
QQuickItem *item = value.item;
- qmlobject_disconnect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem()));
+ maybeSubscribeToBaseLineOffsetChanges(item);
QQuickItemPrivate::get(item)->removeItemChangeListener(this, changeTypes);
qCDebug(lcQuickLayouts) << "ChildRemoved" << item;
if (isReady())
@@ -881,7 +923,7 @@ void QQuickLayout::geometryChange(const QRectF &newGeometry, const QRectF &oldGe
{
Q_D(QQuickLayout);
QQuickItem::geometryChange(newGeometry, oldGeometry);
- if (d->m_disableRearrange || !isReady() || !newGeometry.isValid())
+ if (invalidated() || d->m_disableRearrange || !isReady())
return;
qCDebug(lcQuickLayouts) << "QQuickLayout::geometryChange" << newGeometry << oldGeometry;
@@ -913,6 +955,7 @@ bool QQuickLayout::isReady() const
void QQuickLayout::deactivateRecur()
{
if (d_func()->m_hasItemChangeListeners) {
+ ensureLayoutItemsUpdated();
for (int i = 0; i < itemCount(); ++i) {
QQuickItem *item = itemAt(i);
// When deleting a layout with children, there is no reason for the children to inform the layout that their
@@ -1210,7 +1253,15 @@ void QQuickLayout::effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSiz
*/
QLayoutPolicy::Policy QQuickLayout::effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info)
{
- bool fillExtent = false;
+ bool fillExtent([&]{
+ QLayoutPolicy::Policy policy{QLayoutPolicy::Fixed};
+ if (item && QGuiApplication::testAttribute(Qt::AA_QtQuickUseDefaultSizePolicy)) {
+ QLayoutPolicy sizePolicy = QQuickItemPrivate::get(item)->sizePolicy();
+ policy = (orientation == Qt::Horizontal) ? sizePolicy.horizontalPolicy() : sizePolicy.verticalPolicy();
+ }
+ return (policy == QLayoutPolicy::Preferred);
+ }());
+
bool isSet = false;
if (info) {
if (orientation == Qt::Horizontal) {
@@ -1223,8 +1274,8 @@ QLayoutPolicy::Policy QQuickLayout::effectiveSizePolicy_helper(QQuickItem *item,
}
if (!isSet && qobject_cast<QQuickLayout*>(item))
fillExtent = true;
- return fillExtent ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed;
+ return fillExtent ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed;
}
void QQuickLayout::_q_dumpLayoutTree() const
@@ -1306,3 +1357,5 @@ void QQuickLayout::dumpLayoutTreeRecursive(int level, QString &buf) const
}
QT_END_NAMESPACE
+
+#include "moc_qquicklayout_p.cpp"