2019-07-27 12:36:52 +02:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
#include "QskLayoutEngine2D.h"
|
|
|
|
#include "QskLayoutChain.h"
|
2019-09-10 17:01:47 +02:00
|
|
|
#include "QskLayoutHint.h"
|
2019-07-27 12:36:52 +02:00
|
|
|
#include "QskQuick.h"
|
2019-09-10 17:01:47 +02:00
|
|
|
|
2019-07-27 12:36:52 +02:00
|
|
|
#include <qguiapplication.h>
|
|
|
|
|
2019-09-10 17:01:47 +02:00
|
|
|
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 ) ? QskLayoutHint::unlimited : 0.0;
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2019-07-27 12:36:52 +02:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class LayoutData
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
QRectF geometryAt( const QRect& grid ) const
|
|
|
|
{
|
|
|
|
const auto x1 = columns[ grid.left() ].start;
|
|
|
|
const auto x2 = columns[ grid.right() ].end();
|
|
|
|
const auto y1 = rows[ grid.top() ].start;
|
|
|
|
const auto y2 = rows[ grid.bottom() ].end();
|
|
|
|
|
|
|
|
return QRectF( rect.x() + x1, rect.y() + y1, x2 - x1, y2 - y1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt::LayoutDirection direction;
|
|
|
|
|
|
|
|
QRectF rect;
|
|
|
|
QskLayoutChain::Segments rows;
|
|
|
|
QskLayoutChain::Segments columns;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
class QskLayoutEngine2D::PrivateData
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PrivateData()
|
|
|
|
: defaultAlignment( Qt::AlignLeft | Qt::AlignVCenter )
|
|
|
|
, extraSpacingAt( 0 )
|
|
|
|
, visualDirection( Qt::LeftToRight )
|
|
|
|
, constraintType( -1 )
|
|
|
|
, blockInvalidate( false )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
inline QskLayoutChain& layoutChain( Qt::Orientation orientation )
|
|
|
|
{
|
|
|
|
return ( orientation == Qt::Horizontal ) ? columnChain : rowChain;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline Qt::Alignment effectiveAlignment( Qt::Alignment alignment ) const
|
|
|
|
{
|
|
|
|
const auto align = static_cast< Qt::Alignment >( defaultAlignment );
|
|
|
|
|
|
|
|
if ( !( alignment & Qt::AlignVertical_Mask ) )
|
|
|
|
alignment |= ( align & Qt::AlignVertical_Mask );
|
|
|
|
|
|
|
|
if ( !( alignment & Qt::AlignHorizontal_Mask ) )
|
|
|
|
alignment |= ( align & Qt::AlignHorizontal_Mask );
|
|
|
|
|
|
|
|
return alignment;
|
|
|
|
}
|
|
|
|
|
|
|
|
QskLayoutChain columnChain;
|
|
|
|
QskLayoutChain rowChain;
|
|
|
|
|
|
|
|
QSizeF layoutSize;
|
|
|
|
|
|
|
|
QskLayoutChain::Segments rows;
|
|
|
|
QskLayoutChain::Segments columns;
|
|
|
|
|
|
|
|
const LayoutData* layoutData = nullptr;
|
|
|
|
|
|
|
|
unsigned int defaultAlignment : 8;
|
|
|
|
unsigned int extraSpacingAt : 4;
|
|
|
|
unsigned int visualDirection : 4;
|
|
|
|
|
|
|
|
int constraintType : 3;
|
|
|
|
|
|
|
|
/*
|
|
|
|
Some weired controls do lazy updates inside of their sizeHint calculation
|
|
|
|
that lead to LayoutRequest events. While being in the process of
|
|
|
|
updating the tables we can't - and don't need to - handle invalidations
|
|
|
|
because of them.
|
|
|
|
*/
|
|
|
|
bool blockInvalidate : 1;
|
|
|
|
};
|
|
|
|
|
|
|
|
QskLayoutEngine2D::QskLayoutEngine2D()
|
|
|
|
: m_data( new PrivateData )
|
|
|
|
{
|
|
|
|
m_data->columnChain.setSpacing( defaultSpacing( Qt::Horizontal ) );
|
|
|
|
m_data->rowChain.setSpacing( defaultSpacing( Qt::Vertical ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
QskLayoutEngine2D::~QskLayoutEngine2D()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskLayoutEngine2D::setVisualDirection( Qt::LayoutDirection direction )
|
|
|
|
{
|
|
|
|
if ( m_data->visualDirection != direction )
|
|
|
|
{
|
|
|
|
m_data->visualDirection = direction;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt::LayoutDirection QskLayoutEngine2D::visualDirection() const
|
|
|
|
{
|
|
|
|
return static_cast< Qt::LayoutDirection >( m_data->visualDirection );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskLayoutEngine2D::setDefaultAlignment( Qt::Alignment alignment )
|
|
|
|
{
|
|
|
|
if ( defaultAlignment() != alignment )
|
|
|
|
{
|
|
|
|
m_data->defaultAlignment = alignment;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt::Alignment QskLayoutEngine2D::defaultAlignment() const
|
|
|
|
{
|
|
|
|
return static_cast< Qt::Alignment >( m_data->defaultAlignment );
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal QskLayoutEngine2D::defaultSpacing( Qt::Orientation ) const
|
|
|
|
{
|
|
|
|
return 5.0; // should be from the skin
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskLayoutEngine2D::setSpacing(
|
|
|
|
qreal spacing, Qt::Orientations orientations )
|
|
|
|
{
|
|
|
|
if ( spacing < 0.0 )
|
|
|
|
spacing = 0.0;
|
|
|
|
|
|
|
|
bool isModified = false;
|
|
|
|
|
|
|
|
for ( auto o : { Qt::Horizontal, Qt::Vertical } )
|
|
|
|
{
|
|
|
|
if ( orientations & o )
|
|
|
|
isModified |= m_data->layoutChain( o ).setSpacing( spacing );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( isModified )
|
|
|
|
invalidate( LayoutCache );
|
|
|
|
|
|
|
|
return isModified;
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal QskLayoutEngine2D::spacing( Qt::Orientation orientation ) const
|
|
|
|
{
|
|
|
|
return m_data->layoutChain( orientation ).spacing();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QskLayoutEngine2D::setExtraSpacingAt( Qt::Edges edges )
|
|
|
|
{
|
|
|
|
if ( edges == extraSpacingAt() )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
m_data->extraSpacingAt = edges;
|
|
|
|
|
2019-07-28 17:06:50 +02:00
|
|
|
{
|
|
|
|
int fillMode = 0;
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-07-28 17:06:50 +02:00
|
|
|
if ( edges & Qt::LeftEdge )
|
|
|
|
fillMode |= QskLayoutChain::Leading;
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-07-28 17:06:50 +02:00
|
|
|
if ( edges & Qt::RightEdge )
|
|
|
|
fillMode |= QskLayoutChain::Trailing;
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-07-28 17:06:50 +02:00
|
|
|
m_data->columnChain.setFillMode( fillMode );
|
|
|
|
}
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-07-28 17:06:50 +02:00
|
|
|
{
|
|
|
|
int fillMode = 0;
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-07-28 17:06:50 +02:00
|
|
|
if ( edges & Qt::TopEdge )
|
|
|
|
fillMode |= QskLayoutChain::Leading;
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-07-28 17:06:50 +02:00
|
|
|
if ( edges & Qt::BottomEdge )
|
|
|
|
fillMode |= QskLayoutChain::Trailing;
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-07-28 17:06:50 +02:00
|
|
|
m_data->rowChain.setFillMode( fillMode );
|
|
|
|
}
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-07-28 17:06:50 +02:00
|
|
|
m_data->layoutSize = QSize();
|
|
|
|
m_data->rows.clear();
|
|
|
|
m_data->columns.clear();
|
2019-07-27 12:36:52 +02:00
|
|
|
|
|
|
|
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 );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskLayoutEngine2D::setGeometries( const QRectF& rect )
|
|
|
|
{
|
|
|
|
if ( rowCount() < 1 || columnCount() < 1 )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( m_data->layoutSize != rect.size() )
|
|
|
|
{
|
|
|
|
m_data->layoutSize = rect.size();
|
|
|
|
updateSegments( rect.size() );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
In case we have items that send LayoutRequest events on
|
|
|
|
geometry changes - what doesn't make much sense - we
|
|
|
|
better make a ( implicitely shared ) copy of the rows/columns.
|
|
|
|
*/
|
|
|
|
LayoutData data;
|
|
|
|
data.rows = m_data->rows;
|
|
|
|
data.columns = m_data->columns;
|
|
|
|
data.rect = rect;
|
|
|
|
|
|
|
|
data.direction = visualDirection();
|
|
|
|
if ( data.direction == Qt::LayoutDirectionAuto )
|
|
|
|
data.direction = QGuiApplication::layoutDirection();
|
|
|
|
|
|
|
|
m_data->layoutData = &data;
|
|
|
|
layoutItems();
|
|
|
|
m_data->layoutData = nullptr;
|
|
|
|
}
|
|
|
|
|
2019-09-05 10:46:42 +02:00
|
|
|
void QskLayoutEngine2D::layoutItem( QQuickItem* item, const QRect& grid ) const
|
2019-07-27 12:36:52 +02:00
|
|
|
{
|
|
|
|
auto layoutData = m_data->layoutData;
|
|
|
|
|
|
|
|
if ( layoutData == nullptr || item == nullptr )
|
|
|
|
return;
|
|
|
|
|
2019-09-10 17:01:47 +02:00
|
|
|
auto alignment = qskLayoutAlignmentHint( item );
|
2019-07-27 12:36:52 +02:00
|
|
|
alignment = m_data->effectiveAlignment( alignment );
|
|
|
|
|
2019-09-10 17:01:47 +02:00
|
|
|
auto rect = layoutData->geometryAt( grid );
|
|
|
|
rect = qskConstrainedItemRect( item, rect, alignment );
|
2019-07-27 12:36:52 +02:00
|
|
|
|
|
|
|
if ( layoutData->direction == Qt::RightToLeft )
|
|
|
|
{
|
|
|
|
const auto& r = layoutData->rect;
|
|
|
|
rect.moveRight( r.right() - ( rect.left() - r.left() ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
qskSetItemGeometry( item, rect );
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal QskLayoutEngine2D::widthForHeight( qreal height ) const
|
|
|
|
{
|
|
|
|
const QSizeF constraint( -1, height );
|
|
|
|
return sizeHint( Qt::PreferredSize, constraint ).width();
|
|
|
|
}
|
|
|
|
|
|
|
|
qreal QskLayoutEngine2D::heightForWidth( qreal width ) const
|
|
|
|
{
|
|
|
|
const QSizeF constraint( width, -1 );
|
|
|
|
return sizeHint( Qt::PreferredSize, constraint ).height();
|
|
|
|
}
|
|
|
|
|
|
|
|
QSizeF QskLayoutEngine2D::sizeHint(
|
|
|
|
Qt::SizeHint which, const QSizeF& constraint ) const
|
|
|
|
{
|
2019-09-15 14:32:51 +02:00
|
|
|
if ( constraint.isValid() )
|
|
|
|
return constraint; // should never happen
|
|
|
|
|
2019-07-27 12:36:52 +02:00
|
|
|
if ( effectiveCount( Qt::Horizontal ) <= 0 )
|
|
|
|
return QSizeF( 0.0, 0.0 );
|
|
|
|
|
2019-09-15 14:32:51 +02:00
|
|
|
auto requestType = constraintType();
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-09-15 14:32:51 +02:00
|
|
|
switch ( requestType )
|
2019-09-14 15:31:48 +02:00
|
|
|
{
|
2019-09-15 14:32:51 +02:00
|
|
|
case QskSizePolicy::HeightForWidth:
|
|
|
|
{
|
|
|
|
if ( constraint.height() >= 0 )
|
|
|
|
{
|
|
|
|
qWarning( "QskLayoutEngine2D: HeightForWidth conflict." );
|
|
|
|
return QSizeF();
|
|
|
|
}
|
2019-09-14 15:31:48 +02:00
|
|
|
|
2019-09-15 14:32:51 +02:00
|
|
|
if ( constraint.width() <= 0 )
|
|
|
|
requestType = QskSizePolicy::Unconstrained;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QskSizePolicy::WidthForHeight:
|
2019-09-14 15:31:48 +02:00
|
|
|
{
|
2019-09-15 14:32:51 +02:00
|
|
|
if ( constraint.width() >= 0 )
|
|
|
|
{
|
2019-09-14 15:31:48 +02:00
|
|
|
qWarning( "QskLayoutEngine2D: WidthForHeight conflict." );
|
2019-09-15 14:32:51 +02:00
|
|
|
return QSizeF();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( constraint.height() <= 0 )
|
|
|
|
requestType = QskSizePolicy::Unconstrained;
|
|
|
|
|
|
|
|
break;
|
2019-09-14 15:31:48 +02:00
|
|
|
}
|
2019-09-15 14:32:51 +02:00
|
|
|
default:
|
2019-09-14 15:31:48 +02:00
|
|
|
{
|
2019-09-15 14:32:51 +02:00
|
|
|
if ( constraint.height() >= 0 || constraint.width() >= 0 )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
As none of the items have the Constrained flag the constraint
|
|
|
|
will have no effect and we ignore it to make use of the cached
|
|
|
|
results from unconstrained requests
|
|
|
|
*/
|
|
|
|
qWarning( "QskLayoutEngine2D: constraint will be ignored." );
|
|
|
|
}
|
2019-09-14 15:31:48 +02:00
|
|
|
}
|
2019-09-15 14:32:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
auto& rowChain = m_data->rowChain;
|
|
|
|
auto& columnChain = m_data->columnChain;
|
2019-09-14 15:31:48 +02:00
|
|
|
|
2019-07-27 12:36:52 +02:00
|
|
|
m_data->blockInvalidate = true;
|
|
|
|
|
2019-09-15 14:32:51 +02:00
|
|
|
switch( requestType )
|
2019-07-27 12:36:52 +02:00
|
|
|
{
|
2019-09-15 14:32:51 +02:00
|
|
|
case QskSizePolicy::HeightForWidth:
|
|
|
|
{
|
|
|
|
setupChain( Qt::Horizontal );
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-09-15 14:32:51 +02:00
|
|
|
const auto constraints = columnChain.segments( constraint.width() );
|
|
|
|
setupChain( Qt::Vertical, constraints );
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-09-15 14:32:51 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QskSizePolicy::WidthForHeight:
|
|
|
|
{
|
|
|
|
setupChain( Qt::Vertical );
|
|
|
|
|
|
|
|
const auto constraints = rowChain.segments( constraint.height() );
|
|
|
|
setupChain( Qt::Horizontal, constraints );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
setupChain( Qt::Horizontal );
|
|
|
|
setupChain( Qt::Vertical );
|
|
|
|
}
|
2019-07-27 12:36:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
m_data->blockInvalidate = false;
|
|
|
|
|
2019-09-15 14:32:51 +02:00
|
|
|
QSizeF hint;
|
|
|
|
|
|
|
|
if ( constraint.width() <= 0.0 )
|
|
|
|
hint.rwidth() = columnChain.boundingHint().size( which );
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-09-15 14:32:51 +02:00
|
|
|
if ( constraint.height() <= 0.0 )
|
|
|
|
hint.rheight() = rowChain.boundingHint().size( which );
|
|
|
|
|
|
|
|
return hint;
|
2019-07-27 12:36:52 +02:00
|
|
|
}
|
|
|
|
|
2019-09-10 17:01:47 +02:00
|
|
|
QskLayoutHint QskLayoutEngine2D::layoutHint( const QQuickItem* item,
|
|
|
|
Qt::Orientation orientation, qreal constraint ) const
|
|
|
|
{
|
|
|
|
if ( item == nullptr )
|
|
|
|
return QskLayoutHint();
|
|
|
|
|
|
|
|
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 QskLayoutHint( minimum, preferred, maximum );
|
|
|
|
}
|
|
|
|
|
2019-07-27 12:36:52 +02:00
|
|
|
void QskLayoutEngine2D::setupChain( Qt::Orientation orientation ) const
|
|
|
|
{
|
|
|
|
setupChain( orientation, QskLayoutChain::Segments() );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskLayoutEngine2D::setupChain( Qt::Orientation orientation,
|
|
|
|
const QskLayoutChain::Segments& constraints ) const
|
|
|
|
{
|
|
|
|
const auto count = effectiveCount( orientation );
|
|
|
|
const qreal constraint =
|
|
|
|
constraints.isEmpty() ? -1.0 : constraints.last().end();
|
|
|
|
|
|
|
|
auto& chain = m_data->layoutChain( orientation );
|
|
|
|
|
2019-09-15 14:32:51 +02:00
|
|
|
if ( ( chain.constraint() == constraint ) && ( chain.count() == count ) )
|
2019-07-27 12:36:52 +02:00
|
|
|
{
|
|
|
|
return; // already up to date
|
|
|
|
}
|
|
|
|
|
|
|
|
chain.reset( count, constraint );
|
|
|
|
setupChain( orientation, constraints, chain );
|
|
|
|
chain.finish();
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
qDebug() << "==" << this << orientation << chain.count();
|
|
|
|
|
|
|
|
for ( int i = 0; i < chain.count(); i++ )
|
|
|
|
qDebug() << i << ":" << chain.cell( i );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskLayoutEngine2D::updateSegments( const QSizeF& size ) const
|
|
|
|
{
|
|
|
|
auto& rowChain = m_data->rowChain;
|
2019-07-28 17:06:50 +02:00
|
|
|
auto& columnChain = m_data->columnChain;
|
2019-07-27 12:36:52 +02:00
|
|
|
|
|
|
|
auto& rows = m_data->rows;
|
|
|
|
auto& columns = m_data->columns;
|
|
|
|
|
|
|
|
m_data->blockInvalidate = true;
|
|
|
|
|
|
|
|
switch( constraintType() )
|
|
|
|
{
|
2019-09-10 17:01:47 +02:00
|
|
|
case QskSizePolicy::WidthForHeight:
|
2019-07-27 12:36:52 +02:00
|
|
|
{
|
|
|
|
setupChain( Qt::Vertical );
|
|
|
|
rows = rowChain.segments( size.height() );
|
|
|
|
|
|
|
|
setupChain( Qt::Horizontal, rows );
|
2019-07-28 17:06:50 +02:00
|
|
|
columns = columnChain.segments( size.width() );
|
2019-07-27 12:36:52 +02:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2019-09-10 17:01:47 +02:00
|
|
|
case QskSizePolicy::HeightForWidth:
|
2019-07-27 12:36:52 +02:00
|
|
|
{
|
|
|
|
setupChain( Qt::Horizontal );
|
2019-07-28 17:06:50 +02:00
|
|
|
columns = columnChain.segments( size.width() );
|
2019-07-27 12:36:52 +02:00
|
|
|
|
|
|
|
setupChain( Qt::Vertical, m_data->columns );
|
|
|
|
rows = rowChain.segments( size.height() );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
setupChain( Qt::Horizontal );
|
2019-07-28 17:06:50 +02:00
|
|
|
columns = columnChain.segments( size.width() );
|
2019-07-27 12:36:52 +02:00
|
|
|
|
|
|
|
setupChain( Qt::Vertical );
|
|
|
|
rows = rowChain.segments( size.height() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_data->blockInvalidate = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskLayoutEngine2D::invalidate( int what )
|
|
|
|
{
|
|
|
|
if ( m_data->blockInvalidate )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( what & ElementCache )
|
|
|
|
{
|
|
|
|
m_data->constraintType = -1;
|
|
|
|
invalidateElementCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( what & LayoutCache )
|
|
|
|
{
|
|
|
|
m_data->rowChain.invalidate();
|
|
|
|
m_data->columnChain.invalidate();
|
|
|
|
|
|
|
|
m_data->layoutSize = QSize();
|
|
|
|
m_data->rows.clear();
|
|
|
|
m_data->columns.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-10 17:01:47 +02:00
|
|
|
QskSizePolicy::ConstraintType QskLayoutEngine2D::constraintType() const
|
2019-07-27 12:36:52 +02:00
|
|
|
{
|
|
|
|
if ( m_data->constraintType < 0 )
|
|
|
|
{
|
2019-09-10 17:01:47 +02:00
|
|
|
auto constraintType = QskSizePolicy::Unconstrained;
|
2019-07-27 12:36:52 +02:00
|
|
|
|
|
|
|
for ( int i = 0; i < count(); i++ )
|
|
|
|
{
|
2019-09-10 17:01:47 +02:00
|
|
|
const auto type = qskSizePolicy( itemAt( i ) ).constraintType();
|
2019-07-27 12:36:52 +02:00
|
|
|
|
2019-09-10 17:01:47 +02:00
|
|
|
if ( type != QskSizePolicy::Unconstrained )
|
2019-07-27 12:36:52 +02:00
|
|
|
{
|
2019-09-10 17:01:47 +02:00
|
|
|
if ( constraintType == QskSizePolicy::Unconstrained )
|
2019-07-27 12:36:52 +02:00
|
|
|
{
|
|
|
|
constraintType = type;
|
|
|
|
}
|
|
|
|
else if ( constraintType != type )
|
|
|
|
{
|
|
|
|
qWarning( "QskLayoutEngine2D: conflicting constraints");
|
2019-09-10 17:01:47 +02:00
|
|
|
constraintType = QskSizePolicy::Unconstrained;
|
2019-07-27 12:36:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_data->constraintType = constraintType;
|
|
|
|
}
|
|
|
|
|
2019-09-10 17:01:47 +02:00
|
|
|
return static_cast< QskSizePolicy::ConstraintType >( m_data->constraintType );
|
2019-07-27 12:36:52 +02:00
|
|
|
}
|
|
|
|
|