qskinny/src/layouts/QskLayoutConstraint.cpp

293 lines
8.1 KiB
C++
Raw Normal View History

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"
#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 )
{
if ( targetValue > value )
{
if ( policy & QskSizePolicy::GrowFlag )
return targetValue;
}
else if ( targetValue < value )
{
if ( policy & QskSizePolicy::ShrinkFlag )
return targetValue;
}
return value;
}
2017-07-21 18:21:34 +02:00
bool QskLayoutConstraint::hasDynamicConstraint( const QQuickItem* item )
{
2019-04-26 11:56:09 +02:00
if ( auto control = qskControlCast( item ) )
2017-07-21 18:21:34 +02:00
{
2018-12-11 15:00:53 +01:00
const auto& policy = control->sizePolicy();
2017-07-21 18:21:34 +02:00
return ( policy.horizontalPolicy() == QskSizePolicy::Constrained ) ||
( policy.verticalPolicy() == QskSizePolicy::Constrained );
}
return qskHasHintFor( item, "hasHeightForWidth" ) ||
qskHasHintFor( item, "hasWidthForHeight" );
}
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 );
}
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;
}
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 ) )
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 );
}
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 );
}
QSizeF QskLayoutConstraint::adjustedSize( const QQuickItem* item, const QSizeF& targetSize )
{
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 );
}