diff --git a/src/controls/QskQuick.cpp b/src/controls/QskQuick.cpp index 3012f862..d08f5fbc 100644 --- a/src/controls/QskQuick.cpp +++ b/src/controls/QskQuick.cpp @@ -6,6 +6,7 @@ #include "QskQuick.h" #include "QskControl.h" #include "QskFunctions.h" +#include "QskLayoutElement.h" #include QSK_QT_PRIVATE_BEGIN @@ -419,221 +420,23 @@ QSizeF qskEffectiveSizeHint( const QQuickItem* item, return hint; } -static QSizeF qskBoundedConstraint( const QQuickItem* item, - const QSizeF& constraint, QskSizePolicy policy ) -{ - Qt::Orientation orientation; - - if ( constraint.width() >= 0.0 ) - { - orientation = Qt::Horizontal; - } - else if ( constraint.height() >= 0.0 ) - { - orientation = Qt::Vertical; - } - else - { - return constraint; - } - - const auto whichMin = policy.effectiveSizeHintType( Qt::MinimumSize, orientation ); - const auto whichMax = policy.effectiveSizeHintType( Qt::MaximumSize, orientation ); - - const auto hintMin = qskEffectiveSizeHint( item, whichMin ); - const auto hintMax = ( whichMax == whichMin ) - ? hintMin : qskEffectiveSizeHint( item, whichMax ); - - QSizeF size = constraint; - - if ( orientation == Qt::Horizontal ) - { - if ( hintMax.width() >= 0.0 ) - size.rwidth() = qMin( size.width(), hintMax.width() ); - - size.rwidth() = qMax( size.width(), hintMin.width() ); - } - else - { - if ( hintMax.height() >= 0.0 ) - size.rheight() = qMin( size.height(), hintMax.height() ); - - size.rheight() = qMax( size.height(), hintMin.height() ); - } - - return size; -} - QSizeF qskSizeConstraint( const QQuickItem* item, Qt::SizeHint which, const QSizeF& constraint ) { if ( item == nullptr ) return QSizeF( 0, 0 ); - if ( constraint.isValid() ) - return constraint; - - auto policy = qskSizePolicy( item ); - - bool ignoreWidth = false; - bool ignoreHeight = false; - - if ( which == Qt::PreferredSize ) - { - /* - First we are checking the IgnoreFlag, to avoid doing - pointless calculations. - */ - ignoreWidth = policy.policy( Qt::Horizontal ) & QskSizePolicy::IgnoreFlag; - ignoreHeight = policy.policy( Qt::Vertical ) & QskSizePolicy::IgnoreFlag; - - if ( ( ignoreWidth && ignoreHeight ) - || ( ignoreWidth && constraint.height() >= 0.0 ) - || ( ignoreHeight && constraint.width() >= 0.0 ) ) - { - return QSizeF(); - } - } - - const auto whichH = policy.effectiveSizeHintType( which, Qt::Horizontal ); - const auto whichV = policy.effectiveSizeHintType( which, Qt::Vertical ); - - QSizeF size; - - int constraintType = QskSizePolicy::Unconstrained; - - /* - We apply a constraint - even, when the policy is unconstrained. - Do we really want to do this ??? - */ - if ( constraint.height() >= 0.0 ) - { - const auto c = qskBoundedConstraint( item, constraint, policy ); - size = qskEffectiveSizeHint( item, whichV, c ); - - if ( ( whichH != whichV ) || ( size.height() != c.height() ) ) - constraintType = QskSizePolicy::WidthForHeight; - } - else if ( constraint.width() >= 0.0 ) - { - const auto c = qskBoundedConstraint( item, constraint, policy ); - size = qskEffectiveSizeHint( item, whichH, c ); - - if ( ( whichV != whichH ) || ( size.width() != c.height() ) ) - constraintType = QskSizePolicy::HeightForWidth; - } - else - { - constraintType = policy.constraintType(); - - switch( constraintType ) - { - case QskSizePolicy::WidthForHeight: - { - size = qskEffectiveSizeHint( item, whichV ); - break; - } - case QskSizePolicy::HeightForWidth: - { - size = qskEffectiveSizeHint( item, whichH ); - break; - } - default: - { - if ( whichV != whichH ) - { - if ( !ignoreWidth ) - size.rwidth() = qskEffectiveSizeHint( item, whichH ).width(); - - if ( !ignoreHeight ) - size.rheight() = qskEffectiveSizeHint( item, whichV ).height(); - } - else - { - size = qskEffectiveSizeHint( item, whichH ); - } - } - } - } - - switch( constraintType ) - { - case QskSizePolicy::HeightForWidth: - { - const QSizeF c( size.width(), -1.0 ); - size.rheight() = qskEffectiveSizeHint( item, whichV, c ).height(); - break; - } - case QskSizePolicy::WidthForHeight: - { - const QSizeF c( -1.0, size.height() ); - size.rwidth() = qskEffectiveSizeHint( item, whichH, c ).width(); - break; - } - } - - if ( ignoreWidth || constraint.width() >= 0.0 ) - size.rwidth() = -1.0; - - if ( ignoreHeight || constraint.height() >= 0.0 ) - size.rheight() = -1.0; - - return size; + const QskItemLayoutElement layoutElement( item ); + return layoutElement.sizeConstraint( which, constraint ); } QSizeF qskConstrainedItemSize( const QQuickItem* item, const QSizeF& size ) { - if ( item == nullptr || ( size.width() <= 0.0 && size.height() <= 0.0 ) ) + if ( item == nullptr ) return QSizeF( 0.0, 0.0 ); - QSizeF min, max; - - const auto policy = qskSizePolicy( item ); - - switch( policy.constraintType() ) - { - case QskSizePolicy::WidthForHeight: - { - const auto constraint = qskBoundedConstraint( item, - QSizeF( -1.0, size.height() ), policy ); - - min = qskSizeConstraint( item, Qt::MinimumSize, constraint ); - max = qskSizeConstraint( item, Qt::MaximumSize, constraint ); - - min.rheight() = max.rheight() = constraint.height(); - break; - } - case QskSizePolicy::HeightForWidth: - { - const auto constraint = qskBoundedConstraint( item, - QSizeF( size.width(), -1.0 ), policy ); - - min = qskSizeConstraint( item, Qt::MinimumSize, constraint ); - max = qskSizeConstraint( item, Qt::MaximumSize, constraint ); - - min.rwidth() = max.rwidth() = constraint.width(); - break; - } - default: - { - min = qskSizeConstraint( item, Qt::MinimumSize, QSizeF() ); - max = qskSizeConstraint( item, Qt::MaximumSize, QSizeF() ); - } - } - - qreal width = size.width(); - qreal height = size.height(); - - if ( max.width() >= 0.0 ) - width = qMin( width, max.width() ); - - if ( max.height() >= 0.0 ) - height = qMin( height, max.height() ); - - width = qMax( width, min.width() ); - height = qMax( height, min.height() ); - - return QSizeF( width, height ); + const QskItemLayoutElement layoutElement( item ); + return layoutElement.constrainedSize( size ); } QRectF qskConstrainedItemRect( const QQuickItem* item, diff --git a/src/layouts/QskGridLayoutEngine.cpp b/src/layouts/QskGridLayoutEngine.cpp index 0ceacc7c..92cce6bd 100644 --- a/src/layouts/QskGridLayoutEngine.cpp +++ b/src/layouts/QskGridLayoutEngine.cpp @@ -6,6 +6,7 @@ #include "QskGridLayoutEngine.h" #include "QskLayoutMetrics.h" #include "QskLayoutChain.h" +#include "QskLayoutElement.h" #include "QskSizePolicy.h" #include "QskQuick.h" @@ -162,6 +163,13 @@ namespace namespace { + inline QskLayoutMetrics qskItemMetrics( + QQuickItem* item, Qt::Orientation orientation, qreal constraint ) + { + const QskItemLayoutElement layoutItem( item ); + return layoutItem.metrics( orientation, constraint ); + } + class Element { public: @@ -543,6 +551,30 @@ QQuickItem* QskGridLayoutEngine::itemAt( int index ) const return nullptr; } +QskSizePolicy QskGridLayoutEngine::sizePolicyAt( int index ) const +{ + return qskSizePolicy( itemAt( index ) ); +} + +int QskGridLayoutEngine::indexOf( const QQuickItem* item ) const +{ + if ( item ) + { + /* + indexOf is often called after inserting an item to + set additinal properties. So we search in reverse order + */ + + for ( int i = count() - 1; i >= 0; --i ) + { + if ( itemAt( i ) == item ) + return i; + } + } + + return -1; +} + QSizeF QskGridLayoutEngine::spacerAt( int index ) const { if ( const auto element = m_data->elementAt( index ) ) @@ -601,7 +633,12 @@ void QskGridLayoutEngine::layoutItems() if ( qskIsAdjustableByLayout( item ) ) { const auto grid = m_data->effectiveGrid( element ); - layoutItem( item, grid ); + + const QskItemLayoutElement layoutElement( item ); + + const auto rect = geometryAt( &layoutElement, grid ); + if ( rect.size().isValid() ) + qskSetItemGeometry( item, rect ); } } } @@ -650,8 +687,9 @@ void QskGridLayoutEngine::setupChain( Qt::Orientation orientation, constraint = qskSegmentLength( constraints, grid.left(), grid.right() ); auto cell = element.cell( orientation ); + if ( element.item() ) - cell.metrics = layoutMetrics( element.item(), orientation, constraint ); + cell.metrics = qskItemMetrics( element.item(), orientation, constraint ); chain.expandCell( grid.top(), cell ); } @@ -677,7 +715,7 @@ void QskGridLayoutEngine::setupChain( Qt::Orientation orientation, constraint = qskSegmentLength( constraints, grid.left(), grid.right() ); auto cell = element->cell( orientation ); - cell.metrics = layoutMetrics( element->item(), orientation, constraint ); + cell.metrics = qskItemMetrics( element->item(), orientation, constraint ); chain.expandCells( grid.top(), grid.height(), cell ); } diff --git a/src/layouts/QskGridLayoutEngine.h b/src/layouts/QskGridLayoutEngine.h index 00e6f9a4..d941865c 100644 --- a/src/layouts/QskGridLayoutEngine.h +++ b/src/layouts/QskGridLayoutEngine.h @@ -39,12 +39,14 @@ class QskGridLayoutEngine : public QskLayoutEngine2D bool removeAt( int index ); bool clear(); - QQuickItem* itemAt( int index ) const override final; + QQuickItem* itemAt( int index ) const; QSizeF spacerAt( int index ) const; QQuickItem* itemAt( int row, int column ) const; int indexAt( int row, int column ) const; + int indexOf( const QQuickItem* ) const; + bool setGridAt( int index, const QRect& ); QRect gridAt( int index ) const; @@ -53,6 +55,7 @@ class QskGridLayoutEngine : public QskLayoutEngine2D void transpose(); private: + QskSizePolicy sizePolicyAt( int index ) const override final; void layoutItems() override; int effectiveCount( Qt::Orientation ) const override; diff --git a/src/layouts/QskLayoutElement.cpp b/src/layouts/QskLayoutElement.cpp new file mode 100644 index 00000000..e3607397 --- /dev/null +++ b/src/layouts/QskLayoutElement.cpp @@ -0,0 +1,385 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#include "QskLayoutElement.h" +#include "QskLayoutMetrics.h" +#include "QskSizePolicy.h" + +static inline bool qskCanGrow( + const QskLayoutElement* element, Qt::Orientation orientation ) +{ + const quint32 growFlags = QskSizePolicy::GrowFlag | QskSizePolicy::ExpandFlag; + return element->sizePolicy().policy( orientation ) & growFlags; +} + +QskLayoutElement::QskLayoutElement() +{ +} + +QskLayoutElement::~QskLayoutElement() +{ +} + +QSizeF QskLayoutElement::sizeConstraint( + Qt::SizeHint which, const QSizeF& constraint ) const +{ + if ( constraint.isValid() ) + return constraint; + + auto policy = sizePolicy(); + + bool ignoreWidth = false; + bool ignoreHeight = false; + + if ( which == Qt::PreferredSize ) + { + /* + First we are checking the IgnoreFlag, to avoid doing + pointless calculations. + */ + ignoreWidth = policy.policy( Qt::Horizontal ) & QskSizePolicy::IgnoreFlag; + ignoreHeight = policy.policy( Qt::Vertical ) & QskSizePolicy::IgnoreFlag; + + if ( ( ignoreWidth && ignoreHeight ) + || ( ignoreWidth && constraint.height() >= 0.0 ) + || ( ignoreHeight && constraint.width() >= 0.0 ) ) + { + return QSizeF(); + } + } + + const auto whichH = policy.effectiveSizeHintType( which, Qt::Horizontal ); + const auto whichV = policy.effectiveSizeHintType( which, Qt::Vertical ); + + QSizeF size; + + int constraintType = QskSizePolicy::Unconstrained; + + /* + We apply a constraint - even, when the policy is unconstrained. + Do we really want to do this ??? + */ + if ( constraint.height() >= 0.0 ) + { + const auto h = boundedSize( Qt::Vertical, constraint.height() ); + size = sizeHint( whichV, QSizeF( -1.0, h ) ); + + if ( ( whichH != whichV ) || ( size.height() != h ) ) + constraintType = QskSizePolicy::WidthForHeight; + } + else if ( constraint.width() >= 0.0 ) + { + const auto w = boundedSize( Qt::Horizontal, constraint.width() ); + size = sizeHint( whichH, QSizeF( w, -1.0 ) ); + + if ( ( whichV != whichH ) || ( size.width() != w ) ) + constraintType = QskSizePolicy::HeightForWidth; + } + else + { + constraintType = policy.constraintType(); + + switch( constraintType ) + { + case QskSizePolicy::WidthForHeight: + { + size = sizeHint( whichV ); + break; + } + case QskSizePolicy::HeightForWidth: + { + size = sizeHint( whichH ); + break; + } + default: + { + if ( whichV != whichH ) + { + if ( !ignoreWidth ) + size.rwidth() = sizeHint( whichH ).width(); + + if ( !ignoreHeight ) + size.rheight() = sizeHint( whichV ).height(); + } + else + { + size = sizeHint( whichH ); + } + } + } + } + + switch( constraintType ) + { + case QskSizePolicy::HeightForWidth: + { + const QSizeF c( size.width(), -1.0 ); + size.rheight() = sizeHint( whichV, c ).height(); + break; + } + case QskSizePolicy::WidthForHeight: + { + const QSizeF c( -1.0, size.height() ); + size.rwidth() = sizeHint( whichH, c ).width(); + break; + } + } + + if ( ignoreWidth || constraint.width() >= 0.0 ) + size.rwidth() = -1.0; + + if ( ignoreHeight || constraint.height() >= 0.0 ) + size.rheight() = -1.0; + + return size; +} + +QSizeF QskLayoutElement::constrainedSize( const QSizeF& size ) const +{ + if ( size.width() <= 0.0 && size.height() <= 0.0 ) + return QSizeF( 0.0, 0.0 ); + + QSizeF min, max; + + const auto policy = sizePolicy(); + + switch( policy.constraintType() ) + { + case QskSizePolicy::WidthForHeight: + { + const qreal h = boundedSize( Qt::Vertical, size.height() ); + + min = sizeConstraint( Qt::MinimumSize, QSizeF( -1.0, h ) ); + max = sizeConstraint( Qt::MaximumSize, QSizeF( -1.0, h ) ); + + min.rheight() = max.rheight() = h; + break; + } + case QskSizePolicy::HeightForWidth: + { + const qreal w = boundedSize( Qt::Horizontal, size.width() ); + + min = sizeConstraint( Qt::MinimumSize, QSizeF( w, -1.0 ) ); + max = sizeConstraint( Qt::MaximumSize, QSizeF( w, -1.0 ) ); + + min.rwidth() = max.rwidth() = w; + break; + } + default: + { + min = sizeConstraint( Qt::MinimumSize, QSizeF() ); + max = sizeConstraint( Qt::MaximumSize, QSizeF() ); + } + } + + qreal width = size.width(); + qreal height = size.height(); + + if ( max.width() >= 0.0 ) + width = qMin( width, max.width() ); + + if ( max.height() >= 0.0 ) + height = qMin( height, max.height() ); + + width = qMax( width, min.width() ); + height = qMax( height, min.height() ); + + return QSizeF( width, height ); +} + +QskLayoutMetrics QskLayoutElement::metrics( + Qt::Orientation orientation, qreal constraint ) const +{ + const auto policy = sizePolicy().policy( orientation ); + + qreal minimum, preferred, maximum; + + const auto expandFlags = QskSizePolicy::GrowFlag | QskSizePolicy::ExpandFlag; + + if ( ( policy & QskSizePolicy::ShrinkFlag ) && + ( policy & expandFlags ) && ( policy & QskSizePolicy::IgnoreFlag ) ) + { + // we don't need to calculate the preferred size + + minimum = metric( orientation, Qt::MinimumSize, -1.0 ); + maximum = metric( orientation, Qt::MaximumSize, -1.0 ); + preferred = minimum; + } + else + { + if ( constraint >= 0.0 ) + { + if ( !( policy & QskSizePolicy::ConstrainedFlag ) ) + constraint = -1.0; + } + + preferred = metric( orientation, Qt::PreferredSize, constraint ); + + if ( policy & QskSizePolicy::ShrinkFlag ) + minimum = metric( orientation, Qt::MinimumSize, -1.0 ); + else + minimum = preferred; + + if ( policy & expandFlags ) + maximum = metric( orientation, Qt::MaximumSize, -1.0 ); + else + maximum = preferred; + + if ( policy & QskSizePolicy::IgnoreFlag ) + preferred = minimum; + } + + return QskLayoutMetrics( minimum, preferred, maximum ); +} + +qreal QskLayoutElement::metric( Qt::Orientation orientation, + Qt::SizeHint which, qreal constraint ) const +{ + qreal metric = 0.0; + + if ( which == Qt::MinimumSize ) + { + const auto hint = sizeHint( Qt::MinimumSize ); + + if ( orientation == Qt::Horizontal ) + metric = hint.width(); + else + metric = hint.height(); + + if ( metric < 0.0 ) + metric = 0.0; + } + else if ( which == Qt::MaximumSize ) + { + const auto hint = sizeHint( Qt::MaximumSize ); + + if ( orientation == Qt::Horizontal ) + metric = hint.width(); + else + metric = hint.height(); + + if ( metric < 0.0 ) + metric = QskLayoutMetrics::unlimited; + } + else if ( which == Qt::PreferredSize ) + { + const auto constraintType = sizePolicy().constraintType(); + + if ( constraintType == QskSizePolicy::Unconstrained ) + { + const auto hint = sizeHint( Qt::PreferredSize ); + + if ( orientation == Qt::Horizontal ) + metric = hint.width(); + else + metric = hint.height(); + } + else + { + if ( constraint < 0.0 ) + { + auto hint = sizeHint( Qt::PreferredSize ); + + if ( orientation == Qt::Horizontal ) + { + if ( constraintType == QskSizePolicy::WidthForHeight ) + hint.setWidth( widthForHeight( hint.height() ) ); + + metric = hint.width(); + } + else + { + if ( constraintType == QskSizePolicy::HeightForWidth ) + hint.setHeight( heightForWidth( hint.width() ) ); + + metric = hint.height(); + } + } + else + { + if ( orientation == Qt::Horizontal ) + { + if ( !qskCanGrow( this, Qt::Vertical ) ) + { + const auto maxH = sizeHint( Qt::PreferredSize ).height(); + + if ( maxH >= 0.0 ) + constraint = qMin( constraint, maxH ); + } + + metric = widthForHeight( constraint ); + } + else + { + if ( !qskCanGrow( this, Qt::Horizontal ) ) + { + const auto maxW = sizeHint( Qt::PreferredSize ).width(); + + if ( maxW >= 0.0 ) + constraint = qMin( constraint, maxW ); + } + + metric = heightForWidth( constraint ); + } + } + } + + if ( metric < 0.0 ) + metric = 0.0; + } + + return metric; +} + +qreal QskLayoutElement::boundedSize( Qt::Orientation orientation, qreal size ) const +{ + const auto policy = sizePolicy(); + + const auto whichMin = policy.effectiveSizeHintType( Qt::MinimumSize, orientation ); + const auto whichMax = policy.effectiveSizeHintType( Qt::MaximumSize, orientation ); + + const auto hintMin = sizeHint( whichMin ); + const auto hintMax = ( whichMax == whichMin ) ? hintMin : sizeHint( whichMax ); + + if ( orientation == Qt::Horizontal ) + { + if ( hintMax.width() >= 0.0 ) + size = qMin( size, hintMax.width() ); + + size = qMax( size, hintMin.width() ); + } + else + { + if ( hintMax.height() >= 0.0 ) + size = qMin( size, hintMax.height() ); + + size = qMax( size, hintMin.height() ); + } + + return size; +} + +#include "QskQuick.h" + +QskItemLayoutElement::QskItemLayoutElement( const QQuickItem* item ) + : m_item( item ) +{ +} + +QskSizePolicy QskItemLayoutElement::sizePolicy() const +{ + return qskSizePolicy( m_item ); +} + +QSizeF QskItemLayoutElement::sizeHint( Qt::SizeHint which, + const QSizeF& constraint ) const +{ + return qskEffectiveSizeHint( m_item, which, constraint ); +} + +Qt::Alignment QskItemLayoutElement::alignment() const +{ + return qskLayoutAlignmentHint( m_item ); +} diff --git a/src/layouts/QskLayoutElement.h b/src/layouts/QskLayoutElement.h new file mode 100644 index 00000000..a465e7b7 --- /dev/null +++ b/src/layouts/QskLayoutElement.h @@ -0,0 +1,71 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_LAYOUT_ELEMENT_H +#define QSK_LAYOUT_ELEMENT_H + +#include "QskGlobal.h" +#include +#include + +class QskSizePolicy; +class QskLayoutMetrics; + +class QskLayoutElement +{ + public: + QskLayoutElement(); + virtual ~QskLayoutElement(); + + QskLayoutMetrics metrics( Qt::Orientation, qreal constraint ) const; + + virtual QskSizePolicy sizePolicy() const = 0; + virtual Qt::Alignment alignment() const = 0; + + QSizeF constrainedSize( const QSizeF& ) const; + + QSizeF sizeConstraint( Qt::SizeHint, const QSizeF& constraint ) const; + + private: + Q_DISABLE_COPY( QskLayoutElement ) + + qreal metric( Qt::Orientation, Qt::SizeHint, qreal constraint ) const; + + virtual QSizeF sizeHint( Qt::SizeHint, + const QSizeF& constraint = QSizeF() ) const = 0; + + qreal heightForWidth( qreal ) const; + qreal widthForHeight( qreal ) const; + + qreal boundedSize( Qt::Orientation, qreal ) const; +}; + +inline qreal QskLayoutElement::heightForWidth( qreal width ) const +{ + return sizeHint( Qt::PreferredSize, QSizeF( width, -1.0 ) ).height(); +} + +inline qreal QskLayoutElement::widthForHeight( qreal height ) const +{ + return sizeHint( Qt::PreferredSize, QSizeF( -1.0, height ) ).width(); +} + +class QQuickItem; + +class QskItemLayoutElement final : public QskLayoutElement +{ + public: + QskItemLayoutElement( const QQuickItem* ); + + QskSizePolicy sizePolicy() const override; + Qt::Alignment alignment() const override; + + private: + QSizeF sizeHint( Qt::SizeHint, const QSizeF& ) const override; + + const QQuickItem* m_item = nullptr; +}; + +#endif diff --git a/src/layouts/QskLayoutEngine2D.cpp b/src/layouts/QskLayoutEngine2D.cpp index 34c484d6..11680474 100644 --- a/src/layouts/QskLayoutEngine2D.cpp +++ b/src/layouts/QskLayoutEngine2D.cpp @@ -5,100 +5,11 @@ #include "QskLayoutEngine2D.h" #include "QskLayoutChain.h" -#include "QskLayoutMetrics.h" -#include "QskControl.h" -#include "QskQuick.h" +#include "QskLayoutElement.h" +#include "QskFunctions.h" #include -static QSizeF qskItemConstraint( const QQuickItem* item, const QSizeF& constraint ) -{ - QSizeF hint( 0, 0 ); - - const auto sizePolicy = qskSizePolicy( item ); - - const auto constraintType = sizePolicy.constraintType(); - const auto which = Qt::PreferredSize; - - if ( constraintType != QskSizePolicy::Unconstrained ) - { - const quint32 growFlags = QskSizePolicy::GrowFlag | QskSizePolicy::ExpandFlag; - - if ( constraint.width() > 0 ) // && constrainedType == HeightForWidth ?? - { - qreal w = constraint.width(); - - if ( !( sizePolicy.policy( Qt::Horizontal ) & growFlags ) ) - { - const auto maxW = qskEffectiveSizeHint( item, which ).width(); - - if ( maxW >= 0.0 ) - w = qMin( w, maxW ); - } - - hint.setWidth( w ); - hint.setHeight( qskHeightForWidth( item, which, w ) ); - } - else if ( constraint.height() > 0 ) // && constrainedType == WidthForHeight ?? - { - qreal h = constraint.height(); - - if ( !( sizePolicy.policy( Qt::Vertical ) & growFlags ) ) - { - const auto maxH = qskEffectiveSizeHint( item, which ).height(); - - if ( maxH >= 0.0 ) - h = qMin( h, maxH ); - } - - hint.setWidth( qskWidthForHeight( item, which, h ) ); - hint.setHeight( h ); - } - else - { - hint = qskEffectiveSizeHint( item, which ); - - if ( constraintType == QskSizePolicy::WidthForHeight ) - hint.setWidth( qskWidthForHeight( item, which, hint.height() ) ); - else - hint.setHeight( qskHeightForWidth( item, which, hint.width() ) ); - } - } - else - { - hint = qskEffectiveSizeHint( item, which ); - } - - hint = hint.expandedTo( QSizeF( 0.0, 0.0 ) ); - - return hint; -} - -static inline qreal qskLayoutConstraint( const QQuickItem* item, - Qt::Orientation orientation, qreal constraint ) -{ - if ( orientation == Qt::Horizontal ) - return qskItemConstraint( item, QSizeF( -1.0, constraint ) ).width(); - else - return qskItemConstraint( item, QSizeF( constraint, -1.0 ) ).height(); -} - -static inline qreal qskEffectiveConstraint( const QQuickItem* item, - Qt::SizeHint which, Qt::Orientation orientation ) -{ - qreal value; - - if ( orientation == Qt::Horizontal ) - value = qskEffectiveSizeHint( item, which ).width(); - else - value = qskEffectiveSizeHint( item, which ).height(); - - if ( value < 0.0 ) - value = ( which == Qt::MaximumSize ) ? QskLayoutMetrics::unlimited : 0.0; - - return value; -} - namespace { class LayoutData @@ -289,25 +200,6 @@ bool QskLayoutEngine2D::setExtraSpacingAt( Qt::Edges edges ) return true; } -int QskLayoutEngine2D::indexOf( const QQuickItem* item ) const -{ - if ( item ) - { - /* - indexOf is often called after inserting an item to - set additinal properties. So we search in reverse order - */ - - for ( int i = count() - 1; i >= 0; --i ) - { - if ( itemAt( i ) == item ) - return i; - } - } - - return -1; -} - Qt::Edges QskLayoutEngine2D::extraSpacingAt() const { return static_cast< Qt::Edges >( m_data->extraSpacingAt ); @@ -343,26 +235,30 @@ void QskLayoutEngine2D::setGeometries( const QRectF& rect ) m_data->layoutData = nullptr; } -void QskLayoutEngine2D::layoutItem( QQuickItem* item, const QRect& grid ) const +QRectF QskLayoutEngine2D::geometryAt( + const QskLayoutElement* element, const QRect& grid ) const { - auto layoutData = m_data->layoutData; + const auto layoutData = m_data->layoutData; - if ( layoutData == nullptr || item == nullptr ) - return; - - auto alignment = qskLayoutAlignmentHint( item ); - alignment = m_data->effectiveAlignment( alignment ); - - auto rect = layoutData->geometryAt( grid ); - rect = qskConstrainedItemRect( item, rect, alignment ); - - if ( layoutData->direction == Qt::RightToLeft ) + if ( layoutData && element ) { - const auto& r = layoutData->rect; - rect.moveRight( r.right() - ( rect.left() - r.left() ) ); + auto rect = layoutData->geometryAt( grid ); + + const auto size = element->constrainedSize( rect.size() ); + const auto alignment = m_data->effectiveAlignment( element->alignment() ); + + rect = qskAlignedRectF( rect, size, alignment ); + + if ( layoutData->direction == Qt::RightToLeft ) + { + const auto& r = layoutData->rect; + rect.moveRight( r.right() - ( rect.left() - r.left() ) ); + } + + return rect; } - qskSetItemGeometry( item, rect ); + return QRectF( 0, 0, -1, -1 ); } qreal QskLayoutEngine2D::widthForHeight( qreal height ) const @@ -421,7 +317,7 @@ QSizeF QskLayoutEngine2D::sizeHint( if ( constraint.height() >= 0 || constraint.width() >= 0 ) { /* - As none of the items have the Constrained flag the constraint + As none of the elements has the Constrained flag the constraint will have no effect and we ignore it to make use of the cached results from unconstrained requests */ @@ -477,54 +373,6 @@ QSizeF QskLayoutEngine2D::sizeHint( return hint; } -QskLayoutMetrics QskLayoutEngine2D::layoutMetrics( const QQuickItem* item, - Qt::Orientation orientation, qreal constraint ) const -{ - if ( item == nullptr ) - return QskLayoutMetrics(); - - const auto policy = qskSizePolicy( item ).policy( orientation ); - - if ( constraint >= 0.0 ) - { - if ( !( policy & QskSizePolicy::ConstrainedFlag ) ) - constraint = -1.0; - } - - qreal minimum, preferred, maximum; - - const auto expandFlags = QskSizePolicy::GrowFlag | QskSizePolicy::ExpandFlag; - - if ( ( policy & QskSizePolicy::ShrinkFlag ) && - ( policy & expandFlags ) && ( policy & QskSizePolicy::IgnoreFlag ) ) - { - // we don't need to calculate the preferred size - - minimum = qskEffectiveConstraint( item, Qt::MinimumSize, orientation ); - maximum = qskEffectiveConstraint( item, Qt::MaximumSize, orientation ); - preferred = minimum; - } - else - { - preferred = qskLayoutConstraint( item, orientation, constraint ); - - if ( policy & QskSizePolicy::ShrinkFlag ) - minimum = qskEffectiveConstraint( item, Qt::MinimumSize, orientation ); - else - minimum = preferred; - - if ( policy & expandFlags ) - maximum = qskEffectiveConstraint( item, Qt::MaximumSize, orientation ); - else - maximum = preferred; - - if ( policy & QskSizePolicy::IgnoreFlag ) - preferred = minimum; - } - - return QskLayoutMetrics( minimum, preferred, maximum ); -} - void QskLayoutEngine2D::setupChain( Qt::Orientation orientation ) const { setupChain( orientation, QskLayoutChain::Segments() ); @@ -631,7 +479,7 @@ QskSizePolicy::ConstraintType QskLayoutEngine2D::constraintType() const for ( int i = 0; i < count(); i++ ) { - const auto type = qskSizePolicy( itemAt( i ) ).constraintType(); + const auto type = sizePolicyAt( i ).constraintType(); if ( type != QskSizePolicy::Unconstrained ) { diff --git a/src/layouts/QskLayoutEngine2D.h b/src/layouts/QskLayoutEngine2D.h index 85fb1923..6b774593 100644 --- a/src/layouts/QskLayoutEngine2D.h +++ b/src/layouts/QskLayoutEngine2D.h @@ -13,8 +13,7 @@ #include #include -class QQuickItem; -class QskLayoutMetrics; +class QskLayoutElement; class QskLayoutEngine2D { @@ -24,10 +23,6 @@ class QskLayoutEngine2D virtual int count() const = 0; - virtual QQuickItem* itemAt( int index ) const = 0; - - int indexOf( const QQuickItem* ) const; - int rowCount() const; int columnCount() const; @@ -55,10 +50,7 @@ class QskLayoutEngine2D void setGeometries( const QRectF& ); protected: - - void layoutItem( QQuickItem*, const QRect& grid ) const; - QskLayoutMetrics layoutMetrics( const QQuickItem*, - Qt::Orientation, qreal constraint ) const; + QRectF geometryAt( const QskLayoutElement*, const QRect& grid ) const; enum { @@ -79,6 +71,8 @@ class QskLayoutEngine2D virtual void invalidateElementCache() = 0; QskSizePolicy::ConstraintType constraintType() const; + virtual QskSizePolicy sizePolicyAt( int index ) const = 0; + void setupChain( Qt::Orientation ) const; void setupChain( Qt::Orientation, const QskLayoutChain::Segments& ) const; diff --git a/src/layouts/QskLinearLayoutEngine.cpp b/src/layouts/QskLinearLayoutEngine.cpp index a50f8e49..2305aa76 100644 --- a/src/layouts/QskLinearLayoutEngine.cpp +++ b/src/layouts/QskLinearLayoutEngine.cpp @@ -6,6 +6,7 @@ #include "QskLinearLayoutEngine.h" #include "QskLayoutMetrics.h" #include "QskLayoutChain.h" +#include "QskLayoutElement.h" #include "QskSizePolicy.h" #include "QskQuick.h" @@ -13,6 +14,13 @@ namespace { + inline QskLayoutMetrics qskItemMetrics( + QQuickItem* item, Qt::Orientation orientation, qreal constraint ) + { + const QskItemLayoutElement layoutItem( item ); + return layoutItem.metrics( orientation, constraint ); + } + class Element { public: @@ -331,6 +339,30 @@ bool QskLinearLayoutEngine::clear() return true; } +QskSizePolicy QskLinearLayoutEngine::sizePolicyAt( int index ) const +{ + return qskSizePolicy( itemAt( index ) ); +} + +int QskLinearLayoutEngine::indexOf( const QQuickItem* item ) const +{ + if ( item ) + { + /* + indexOf is often called after inserting an item to + set additinal properties. So we search in reverse order + */ + + for ( int i = count() - 1; i >= 0; --i ) + { + if ( itemAt( i ) == item ) + return i; + } + } + + return -1; +} + QQuickItem* QskLinearLayoutEngine::itemAt( int index ) const { if ( const auto element = m_data->elementAt( index ) ) @@ -362,7 +394,12 @@ void QskLinearLayoutEngine::layoutItems() if ( qskIsAdjustableByLayout( item ) ) { const QRect grid( col, row, 1, 1 ); - layoutItem( item, grid ); + + const QskItemLayoutElement layoutElement( item ); + + const auto rect = geometryAt( &layoutElement, grid ); + if ( rect.size().isValid() ) + qskSetItemGeometry( item, rect ); } } @@ -445,7 +482,7 @@ void QskLinearLayoutEngine::setupChain( Qt::Orientation orientation, auto cell = element.cell( orientation, isLayoutOrientation ); if ( element.item() ) - cell.metrics = layoutMetrics( element.item(), orientation, constraint ); + cell.metrics = qskItemMetrics( element.item(), orientation, constraint ); chain.expandCell( index2, cell ); diff --git a/src/layouts/QskLinearLayoutEngine.h b/src/layouts/QskLinearLayoutEngine.h index ada6169c..61e72fa9 100644 --- a/src/layouts/QskLinearLayoutEngine.h +++ b/src/layouts/QskLinearLayoutEngine.h @@ -39,7 +39,9 @@ class QskLinearLayoutEngine : public QskLayoutEngine2D bool removeAt( int index ); bool clear(); - QQuickItem* itemAt( int index ) const override final; + int indexOf( const QQuickItem* ) const; + + QQuickItem* itemAt( int index ) const; qreal spacerAt( int index ) const; bool setStretchFactorAt( int index, int stretchFactor ); @@ -48,6 +50,7 @@ class QskLinearLayoutEngine : public QskLayoutEngine2D private: Q_DISABLE_COPY(QskLinearLayoutEngine) + QskSizePolicy sizePolicyAt( int index ) const override final; void layoutItems() override; int effectiveCount() const; diff --git a/src/src.pro b/src/src.pro index da3723ec..f2ea9187 100644 --- a/src/src.pro +++ b/src/src.pro @@ -317,6 +317,7 @@ HEADERS += \ layouts/QskIndexedLayoutBox.h \ layouts/QskLayoutChain.h \ layouts/QskLayoutEngine2D.h \ + layouts/QskLayoutElement.h \ layouts/QskLayoutMetrics.h \ layouts/QskLinearBox.h \ layouts/QskLinearLayoutEngine.h \ @@ -329,6 +330,7 @@ SOURCES += \ layouts/QskIndexedLayoutBox.cpp \ layouts/QskLayoutChain.cpp \ layouts/QskLayoutEngine2D.cpp \ + layouts/QskLayoutElement.cpp \ layouts/QskLayoutMetrics.cpp \ layouts/QskLinearBox.cpp \ layouts/QskLinearLayoutEngine.cpp \