6#include "QskLayoutEngine2D.h"
7#include "QskLayoutChain.h"
8#include "QskLayoutElement.h"
9#include "QskFunctions.h"
11#include <qguiapplication.h>
19 QRectF geometryAt(
const QRect& grid )
const
21 const auto x1 = columns[ grid.left() ].start;
22 const auto x2 = columns[ grid.right() ].end();
23 const auto y1 = rows[ grid.top() ].start;
24 const auto y2 = rows[ grid.bottom() ].end();
26 return QRectF( rect.x() + x1, rect.y() + y1, x2 - x1, y2 - y1 );
29 Qt::LayoutDirection direction;
32 QskLayoutChain::Segments rows;
33 QskLayoutChain::Segments columns;
37class QskLayoutEngine2D::PrivateData
41 : defaultAlignment( Qt::AlignLeft | Qt::AlignVCenter )
44 , constraintType( -1 )
45 , blockInvalidate( false )
51 return ( orientation == Qt::Horizontal ) ? columnChain : rowChain;
54 inline Qt::Alignment effectiveAlignment( Qt::Alignment alignment )
const
56 const auto align =
static_cast< Qt::Alignment
>( defaultAlignment );
58 if ( !( alignment & Qt::AlignVertical_Mask ) )
59 alignment |= ( align & Qt::AlignVertical_Mask );
61 if ( !( alignment & Qt::AlignHorizontal_Mask ) )
62 alignment |= ( align & Qt::AlignHorizontal_Mask );
72 QskLayoutChain::Segments rows;
73 QskLayoutChain::Segments columns;
75 const LayoutData* layoutData =
nullptr;
77 unsigned int defaultAlignment : 8;
78 unsigned int extraSpacingAt : 4;
79 unsigned int visualDirection : 4;
81 int constraintType : 3;
89 bool blockInvalidate : 1;
92QskLayoutEngine2D::QskLayoutEngine2D()
93 : m_data( new PrivateData )
95 m_data->columnChain.setSpacing( defaultSpacing( Qt::Horizontal ) );
96 m_data->rowChain.setSpacing( defaultSpacing( Qt::Vertical ) );
99QskLayoutEngine2D::~QskLayoutEngine2D()
103bool QskLayoutEngine2D::setVisualDirection( Qt::LayoutDirection direction )
105 if ( m_data->visualDirection != direction )
107 m_data->visualDirection = direction;
114Qt::LayoutDirection QskLayoutEngine2D::visualDirection()
const
116 return static_cast< Qt::LayoutDirection
>( m_data->visualDirection );
119bool QskLayoutEngine2D::setDefaultAlignment( Qt::Alignment alignment )
121 if ( defaultAlignment() != alignment )
123 m_data->defaultAlignment = alignment;
130Qt::Alignment QskLayoutEngine2D::defaultAlignment()
const
132 return static_cast< Qt::Alignment
>( m_data->defaultAlignment );
135qreal QskLayoutEngine2D::defaultSpacing( Qt::Orientation )
const
140bool QskLayoutEngine2D::setSpacing(
141 qreal spacing, Qt::Orientations orientations )
146 bool isModified =
false;
148 for (
auto o : { Qt::Horizontal, Qt::Vertical } )
150 if ( orientations & o )
151 isModified |= m_data->layoutChain( o ).setSpacing( spacing );
155 invalidate( LayoutCache );
160qreal QskLayoutEngine2D::spacing( Qt::Orientation orientation )
const
162 return m_data->layoutChain( orientation ).spacing();
165bool QskLayoutEngine2D::setExtraSpacingAt( Qt::Edges edges )
167 if ( edges == extraSpacingAt() )
170 m_data->extraSpacingAt = edges;
175 if ( edges & Qt::LeftEdge )
176 fillMode |= QskLayoutChain::Leading;
178 if ( edges & Qt::RightEdge )
179 fillMode |= QskLayoutChain::Trailing;
181 m_data->columnChain.setFillMode( fillMode );
187 if ( edges & Qt::TopEdge )
188 fillMode |= QskLayoutChain::Leading;
190 if ( edges & Qt::BottomEdge )
191 fillMode |= QskLayoutChain::Trailing;
193 m_data->rowChain.setFillMode( fillMode );
196 m_data->layoutSize = QSize();
197 m_data->rows.clear();
198 m_data->columns.clear();
203Qt::Edges QskLayoutEngine2D::extraSpacingAt()
const
205 return static_cast< Qt::Edges
>( m_data->extraSpacingAt );
208void QskLayoutEngine2D::setGeometries(
const QRectF& rect )
210 if ( rowCount() < 1 || columnCount() < 1 )
213 if ( m_data->layoutSize != rect.size() )
215 m_data->layoutSize = rect.size();
216 updateSegments( rect.size() );
225 data.rows = m_data->rows;
226 data.columns = m_data->columns;
229 data.direction = visualDirection();
230 if ( data.direction == Qt::LayoutDirectionAuto )
231 data.direction = QGuiApplication::layoutDirection();
233 m_data->layoutData = &data;
235 m_data->layoutData =
nullptr;
238QRectF QskLayoutEngine2D::geometryAt(
241 const auto layoutData = m_data->layoutData;
243 if ( layoutData && element )
245 auto rect = layoutData->geometryAt( grid );
247 const auto size = element->constrainedSize( rect.size() );
248 const auto alignment = m_data->effectiveAlignment( element->alignment() );
250 rect = qskAlignedRectF( rect, size, alignment );
252 if ( layoutData->direction == Qt::RightToLeft )
254 const auto& r = layoutData->rect;
255 rect.moveRight( r.right() - ( rect.left() - r.left() ) );
261 return QRectF( 0, 0, -1, -1 );
264qreal QskLayoutEngine2D::widthForHeight( qreal height )
const
266 const QSizeF constraint( -1, height );
267 return sizeHint( Qt::PreferredSize, constraint ).width();
270qreal QskLayoutEngine2D::heightForWidth( qreal width )
const
272 const QSizeF constraint( width, -1 );
273 return sizeHint( Qt::PreferredSize, constraint ).height();
276QSizeF QskLayoutEngine2D::sizeHint(
277 Qt::SizeHint which,
const QSizeF& constraint )
const
279 if ( constraint.isValid() )
282 if ( effectiveCount( Qt::Horizontal ) <= 0 )
283 return QSizeF( 0.0, 0.0 );
285 auto requestType = constraintType();
287 switch ( requestType )
289 case QskSizePolicy::HeightForWidth:
291 if ( constraint.height() >= 0 )
293 qWarning(
"QskLayoutEngine2D: HeightForWidth conflict." );
297 if ( constraint.width() <= 0 )
298 requestType = QskSizePolicy::Unconstrained;
302 case QskSizePolicy::WidthForHeight:
304 if ( constraint.width() >= 0 )
306 qWarning(
"QskLayoutEngine2D: WidthForHeight conflict." );
310 if ( constraint.height() <= 0 )
311 requestType = QskSizePolicy::Unconstrained;
317 if ( constraint.height() >= 0 || constraint.width() >= 0 )
325 qWarning(
"QskLayoutEngine2D: constraint will be ignored." );
331 auto& rowChain = m_data->rowChain;
332 auto& columnChain = m_data->columnChain;
334 m_data->blockInvalidate =
true;
336 switch( requestType )
338 case QskSizePolicy::HeightForWidth:
340 setupChain( Qt::Horizontal );
342 const auto constraints = columnChain.segments( constraint.width() );
343 setupChain( Qt::Vertical, constraints );
347 case QskSizePolicy::WidthForHeight:
349 setupChain( Qt::Vertical );
351 const auto constraints = rowChain.segments( constraint.height() );
352 setupChain( Qt::Horizontal, constraints );
358 setupChain( Qt::Horizontal );
359 setupChain( Qt::Vertical );
363 m_data->blockInvalidate =
false;
367 if ( constraint.width() <= 0.0 )
368 hint.rwidth() = columnChain.boundingMetrics().metric( which );
370 if ( constraint.height() <= 0.0 )
371 hint.rheight() = rowChain.boundingMetrics().metric( which );
376void QskLayoutEngine2D::setupChain( Qt::Orientation orientation )
const
378 setupChain( orientation, QskLayoutChain::Segments() );
381void QskLayoutEngine2D::setupChain( Qt::Orientation orientation,
382 const QskLayoutChain::Segments& constraints )
const
384 const auto count = effectiveCount( orientation );
385 const qreal constraint =
386 constraints.isEmpty() ? -1.0 : constraints.last().end();
388 auto& chain = m_data->layoutChain( orientation );
390 if ( ( chain.constraint() == constraint ) && ( chain.count() == count ) )
395 chain.reset( count, constraint );
396 setupChain( orientation, constraints, chain );
400 qDebug() <<
"==" <<
this << orientation << chain.count();
402 for (
int i = 0; i < chain.count(); i++ )
403 qDebug() << i <<
":" << chain.cell( i );
407void QskLayoutEngine2D::updateSegments(
const QSizeF& size )
const
409 auto& rowChain = m_data->rowChain;
410 auto& columnChain = m_data->columnChain;
412 auto& rows = m_data->rows;
413 auto& columns = m_data->columns;
415 m_data->blockInvalidate =
true;
417 switch( constraintType() )
419 case QskSizePolicy::WidthForHeight:
421 setupChain( Qt::Vertical );
422 rows = rowChain.segments( size.height() );
424 setupChain( Qt::Horizontal, rows );
425 columns = columnChain.segments( size.width() );
429 case QskSizePolicy::HeightForWidth:
431 setupChain( Qt::Horizontal );
432 columns = columnChain.segments( size.width() );
434 setupChain( Qt::Vertical, m_data->columns );
435 rows = rowChain.segments( size.height() );
441 setupChain( Qt::Horizontal );
442 columns = columnChain.segments( size.width() );
444 setupChain( Qt::Vertical );
445 rows = rowChain.segments( size.height() );
449 m_data->blockInvalidate =
false;
452void QskLayoutEngine2D::invalidate(
int what )
454 if ( m_data->blockInvalidate )
457 if ( what & ElementCache )
459 m_data->constraintType = -1;
460 invalidateElementCache();
463 if ( what & LayoutCache )
465 m_data->rowChain.invalidate();
466 m_data->columnChain.invalidate();
468 m_data->layoutSize = QSize();
469 m_data->rows.clear();
470 m_data->columns.clear();
474QskSizePolicy::ConstraintType QskLayoutEngine2D::constraintType()
const
476 if ( m_data->constraintType < 0 )
478 auto constraintType = QskSizePolicy::Unconstrained;
480 for (
int i = 0; i < count(); i++ )
482 const auto type = sizePolicyAt( i ).constraintType();
484 if ( type != QskSizePolicy::Unconstrained )
486 if ( constraintType == QskSizePolicy::Unconstrained )
488 constraintType = type;
490 else if ( constraintType != type )
492 qWarning(
"QskLayoutEngine2D: conflicting constraints");
493 constraintType = QskSizePolicy::Unconstrained;
498 m_data->constraintType = constraintType;
501 return static_cast< QskSizePolicy::ConstraintType
>( m_data->constraintType );