From dd39017dbe21c523e78e37568e0daf9f26199811 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 26 Apr 2019 18:09:59 +0200 Subject: [PATCH] implementation of dynamic constraints reorganized --- src/controls/QskBox.cpp | 4 +- src/controls/QskBox.h | 2 +- src/controls/QskControl.cpp | 69 ++++++++++------------------- src/controls/QskControl.h | 9 +++- src/controls/QskPushButton.cpp | 4 +- src/controls/QskPushButton.h | 2 +- src/controls/QskSubWindow.cpp | 4 +- src/controls/QskSubWindow.h | 2 +- src/controls/QskTabButton.cpp | 4 +- src/controls/QskTabButton.h | 2 +- src/layouts/QskLayoutBox.cpp | 31 +++++++------ src/layouts/QskLayoutConstraint.cpp | 67 ++++++++++++++++++++++++++++ src/layouts/QskLayoutConstraint.h | 15 +++++++ src/layouts/QskStackBox.cpp | 58 ++++++++++++------------ 14 files changed, 170 insertions(+), 103 deletions(-) diff --git a/src/controls/QskBox.cpp b/src/controls/QskBox.cpp index b4f2bcad..ba2a64ac 100644 --- a/src/controls/QskBox.cpp +++ b/src/controls/QskBox.cpp @@ -16,9 +16,9 @@ QskBox::~QskBox() { } -QRectF QskBox::layoutRect() const +QRectF QskBox::layoutRectForSize( const QSizeF& size ) const { - return innerBox( Panel, subControlRect( Panel ) ); + return innerBox( Panel, subControlRect( size, Panel ) ); } QSizeF QskBox::contentsSizeHint() const diff --git a/src/controls/QskBox.h b/src/controls/QskBox.h index a591943b..73e638e0 100644 --- a/src/controls/QskBox.h +++ b/src/controls/QskBox.h @@ -20,7 +20,7 @@ class QSK_EXPORT QskBox : public QskControl QskBox( QQuickItem* parent = nullptr ); ~QskBox() override; - QRectF layoutRect() const override; + QRectF layoutRectForSize( const QSizeF& ) const override; QSizeF contentsSizeHint() const override; }; diff --git a/src/controls/QskControl.cpp b/src/controls/QskControl.cpp index d3b2c461..7b002389 100644 --- a/src/controls/QskControl.cpp +++ b/src/controls/QskControl.cpp @@ -13,6 +13,7 @@ #include "QskSkin.h" #include "QskSkinlet.h" #include "QskSkinHintTable.h" +#include "QskLayoutConstraint.h" #include #include @@ -1013,6 +1014,15 @@ QRectF QskControl::subControlRect( QskAspect::Subcontrol subControl ) const return effectiveSkinlet()->subControlRect( this, contentsRect(), subControl ); } +QRectF QskControl::subControlRect( + const QSizeF& size, QskAspect::Subcontrol subControl ) const +{ + QRectF rect( 0.0, 0.0, size.width(), size.height() ); + rect = qskValidOrEmptyInnerRect( rect, margins() ); + + return effectiveSkinlet()->subControlRect( this, rect, subControl ); +} + bool QskControl::layoutMirroring() const { return d_func()->effectiveLayoutMirror; @@ -1379,58 +1389,24 @@ void QskControl::resetImplicitSize() qreal QskControl::heightForWidth( qreal width ) const { - qreal h = -1; + if ( !d_func()->autoLayoutChildren ) + return -1.0; - if ( d_func()->autoLayoutChildren ) - { - const auto innerSize = layoutRect().size(); - const auto outerSize = size(); + using namespace QskLayoutConstraint; - width -= outerSize.width() - innerSize.width(); - - const auto children = childItems(); - for ( auto child : children ) - { - if ( auto control = qobject_cast< const QskControl* >( child ) ) - { - if ( !control->isTransparentForPositioner() ) - h = qMax( h, control->heightForWidth( width ) ); - } - } - - if ( h >= 0 ) - h += outerSize.height() - innerSize.height(); - } - - return h; + return constrainedMetric( + HeightForWidth, this, width, constrainedChildrenMetric ); } qreal QskControl::widthForHeight( qreal height ) const { - qreal w = -1; + if ( !d_func()->autoLayoutChildren ) + return -1.0; - if ( d_func()->autoLayoutChildren ) - { - const auto innerSize = layoutRect().size(); - const auto outerSize = size(); + using namespace QskLayoutConstraint; - height -= outerSize.height() - innerSize.height(); - - const auto children = childItems(); - for ( auto child : children ) - { - if ( auto control = qobject_cast< const QskControl* >( child ) ) - { - if ( !control->isTransparentForPositioner() ) - w = qMax( w, control->widthForHeight( height ) ); - } - } - - if ( w >= 0 ) - w += outerSize.width() - innerSize.width(); - } - - return w; + return constrainedMetric( + WidthForHeight, this, height, constrainedChildrenMetric ); } bool QskControl::event( QEvent* event ) @@ -1932,9 +1908,10 @@ QskControl* QskControl::owningControl() const return const_cast< QskControl* >( this ); } -QRectF QskControl::layoutRect() const +QRectF QskControl::layoutRectForSize( const QSizeF& size ) const { - return contentsRect(); + const QRectF r( 0.0, 0.0, size.width(), size.height() ); + return qskValidOrEmptyInnerRect( r, margins() ); } QRectF QskControl::gestureRect() const diff --git a/src/controls/QskControl.h b/src/controls/QskControl.h index 8f58b835..8256ccaf 100644 --- a/src/controls/QskControl.h +++ b/src/controls/QskControl.h @@ -110,12 +110,14 @@ class QSK_EXPORT QskControl : public QQuickItem, public QskSkinnable QRectF rect() const; QRectF contentsRect() const; + QRectF layoutRect() const; - virtual QRectF layoutRect() const; + virtual QRectF layoutRectForSize( const QSizeF& ) const; virtual QRectF gestureRect() const; virtual QRectF focusIndicatorRect() const; QRectF subControlRect( QskAspect::Subcontrol ) const; + QRectF subControlRect( const QSizeF&, QskAspect::Subcontrol ) const; void setAutoFillBackground( bool ); bool autoFillBackground() const; @@ -313,6 +315,11 @@ inline QSizeF QskControl::preferredSize() const return explicitSizeHint( Qt::PreferredSize ); } +inline QRectF QskControl::layoutRect() const +{ + return layoutRectForSize( size() ); +} + inline QskControl* qskControlCast( QObject* object ) { return qobject_cast< QskControl* >( object ); diff --git a/src/controls/QskPushButton.cpp b/src/controls/QskPushButton.cpp index 2d2bbc31..ae495655 100644 --- a/src/controls/QskPushButton.cpp +++ b/src/controls/QskPushButton.cpp @@ -245,9 +245,9 @@ void QskPushButton::updateLayout() } } -QRectF QskPushButton::layoutRect() const +QRectF QskPushButton::layoutRectForSize( const QSizeF& size ) const { - return innerBox( Panel, subControlRect( Panel ) ); + return innerBox( Panel, subControlRect( size, Panel ) ); } QSizeF QskPushButton::contentsSizeHint() const diff --git a/src/controls/QskPushButton.h b/src/controls/QskPushButton.h index 9bd5b5fe..999f0362 100644 --- a/src/controls/QskPushButton.h +++ b/src/controls/QskPushButton.h @@ -65,7 +65,7 @@ class QSK_EXPORT QskPushButton : public QskAbstractButton QFont font() const; QSizeF contentsSizeHint() const override; - QRectF layoutRect() const override; + QRectF layoutRectForSize( const QSizeF& ) const override; public Q_SLOTS: void setText( const QString& ); diff --git a/src/controls/QskSubWindow.cpp b/src/controls/QskSubWindow.cpp index 07d43617..21e48970 100644 --- a/src/controls/QskSubWindow.cpp +++ b/src/controls/QskSubWindow.cpp @@ -231,11 +231,11 @@ void QskSubWindow::updateLayout() Inherited::updateLayout(); } -QRectF QskSubWindow::layoutRect() const +QRectF QskSubWindow::layoutRectForSize( const QSizeF& size ) const { QRectF rect = contentsRect(); - const qreal top = rect.top() + subControlRect( TitleBar ).height(); + const qreal top = rect.top() + subControlRect( size, TitleBar ).height(); rect.setTop( top ); return innerBox( Panel, rect ); diff --git a/src/controls/QskSubWindow.h b/src/controls/QskSubWindow.h index ad1913d1..39aab9c1 100644 --- a/src/controls/QskSubWindow.h +++ b/src/controls/QskSubWindow.h @@ -79,7 +79,7 @@ class QSK_EXPORT QskSubWindow : public QskPopup QRectF titleBarRect() const; QSizeF contentsSizeHint() const override; - QRectF layoutRect() const override; + QRectF layoutRectForSize( const QSizeF& ) const override; Q_SIGNALS: void decoratedChanged(); diff --git a/src/controls/QskTabButton.cpp b/src/controls/QskTabButton.cpp index d4d686e6..3c5c91fa 100644 --- a/src/controls/QskTabButton.cpp +++ b/src/controls/QskTabButton.cpp @@ -101,9 +101,9 @@ QSizeF QskTabButton::contentsSizeHint() const return size; } -QRectF QskTabButton::layoutRect() const +QRectF QskTabButton::layoutRectForSize( const QSizeF& size ) const { - return innerBox( Panel, subControlRect( Panel ) ); + return innerBox( Panel, subControlRect( size, Panel ) ); } QskAspect::Placement QskTabButton::effectivePlacement() const diff --git a/src/controls/QskTabButton.h b/src/controls/QskTabButton.h index 1f3b1691..10f99078 100644 --- a/src/controls/QskTabButton.h +++ b/src/controls/QskTabButton.h @@ -37,7 +37,7 @@ class QSK_EXPORT QskTabButton : public QskAbstractButton QskTextOptions textOptions() const; QSizeF contentsSizeHint() const override; - QRectF layoutRect() const override; + QRectF layoutRectForSize( const QSizeF& ) const override; QskAspect::Placement effectivePlacement() const override; diff --git a/src/layouts/QskLayoutBox.cpp b/src/layouts/QskLayoutBox.cpp index 48cbcde2..5dd0eb8f 100644 --- a/src/layouts/QskLayoutBox.cpp +++ b/src/layouts/QskLayoutBox.cpp @@ -7,6 +7,7 @@ #include "QskEvent.h" #include "QskLayoutEngine.h" #include "QskLayoutItem.h" +#include "QskLayoutConstraint.h" class QskLayoutBox::PrivateData { @@ -266,24 +267,26 @@ QSizeF QskLayoutBox::layoutItemsSizeHint() const qreal QskLayoutBox::heightForWidth( qreal width ) const { - const auto m = margins(); - width -= m.left() + m.right(); - - qreal height = engine().heightForWidth( width ); - - height += m.top() + m.bottom(); - return height; + auto constrainedHeight = + [this]( QskLayoutConstraint::Type, const QskControl*, qreal width ) + { + return engine().heightForWidth( width ); + }; + + return QskLayoutConstraint::constrainedMetric( + QskLayoutConstraint::HeightForWidth, this, width, constrainedHeight ); } qreal QskLayoutBox::widthForHeight( qreal height ) const { - const auto m = margins(); - height -= m.top() + m.bottom(); - - qreal width = engine().widthForHeight( height ); - - width += m.left() + m.right(); - return width; + auto constrainedWidth = + [this]( QskLayoutConstraint::Type, const QskControl*, qreal height ) + { + return engine().widthForHeight( height ); + }; + + return QskLayoutConstraint::constrainedMetric( + QskLayoutConstraint::WidthForHeight, this, height, constrainedWidth ); } void QskLayoutBox::geometryChangeEvent( QskGeometryChangeEvent* event ) diff --git a/src/layouts/QskLayoutConstraint.cpp b/src/layouts/QskLayoutConstraint.cpp index 58ad250f..c9d98e6a 100644 --- a/src/layouts/QskLayoutConstraint.cpp +++ b/src/layouts/QskLayoutConstraint.cpp @@ -7,6 +7,8 @@ #include "QskControl.h" #include "QskSizePolicy.h" +#include + static inline qreal qskHintFor( const QQuickItem* item, const char* method, qreal widthOrHeight ) { @@ -85,6 +87,71 @@ qreal QskLayoutConstraint::widthForHeight( const QQuickItem* item, qreal height return qskHintFor( item, "widthForHeight", height ); } +qreal QskLayoutConstraint::constrainedMetric( + Type type, const QskControl* control, qreal widthOrHeight, + std::function< qreal( Type, const QskControl*, qreal ) > constrainFunction ) +{ +#if 1 + /* + In case of having a corner radius of Qt::RelativeSize the margins + we might have a wrong result when using QskLayoutConstraint::unlimited. + No idea how to solve this in a generic way: TODO ... + */ +#endif + + const qreal upperLimit = 10e6; + + if ( type == WidthForHeight ) + { + const QSizeF outer( upperLimit, widthOrHeight ); + const QSizeF inner = control->layoutRectForSize( outer ).size(); + + qreal width = constrainFunction( type, control, inner.height() ); + + if ( width >= 0.0 ) + width += outer.width() - inner.width(); + + return width; + } + else + { + const QSizeF outer( widthOrHeight, upperLimit ); + const QSizeF inner = control->layoutRectForSize( outer ).size(); + + qreal height = constrainFunction( type, control, inner.width() ); + + if ( height >= 0.0 ) + height += outer.height() - inner.height(); + + return height; + } +} + +qreal QskLayoutConstraint::constrainedChildrenMetric( + Type type, const QskControl* control, qreal widthOrHeight ) +{ + auto constrainFunction = + ( type == WidthForHeight ) ? widthForHeight : heightForWidth; + + qreal constrainedValue = -1.0; + + const auto children = control->childItems(); + for ( auto child : children ) + { + if ( auto control = qskControlCast( child ) ) + { + if ( !control->isTransparentForPositioner() ) + { + const auto v = constrainFunction( control, widthOrHeight ); + if ( v > constrainedValue ) + constrainedValue = v; + } + } + } + + return constrainedValue; +} + QSizeF QskLayoutConstraint::effectiveConstraint( const QQuickItem* item, Qt::SizeHint whichHint ) { diff --git a/src/layouts/QskLayoutConstraint.h b/src/layouts/QskLayoutConstraint.h index 244666fa..bb2fe0a0 100644 --- a/src/layouts/QskLayoutConstraint.h +++ b/src/layouts/QskLayoutConstraint.h @@ -12,15 +12,30 @@ #include class QskSizePolicy; +class QskControl; class QQuickItem; class QSizeF; +namespace std { template< typename T > class function; } + namespace QskLayoutConstraint { + enum Type + { + WidthForHeight, + HeightForWidth + }; + QSK_EXPORT bool hasDynamicConstraint( const QQuickItem* ); QSK_EXPORT qreal heightForWidth( const QQuickItem*, qreal width ); QSK_EXPORT qreal widthForHeight( const QQuickItem*, qreal height ); + QSK_EXPORT qreal constrainedMetric( + Type, const QskControl*, qreal value, + std::function< qreal( Type, const QskControl*, qreal ) > ); + + QSK_EXPORT qreal constrainedChildrenMetric( Type, const QskControl*, qreal width ); + QSK_EXPORT QSizeF effectiveConstraint( const QQuickItem*, Qt::SizeHint ); QSK_EXPORT QskSizePolicy sizePolicy( const QQuickItem* ); diff --git a/src/layouts/QskStackBox.cpp b/src/layouts/QskStackBox.cpp index 10afc3d9..c7ffd46d 100644 --- a/src/layouts/QskStackBox.cpp +++ b/src/layouts/QskStackBox.cpp @@ -11,6 +11,30 @@ #include +static qreal qskConstrainedValue( QskLayoutConstraint::Type type, + const QskControl* control, qreal widthOrHeight ) +{ + using namespace QskLayoutConstraint; + + auto constrainFunction = + ( type == WidthForHeight ) ? widthForHeight : heightForWidth; + + qreal constrainedValue = -1; + auto stackBox = static_cast< const QskStackBox* >( control ); + + for ( int i = 0; i < stackBox->itemCount(); i++ ) + { + if ( const auto item = stackBox->itemAtIndex( i ) ) + { + const qreal v = constrainFunction( item, widthOrHeight ); + if ( v > constrainedValue ) + constrainedValue = v; + } + } + + return constrainedValue; +} + class QskStackBox::PrivateData { public: @@ -252,40 +276,14 @@ QSizeF QskStackBox::layoutItemsSizeHint() const qreal QskStackBox::heightForWidth( qreal width ) const { - const auto m = margins(); - width -= m.left() + m.right(); - - qreal height = -1; - - const QskLayoutEngine& engine = this->engine(); - for ( int i = 0; i < engine.itemCount(); i++ ) - { - const QskLayoutItem* layoutItem = engine.layoutItemAt( i ); - if ( const auto item = layoutItem->item() ) - height = qMax( height, QskLayoutConstraint::heightForWidth( item, width ) ); - } - - height += m.top() + m.bottom(); - return height; + return QskLayoutConstraint::constrainedMetric( + QskLayoutConstraint::HeightForWidth, this, width, qskConstrainedValue ); } qreal QskStackBox::widthForHeight( qreal height ) const { - const auto m = margins(); - height -= m.top() + m.bottom(); - - qreal width = -1; - - const QskLayoutEngine& engine = this->engine(); - for ( int i = 0; i < engine.itemCount(); i++ ) - { - const QskLayoutItem* layoutItem = engine.layoutItemAt( i ); - if ( const auto item = layoutItem->item() ) - width = qMax( width, QskLayoutConstraint::widthForHeight( item, height ) ); - } - - width += m.left() + m.right(); - return width; + return QskLayoutConstraint::constrainedMetric( + QskLayoutConstraint::WidthForHeight, this, height, qskConstrainedValue ); } void QskStackBox::layoutItemInserted( QskLayoutItem* layoutItem, int index )