2017-07-21 18:21:34 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskLayoutConstraint.h"
|
|
|
|
#include "QskControl.h"
|
|
|
|
#include "QskSizePolicy.h"
|
2019-05-17 22:33:38 +02:00
|
|
|
#include "QskFunctions.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2019-04-26 18:09:59 +02:00
|
|
|
#include <functional>
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
static inline qreal qskHintFor(
|
|
|
|
const QQuickItem* item, const char* method, qreal widthOrHeight )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
if ( item->metaObject()->indexOfMethod( method ) >= 0 )
|
|
|
|
{
|
|
|
|
qreal value;
|
|
|
|
|
|
|
|
( void ) QMetaObject::invokeMethod(
|
|
|
|
const_cast< QQuickItem* >( item ), method, Qt::DirectConnection,
|
|
|
|
Q_RETURN_ARG( qreal, value ), Q_ARG( qreal, widthOrHeight ) );
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2017-12-07 17:04:05 +01:00
|
|
|
static inline bool qskHasHintFor( const QQuickItem* item, const char* method )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
if ( item->metaObject()->indexOfMethod( method ) >= 0 )
|
|
|
|
{
|
|
|
|
bool enabled;
|
|
|
|
|
|
|
|
( void ) QMetaObject::invokeMethod( const_cast< QQuickItem* >( item ),
|
|
|
|
method, Qt::DirectConnection, Q_RETURN_ARG( bool, enabled ) );
|
|
|
|
|
|
|
|
return enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-12-11 15:00:53 +01:00
|
|
|
static inline qreal qskAdjustedValue(
|
|
|
|
QskSizePolicy::Policy policy, qreal value, qreal targetValue )
|
2018-11-17 09:22:11 +01:00
|
|
|
{
|
|
|
|
if ( targetValue > value )
|
|
|
|
{
|
|
|
|
if ( policy & QskSizePolicy::GrowFlag )
|
|
|
|
return targetValue;
|
|
|
|
}
|
|
|
|
else if ( targetValue < value )
|
|
|
|
{
|
|
|
|
if ( policy & QskSizePolicy::ShrinkFlag )
|
|
|
|
return targetValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2019-05-17 22:33:38 +02:00
|
|
|
static inline QSizeF qskExpandedSize( const QQuickItem* item, const QSizeF& constraint )
|
|
|
|
{
|
|
|
|
using namespace QskLayoutConstraint;
|
|
|
|
|
|
|
|
const auto policy = sizePolicy( item );
|
|
|
|
|
|
|
|
QSizeF size = constraint;
|
|
|
|
|
|
|
|
const bool vGrow = ( policy.policy( Qt::Vertical ) & QskSizePolicy::GrowFlag );
|
|
|
|
const bool hGrow = ( policy.policy( Qt::Horizontal ) & QskSizePolicy::GrowFlag );
|
|
|
|
|
|
|
|
if ( !vGrow || !hGrow )
|
|
|
|
{
|
|
|
|
const auto hint = sizeHint( item, Qt::PreferredSize, constraint );
|
|
|
|
|
|
|
|
if ( !vGrow )
|
|
|
|
size.setHeight( hint.height() );
|
|
|
|
|
|
|
|
if ( !hGrow )
|
|
|
|
size.setWidth( hint.width() );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !size.isValid() )
|
|
|
|
{
|
|
|
|
const auto hint = sizeHint( item, Qt::MaximumSize, size );
|
|
|
|
|
|
|
|
if ( size.width() == -1 )
|
|
|
|
size.setWidth( hint.width() );
|
|
|
|
|
|
|
|
if ( size.height() == -1 )
|
|
|
|
size.setHeight( hint.height() );
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2019-05-12 12:27:25 +02:00
|
|
|
QskLayoutConstraint::Type QskLayoutConstraint::constraintType( const QQuickItem* item )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2019-06-04 07:59:30 +02:00
|
|
|
if ( item == nullptr )
|
|
|
|
return Unconstrained;
|
|
|
|
|
2019-05-12 12:27:25 +02:00
|
|
|
Type constraintType = Unconstrained;
|
2019-06-04 07:59:30 +02:00
|
|
|
|
2019-04-26 11:56:09 +02:00
|
|
|
if ( auto control = qskControlCast( item ) )
|
2019-05-12 12:27:25 +02:00
|
|
|
{
|
|
|
|
const auto policy = control->sizePolicy();
|
|
|
|
if ( policy.horizontalPolicy() == QskSizePolicy::Constrained )
|
|
|
|
{
|
|
|
|
constraintType = WidthForHeight;
|
|
|
|
}
|
|
|
|
else if ( policy.verticalPolicy() == QskSizePolicy::Constrained )
|
|
|
|
{
|
|
|
|
constraintType = HeightForWidth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2019-05-12 12:27:25 +02:00
|
|
|
if ( qskHasHintFor( item, "hasWidthForHeight" ) )
|
|
|
|
{
|
|
|
|
constraintType = WidthForHeight;
|
|
|
|
}
|
|
|
|
else if ( qskHasHintFor( item, "hasHeightForWidth" ) )
|
|
|
|
{
|
|
|
|
constraintType = HeightForWidth;
|
|
|
|
}
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
2019-05-12 12:27:25 +02:00
|
|
|
|
|
|
|
return constraintType;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
qreal QskLayoutConstraint::heightForWidth( const QQuickItem* item, qreal width )
|
|
|
|
{
|
2019-04-26 11:56:09 +02:00
|
|
|
if ( auto control = qskControlCast( item ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
return control->heightForWidth( width );
|
|
|
|
|
|
|
|
return qskHintFor( item, "heightForWidth", width );
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal QskLayoutConstraint::widthForHeight( const QQuickItem* item, qreal height )
|
|
|
|
{
|
2019-04-26 11:56:09 +02:00
|
|
|
if ( auto control = qskControlCast( item ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
return control->widthForHeight( height );
|
|
|
|
|
|
|
|
return qskHintFor( item, "widthForHeight", height );
|
|
|
|
}
|
|
|
|
|
2019-04-26 18:09:59 +02:00
|
|
|
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
|
|
|
|
|
2019-04-30 09:04:46 +02:00
|
|
|
const qreal upperLimit = 1e6;
|
2019-04-26 18:09:59 +02:00
|
|
|
|
|
|
|
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();
|
2019-05-06 16:36:52 +02:00
|
|
|
|
2019-04-26 18:09:59 +02:00
|
|
|
qreal height = constrainFunction( type, control, inner.width() );
|
2019-05-06 16:36:52 +02:00
|
|
|
|
2019-04-26 18:09:59 +02:00
|
|
|
if ( height >= 0.0 )
|
|
|
|
height += outer.height() - inner.height();
|
2019-05-06 16:36:52 +02:00
|
|
|
|
2019-04-26 18:09:59 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-07-21 18:21:34 +02:00
|
|
|
QSizeF QskLayoutConstraint::effectiveConstraint(
|
|
|
|
const QQuickItem* item, Qt::SizeHint whichHint )
|
|
|
|
{
|
2019-04-26 11:56:09 +02:00
|
|
|
if ( auto control = qskControlCast( item ) )
|
2018-05-01 13:55:26 +02:00
|
|
|
return control->effectiveSizeHint( whichHint );
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
QSizeF constraint( -1.0, -1.0 ); // no hint
|
|
|
|
|
|
|
|
const char* properties[] =
|
|
|
|
{
|
|
|
|
"minimumSize",
|
|
|
|
"preferredSize",
|
|
|
|
"maximumSize"
|
|
|
|
};
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
const QVariant v = item->property( properties[ whichHint ] );
|
2017-07-21 18:21:34 +02:00
|
|
|
if ( v.canConvert( QMetaType::QSizeF ) )
|
|
|
|
constraint = v.toSizeF();
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
switch ( whichHint )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
case Qt::MinimumSize:
|
|
|
|
{
|
|
|
|
if ( constraint.width() < 0 )
|
|
|
|
constraint.setWidth( 0.0 );
|
|
|
|
|
|
|
|
if ( constraint.height() < 0 )
|
|
|
|
constraint.setHeight( 0.0 );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Qt::PreferredSize:
|
|
|
|
{
|
|
|
|
if ( constraint.width() < 0 )
|
|
|
|
constraint.setWidth( item->implicitWidth() );
|
|
|
|
|
|
|
|
if ( constraint.height() < 0 )
|
|
|
|
constraint.setHeight( item->implicitHeight() );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Qt::MaximumSize:
|
|
|
|
{
|
|
|
|
if ( constraint.width() < 0 )
|
|
|
|
constraint.setWidth( unlimited );
|
|
|
|
|
|
|
|
if ( constraint.height() < 0 )
|
|
|
|
constraint.setHeight( unlimited );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return constraint;
|
|
|
|
}
|
|
|
|
|
|
|
|
QskSizePolicy QskLayoutConstraint::sizePolicy( const QQuickItem* item )
|
|
|
|
{
|
|
|
|
if ( item )
|
|
|
|
{
|
2019-04-26 11:56:09 +02:00
|
|
|
if ( auto control = qskControlCast( item ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
return control->sizePolicy();
|
|
|
|
|
|
|
|
const QVariant v = item->property( "sizePolicy" );
|
|
|
|
if ( v.canConvert< QskSizePolicy >() )
|
|
|
|
return qvariant_cast< QskSizePolicy >( v );
|
|
|
|
}
|
|
|
|
|
|
|
|
return QskSizePolicy( QskSizePolicy::Preferred, QskSizePolicy::Preferred );
|
|
|
|
}
|
2018-11-17 09:22:11 +01:00
|
|
|
|
|
|
|
QSizeF QskLayoutConstraint::boundedSize( const QQuickItem* item, const QSizeF& size )
|
|
|
|
{
|
|
|
|
const auto minSize = effectiveConstraint( item, Qt::MinimumSize );
|
|
|
|
const auto maxSize = effectiveConstraint( item, Qt::MaximumSize );
|
|
|
|
|
|
|
|
qreal width = size.width();
|
|
|
|
qreal height = size.height();
|
|
|
|
|
|
|
|
if ( ( minSize.width() >= 0 ) && ( minSize.width() > width ) )
|
|
|
|
width = minSize.width();
|
|
|
|
|
|
|
|
if ( ( minSize.height() >= 0 ) && ( minSize.height() > height ) )
|
|
|
|
height = minSize.height();
|
|
|
|
|
|
|
|
if ( ( maxSize.width() >= 0 ) && ( maxSize.width() < width ) )
|
|
|
|
width = maxSize.width();
|
|
|
|
|
|
|
|
if ( ( maxSize.height() >= 0 ) && ( maxSize.height() < height ) )
|
|
|
|
height = maxSize.height();
|
|
|
|
|
|
|
|
return QSizeF( width, height );
|
|
|
|
}
|
|
|
|
|
2019-05-17 22:33:38 +02:00
|
|
|
QSizeF QskLayoutConstraint::adjustedSize(
|
|
|
|
const QQuickItem* item, const QSizeF& targetSize )
|
2018-11-17 09:22:11 +01:00
|
|
|
{
|
|
|
|
const auto policy = sizePolicy( item );
|
|
|
|
|
|
|
|
const auto boundedSize = QskLayoutConstraint::boundedSize( item, targetSize );
|
|
|
|
const auto preferredSize = effectiveConstraint( item, Qt::PreferredSize );
|
|
|
|
|
|
|
|
qreal w;
|
|
|
|
qreal h;
|
|
|
|
|
|
|
|
if ( policy.horizontalPolicy() == QskSizePolicy::Constrained )
|
|
|
|
{
|
|
|
|
h = qskAdjustedValue( policy.verticalPolicy(),
|
|
|
|
preferredSize.height(), boundedSize.height() );
|
|
|
|
|
|
|
|
w = widthForHeight( item, h );
|
|
|
|
|
|
|
|
if ( w < boundedSize.height() )
|
|
|
|
w = boundedSize.height();
|
|
|
|
}
|
|
|
|
else if ( policy.verticalPolicy() == QskSizePolicy::Constrained )
|
|
|
|
{
|
|
|
|
w = qskAdjustedValue( policy.horizontalPolicy(),
|
|
|
|
preferredSize.width(), boundedSize.width() );
|
|
|
|
|
|
|
|
h = heightForWidth( item, w );
|
|
|
|
|
|
|
|
if ( h < boundedSize.height() )
|
|
|
|
h = boundedSize.height();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
w = qskAdjustedValue( policy.horizontalPolicy(),
|
|
|
|
preferredSize.width(), boundedSize.width() );
|
|
|
|
|
|
|
|
h = qskAdjustedValue( policy.verticalPolicy(),
|
|
|
|
preferredSize.height(), boundedSize.height() );
|
|
|
|
}
|
|
|
|
|
|
|
|
return QSizeF( w, h );
|
|
|
|
}
|
2019-05-12 12:27:25 +02:00
|
|
|
|
|
|
|
QSizeF QskLayoutConstraint::sizeHint( const QQuickItem* item,
|
|
|
|
Qt::SizeHint whichHint, const QSizeF& constraint )
|
|
|
|
{
|
|
|
|
if ( item == nullptr || whichHint < Qt::MinimumSize || whichHint > Qt::MaximumSize )
|
|
|
|
return QSizeF( 0, 0 );
|
|
|
|
|
|
|
|
QSizeF hint( 0, 0 );
|
|
|
|
|
|
|
|
Type constraintType = Unconstrained;
|
|
|
|
|
|
|
|
if ( whichHint == Qt::PreferredSize )
|
|
|
|
constraintType = QskLayoutConstraint::constraintType( item );
|
|
|
|
|
|
|
|
if ( constraintType != Unconstrained )
|
|
|
|
{
|
|
|
|
const quint32 growFlags = QskSizePolicy::GrowFlag | QskSizePolicy::ExpandFlag;
|
|
|
|
|
|
|
|
if ( constraint.width() > 0 ) // && constrainedType == HeightForWidth ??
|
|
|
|
{
|
|
|
|
qreal w = constraint.width();
|
|
|
|
|
|
|
|
if ( !( sizePolicy( item ).policy( Qt::Horizontal ) & growFlags ) )
|
|
|
|
{
|
|
|
|
const auto maxW = effectiveConstraint( item, Qt::PreferredSize ).width();
|
|
|
|
|
|
|
|
if ( maxW >= 0.0 )
|
|
|
|
w = qMin( w, maxW );
|
|
|
|
}
|
|
|
|
|
|
|
|
hint.setWidth( w );
|
|
|
|
hint.setHeight( heightForWidth( item, w ) );
|
|
|
|
}
|
|
|
|
else if ( constraint.height() > 0 ) // && constrainedType == WidthForHeight ??
|
|
|
|
{
|
|
|
|
qreal h = constraint.height();
|
|
|
|
|
|
|
|
if ( !( sizePolicy( item ).policy( Qt::Vertical ) & growFlags ) )
|
|
|
|
{
|
|
|
|
const auto maxH = effectiveConstraint( item, Qt::PreferredSize ).height();
|
|
|
|
|
|
|
|
if ( maxH >= 0.0 )
|
|
|
|
h = qMin( h, maxH );
|
|
|
|
}
|
|
|
|
|
|
|
|
hint.setWidth( widthForHeight( item, h ) );
|
|
|
|
hint.setHeight( h );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hint = effectiveConstraint( item, Qt::PreferredSize );
|
|
|
|
|
|
|
|
if ( constraintType == WidthForHeight )
|
|
|
|
hint.setWidth( widthForHeight( item, hint.height() ) );
|
|
|
|
else
|
|
|
|
hint.setHeight( heightForWidth( item, hint.width() ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hint = effectiveConstraint( item, whichHint );
|
|
|
|
}
|
|
|
|
|
|
|
|
hint = hint.expandedTo( QSizeF( 0.0, 0.0 ) );
|
|
|
|
|
|
|
|
return hint;
|
|
|
|
}
|
2019-05-17 22:33:38 +02:00
|
|
|
|
|
|
|
QRectF QskLayoutConstraint::itemRect( const QQuickItem* item,
|
|
|
|
const QRectF& rect, Qt::Alignment alignment )
|
|
|
|
{
|
|
|
|
QSizeF size = qskExpandedSize( item, QSizeF( -1, -1 ) );
|
|
|
|
|
|
|
|
switch( constraintType( item ) )
|
|
|
|
{
|
|
|
|
case HeightForWidth:
|
|
|
|
{
|
|
|
|
if ( size.width() > rect.width() )
|
|
|
|
size = qskExpandedSize( item, QSizeF( rect.width(), -1 ) );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case WidthForHeight:
|
|
|
|
{
|
|
|
|
if ( size.height() > rect.height() )
|
|
|
|
size = qskExpandedSize( item, QSizeF( -1, rect.height() ) );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
size = size.boundedTo( rect.size() );
|
|
|
|
|
|
|
|
return qskAlignedRectF( rect, size.width(), size.height(), alignment );
|
|
|
|
}
|
|
|
|
|