diff --git a/src/layouts/qquickgridlayoutengine.cpp b/src/layouts/qquickgridlayoutengine.cpp index bacfd965bbef56085abcd5d56b76d07fea728a78..7f65d0d5bf56c46dc92f36426d59e04415830c9f 100644 --- a/src/layouts/qquickgridlayoutengine.cpp +++ b/src/layouts/qquickgridlayoutengine.cpp @@ -98,14 +98,62 @@ static inline void combineHints(qreal ¤t, qreal fallbackHint) current = fallbackHint; } +static inline void combineSize(QSizeF &result, const QSizeF &fallbackSize) +{ + combineHints(result.rwidth(), fallbackSize.width()); + combineHints(result.rheight(), fallbackSize.height()); +} + +static inline void combineImplicitHints(QQuickLayoutAttached *info, Qt::SizeHint which, QSizeF *size) +{ + if (!info) return; + + Q_ASSERT(which == Qt::MinimumSize || which == Qt::MaximumSize); + + const QSizeF constraint(which == Qt::MinimumSize + ? QSizeF(info->minimumWidth(), info->minimumHeight()) + : QSizeF(info->maximumWidth(), info->maximumHeight())); + + if (!info->isExtentExplicitlySet(Qt::Horizontal, which)) + combineHints(size->rwidth(), constraint.width()); + if (!info->isExtentExplicitlySet(Qt::Vertical, which)) + combineHints(size->rheight(), constraint.height()); +} + /*! \internal Note: Can potentially return the attached QQuickLayoutAttached object through \a attachedInfo. It is like this is because it enables it to be reused. + + The goal of this function is to return the effective minimum, preferred and maximum size hints + that the layout will use for this item. + This function takes care of gathering all explicitly set size hints, normalizes them so + that min < pref < max. + Further, the hints _not_explicitly_ set will then be initialized with the implicit size hints, + which is usually derived from the content of the layouts (or items). + + The following table illustrates the preference of the properties used for measuring layout + items. If present, the USER properties will be preferred. If USER properties are not present, + the HINT properties will be preferred. Finally, the FALLBACK properties will be used as an + ultimate fallback. + + Note that one can query if the value of Layout.minimumWidth or Layout.maximumWidth has been + explicitly or implicitly set with QQuickLayoutAttached::isExtentExplicitlySet(). This + determines if it should be used as a USER or as a HINT value. + + + | *Minimum* | *Preferred* | *Maximum* | ++----------------+----------------------+-----------------------+--------------------------+ +|USER (explicit) | Layout.minimumWidth | Layout.preferredWidth | Layout.maximumWidth | +|HINT (implicit) | Layout.minimumWidth | implicitWidth | Layout.maximumWidth | +|FALLBACK | 0 | width | Number.POSITIVE_INFINITY | ++----------------+----------------------+-----------------------+--------------------------+ */ void QQuickGridLayoutItem::effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **attachedInfo, bool useFallbackToWidthOrHeight) { + for (int i = 0; i < Qt::NSizeHints; ++i) + cachedSizeHints[i] = QSizeF(); QQuickLayoutAttached *info = attachedLayoutObject(item, false); // First, retrieve the user-specified hints from the attached "Layout." properties if (info) { @@ -123,16 +171,16 @@ void QQuickGridLayoutItem::effectiveSizeHints_helper(QQuickItem *item, QSizeF *c for (int i = 0; i < NSizes; ++i) { SizeGetter getter = horGetters.call[i]; Q_ASSERT(getter); - cachedSizeHints[i].setWidth((info->*getter)()); + + if (info->isExtentExplicitlySet(Qt::Horizontal, (Qt::SizeHint)i)) + cachedSizeHints[i].setWidth((info->*getter)()); + getter = verGetters.call[i]; Q_ASSERT(getter); - cachedSizeHints[i].setHeight((info->*getter)()); + if (info->isExtentExplicitlySet(Qt::Vertical, (Qt::SizeHint)i)) + cachedSizeHints[i].setHeight((info->*getter)()); } - } else { - for (int i = 0; i < NSizes; ++i) - cachedSizeHints[i] = QSize(); } - cachedSizeHints[Qt::MinimumDescent] = QSize(); //### FIXME when baseline support is added QSizeF &minS = cachedSizeHints[Qt::MinimumSize]; QSizeF &prefS = cachedSizeHints[Qt::PreferredSize]; @@ -144,23 +192,21 @@ void QQuickGridLayoutItem::effectiveSizeHints_helper(QQuickItem *item, QSizeF *c // to: [10, 10, 60] normalizeHints(minS.rwidth(), prefS.rwidth(), maxS.rwidth(), descentS.rwidth()); normalizeHints(minS.rheight(), prefS.rheight(), maxS.rheight(), descentS.rheight()); -/* - The following table illustrates the preference of the properties used for measuring layout - items. If present, the USER properties will be preferred. If USER properties are not present, - the HINT 1 properties will be preferred. Finally, the HINT 2 properties will be used as an - ultimate fallback. - - | USER | HINT 1 | HINT 2 - -----+--------------------------------+-------------------+------- - MIN | Layout.minimumWidth | | 0 - PREF | Layout.preferredWidth | implicitWidth | width - MAX | Layout.maximumWidth | | 1000000000 (-1) - -----+--------------------------------+-------------------+-------- -Fixed | Layout.fillWidth | Expanding if layout, Fixed if item | - -*/ + + // All explicit values gathered, now continue to gather the implicit sizes + + //--- GATHER MAXIMUM SIZE HINTS --- + combineImplicitHints(info, Qt::MaximumSize, &maxS); + combineSize(maxS, QSizeF(std::numeric_limits<qreal>::infinity(), std::numeric_limits<qreal>::infinity())); + // implicit max or min sizes should not limit an explicitly set preferred size + expandSize(maxS, prefS); + expandSize(maxS, minS); + //--- GATHER MINIMUM SIZE HINTS --- - // They are always 0 + combineImplicitHints(info, Qt::MinimumSize, &minS); + expandSize(minS, QSizeF(0,0)); + boundSize(minS, prefS); + boundSize(minS, maxS); //--- GATHER PREFERRED SIZE HINTS --- // First, from implicitWidth/Height @@ -203,17 +249,12 @@ Fixed | Layout.fillWidth | Expanding if layout, Fixed if item | item->blockSignals(false); } } - //--- GATHER MAXIMUM SIZE HINTS --- - combineHints(cachedSizeHints[Qt::MaximumSize].rwidth(), std::numeric_limits<qreal>::infinity()); - combineHints(cachedSizeHints[Qt::MaximumSize].rheight(), std::numeric_limits<qreal>::infinity()); //--- GATHER DESCENT // ### Not implemented // Normalize again after the implicit hints have been gathered - expandSize(minS, QSizeF(0,0)); - boundSize(minS, maxS); expandSize(prefS, minS); boundSize(prefS, maxS); diff --git a/src/layouts/qquicklayout_p.h b/src/layouts/qquicklayout_p.h index 8a334677a781d77ec2e4548b39478e18308ec0a3..0239739a649e90c5b17f963b5f61169bc11a0d18 100644 --- a/src/layouts/qquicklayout_p.h +++ b/src/layouts/qquicklayout_p.h @@ -175,6 +175,22 @@ public: qreal sizeHint(Qt::SizeHint which, Qt::Orientation orientation) const; + bool isExtentExplicitlySet(Qt::Orientation o, Qt::SizeHint whichSize) const + { + switch (whichSize) { + case Qt::MinimumSize: + return o == Qt::Horizontal ? m_isMinimumWidthSet : m_isMinimumHeightSet; + case Qt::MaximumSize: + return o == Qt::Horizontal ? m_isMaximumWidthSet : m_isMaximumHeightSet; + case Qt::PreferredSize: + return true; // Layout.preferredWidth is always explicitly set + case Qt::MinimumDescent: // Not supported + case Qt::NSizeHints: + return false; + } + return false; + } + signals: void minimumWidthChanged(); void minimumHeightChanged(); diff --git a/tests/auto/controls/data/tst_rowlayout.qml b/tests/auto/controls/data/tst_rowlayout.qml index 458a2885f2604a453faf400f039d904982e978a2..e92dad47efec3eee7d36588386034eb4a1c7ab95 100644 --- a/tests/auto/controls/data/tst_rowlayout.qml +++ b/tests/auto/controls/data/tst_rowlayout.qml @@ -459,6 +459,12 @@ Item { { tag: "propagateMaximumWidth", layoutHints: [10, 20, -1], childHints: [11, 21, 30], expected:[10, 20, 30]}, { tag: "propagateAll", layoutHints: [-1, -1, -1], childHints: [10, 20, 30], expected:[10, 20, 30]}, { tag: "propagateCrazy", layoutHints: [-1, -1, -1], childHints: [40, 21, 30], expected:[30, 30, 30]}, + { tag: "expandMinToExplicitPref", layoutHints: [-1, 1, -1], childHints: [11, 21, 31], expected:[ 1, 1, 31]}, + { tag: "expandMaxToExplicitPref", layoutHints: [-1, 99, -1], childHints: [11, 21, 31], expected:[11, 99, 99]}, + { tag: "expandAllToExplicitMin", layoutHints: [99, -1, -1], childHints: [11, 21, 31], expected:[99, 99, 99]}, + { tag: "expandPrefToExplicitMin", layoutHints: [24, -1, -1], childHints: [11, 21, 31], expected:[24, 24, 31]}, + { tag: "boundPrefToExplicitMax", layoutHints: [-1, -1, 19], childHints: [11, 21, 31], expected:[11, 19, 19]}, + { tag: "boundAllToExplicitMax", layoutHints: [-1, -1, 9], childHints: [11, 21, 31], expected:[ 9, 9, 9]}, ]; } @@ -468,22 +474,21 @@ Item { function test_sizeHint(data) { var layout = layout_sizeHint_Component.createObject(container) - layout.Layout.minimumWidth = data.layoutHints[0] - layout.Layout.preferredWidth = data.layoutHints[1] - layout.Layout.maximumWidth = data.layoutHints[2] - var child = layout.children[0].children[0] + var grid = layout.children[0] + grid.Layout.minimumWidth = data.layoutHints[0] + grid.Layout.preferredWidth = data.layoutHints[1] + grid.Layout.maximumWidth = data.layoutHints[2] + + var child = grid.children[0] if (data.implicitWidth !== undefined) { child.implicitWidth = data.implicitWidth } - child.Layout.minimumWidth = data.childHints[0] child.Layout.preferredWidth = data.childHints[1] child.Layout.maximumWidth = data.childHints[2] - var grid = layout.children[0] - var preferredWidth = layout.Layout.preferredWidth >= 0 ? layout.Layout.preferredWidth : layout.implicitWidth - var effectiveSizeHintResult = [layout.Layout.minimumWidth, preferredWidth, layout.Layout.maximumWidth] + var effectiveSizeHintResult = [layout.Layout.minimumWidth, layout.implicitWidth, layout.Layout.maximumWidth] compare(effectiveSizeHintResult, data.expected) layout.destroy() }