6#include "QskSubcontrolLayoutEngine.h"
7#include "QskLayoutElement.h"
8#include "QskLayoutChain.h"
9#include "QskTextRenderer.h"
10#include "QskSkinnable.h"
11#include "QskMargins.h"
12#include "QskTextOptions.h"
16#include <qfontmetrics.h>
19QskSubcontrolLayoutEngine::LayoutElement::LayoutElement(
21 : m_skinnable( skinnable )
22 , m_subControl( subControl )
26Qt::Alignment QskSubcontrolLayoutEngine::LayoutElement::alignment()
const
31void QskSubcontrolLayoutEngine::LayoutElement::setMaximumSize(
const QSizeF& size )
33 setExplicitSizeHint( Qt::MaximumSize, size );
36void QskSubcontrolLayoutEngine::LayoutElement::setMinimumSize(
const QSizeF& size )
38 setExplicitSizeHint( Qt::MinimumSize, size );
41void QskSubcontrolLayoutEngine::LayoutElement::setPreferredSize(
const QSizeF& size )
43 setExplicitSizeHint( Qt::PreferredSize, size );
46void QskSubcontrolLayoutEngine::LayoutElement::setFixedSize(
const QSizeF& size )
48 setSizePolicy(
QskSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ) );
50 const auto newSize = size.expandedTo( QSizeF( 0, 0 ) );
51 setExplicitSizeHint( Qt::PreferredSize, newSize );
54void QskSubcontrolLayoutEngine::LayoutElement::setExplicitSizeHint(
55 Qt::SizeHint which,
const QSizeF& size )
57 if ( which >= Qt::MinimumSize && which <= Qt::MaximumSize )
59 const QSizeF newSize( ( size.width() < 0 ) ? -1.0 : size.width(),
60 ( size.height() < 0 ) ? -1.0 : size.height() );
62 m_explicitSizeHints[ which ] = size;
66QSizeF QskSubcontrolLayoutEngine::LayoutElement::sizeHint(
67 Qt::SizeHint which,
const QSizeF& constraint )
const
69 if ( which < Qt::MinimumSize || which > Qt::MaximumSize )
70 return QSizeF( 0, 0 );
72 if ( constraint.isValid() )
75 const bool isConstrained =
76 constraint.width() >= 0 || constraint.height() >= 0;
83 hint = m_explicitSizeHints[ which ];
86 if ( which == Qt::PreferredSize )
90 const QskMargins padding = m_skinnable->paddingHint( m_subControl );
92 auto innerConstraint = constraint;
94 if ( constraint.width() > 0 )
96 const auto w = constraint.width() - padding.width();
100 innerConstraint.setWidth( w );
102 else if ( constraint.height() > 0 )
104 const auto h = constraint.height() - padding.height();
108 innerConstraint.setHeight( h );
111 hint = implicitSize( innerConstraint );
113 if ( hint.width() >= 0 )
114 hint.setWidth( hint.width() + padding.width() );
116 if ( hint.height() >= 0 )
117 hint.setHeight( hint.height() + padding.height() );
121 if ( !hint.isValid() )
123 const auto sz = implicitSize( constraint );
125 if ( hint.width() < 0 )
126 hint.setWidth( sz.width() );
128 if ( hint.height() < 0 )
129 hint.setHeight( sz.height() );
142QSizeF QskSubcontrolLayoutEngine::TextElement::implicitSize(
const QSizeF& constraint )
const
144 const auto font = skinnable()->effectiveFont( subControl() );
145 const auto textOptions = skinnable()->textOptionsHint( subControl() );
153 const qreal lineHeight = QFontMetricsF( font ).height();
155 if ( m_text.isEmpty() )
157 if ( constraint.height() < 0.0 )
158 hint.setHeight( qCeil( lineHeight ) );
160 else if ( constraint.width() >= 0.0 )
162 if ( textOptions.effectiveElideMode() != Qt::ElideNone )
164 hint.setHeight( qCeil( lineHeight ) );
174 qreal maxHeight = std::numeric_limits< qreal >::max();
175 if ( maxHeight / lineHeight > textOptions.maximumLineCount() )
178 maxHeight = textOptions.maximumLineCount() * lineHeight;
181 QSizeF size( constraint.width(), maxHeight );
182 size = QskTextRenderer::textSize( m_text, font, textOptions, size );
184 hint.setHeight( qCeil( size.height() ) );
187 else if ( constraint.height() >= 0.0 )
189 const qreal maxWidth = std::numeric_limits< qreal >::max();
191 QSizeF size( maxWidth, constraint.height() );
192 size = QskTextRenderer::textSize( m_text, font, textOptions, size );
194 hint.setWidth( qCeil( size.width() ) );
198 hint = QskTextRenderer::textSize( m_text, font, textOptions );
202 if ( hint.width() > 0.0 )
215QSizeF QskSubcontrolLayoutEngine::GraphicElement::effectiveStrutSize()
const
217 auto size = skinnable()->strutSizeHint( subControl() );
219 if ( size.isEmpty() )
221 const qreal aspectRatio = m_sourceSize.width() / m_sourceSize.height();
223 if ( size.width() > 0 )
225 size.setHeight( size.width() / aspectRatio );
227 else if ( size.height() > 0 )
229 size.setWidth( size.height() * aspectRatio );
236QSizeF QskSubcontrolLayoutEngine::GraphicElement::implicitSize(
237 const QSizeF& constraint )
const
239 auto hint = m_sourceSize;
241 if ( !hint.isEmpty() )
243 const qreal aspectRatio = hint.width() / hint.height();
245 if ( constraint.width() >= 0.0 )
247 hint.setHeight( constraint.width() / aspectRatio );
248 hint.setWidth( -1.0 );
250 else if ( constraint.height() > 0.0 )
252 hint.setWidth( constraint.height() * aspectRatio );
253 hint.setHeight( -1.0 );
262 Qt::Orientation orientation,
bool isLayoutOrientation, qreal constraint )
267 const auto policy = element->sizePolicy().policy( orientation );
269 if ( isLayoutOrientation )
271 const auto stretch = element->stretch();
274 cell.stretch = ( policy & QskSizePolicy::ExpandFlag ) ? 1 : 0;
276 cell.stretch = stretch;
279 cell.canGrow = policy & QskSizePolicy::GrowFlag;
280 cell.metrics = element->metrics( orientation, constraint );
285class QskSubcontrolLayoutEngine::PrivateData
288 PrivateData( Qt::Orientation orientation )
289 : orientation( orientation )
291 elements.reserve( 2 );
294 Qt::Orientation orientation;
295 QVector< LayoutElement* > elements;
298QskSubcontrolLayoutEngine::QskSubcontrolLayoutEngine( Qt::Orientation orientation )
299 : m_data( new PrivateData( orientation ) )
301 setExtraSpacingAt( Qt::TopEdge | Qt::BottomEdge | Qt::LeftEdge | Qt::RightEdge );
304QskSubcontrolLayoutEngine::~QskSubcontrolLayoutEngine()
306 qDeleteAll( m_data->elements );
309bool QskSubcontrolLayoutEngine::setOrientation( Qt::Orientation orientation )
311 if ( m_data->orientation != orientation )
313 m_data->orientation = orientation;
314 invalidate( LayoutCache );
322Qt::Orientation QskSubcontrolLayoutEngine::orientation()
const
324 return m_data->orientation;
327void QskSubcontrolLayoutEngine::setSpacing( qreal spacing )
329 Inherited::setSpacing( spacing, Qt::Horizontal | Qt::Vertical );
332qreal QskSubcontrolLayoutEngine::spacing()
const
334 return Inherited::spacing( m_data->orientation );
337void QskSubcontrolLayoutEngine::setGraphicTextElements(
const QskSkinnable* skinnable,
348 auto graphicElement = appendGraphicElement( skinnable, graphicSubControl, graphicSize );
349 auto textElement = appendTextElement( skinnable, textSubcontrol, text );
359 if ( textElement && graphicElement ==
nullptr )
361 textElement->setSizePolicy( SP::Preferred, SP::Constrained );
363 else if ( graphicElement && textElement ==
nullptr )
365 const auto size = graphicElement->effectiveStrutSize();
367 if ( !size.isEmpty() )
368 graphicElement->setFixedSize( size );
370 graphicElement->setSizePolicy( SP::Ignored, SP::ConstrainedExpanding );
372 else if ( textElement && graphicElement )
374 if ( orientation() == Qt::Horizontal )
376 graphicElement->setSizePolicy( SP::Constrained, SP::Fixed );
377 textElement->setSizePolicy( SP::Preferred, SP::Preferred );
381 graphicElement->setSizePolicy( SP::Preferred, SP::Fixed );
382 textElement->setSizePolicy( SP::Preferred, SP::Constrained );
385 auto size = graphicElement->effectiveStrutSize();
387 if ( size.isEmpty() )
389 const auto h = 1.5 * skinnable->effectiveFontHeight( textSubcontrol );
391 size.setWidth( graphicElement->widthForHeight( h ) );
395 graphicElement->setPreferredSize( size );
402 GraphicElement* graphicElement =
nullptr;
404 if ( !graphicSize.isEmpty() && ( graphicSubcontrol != QskAspect::NoSubcontrol ) )
406 graphicElement =
dynamic_cast< GraphicElement*
>( element( graphicSubcontrol ) );
407 if ( graphicElement ==
nullptr )
409 graphicElement =
new GraphicElement( skinnable, graphicSubcontrol );
410 m_data->elements.prepend( graphicElement );
413 graphicElement->setSourceSize( graphicSize );
416 return graphicElement;
422 TextElement* textElement =
nullptr;
424 if ( !text.isEmpty() && ( textSubcontrol != QskAspect::NoSubcontrol ) )
426 textElement =
dynamic_cast< TextElement*
>( element( textSubcontrol ) );
427 if ( textElement ==
nullptr )
429 textElement =
new TextElement( skinnable, textSubcontrol );
430 m_data->elements.append( textElement );
433 textElement->setText( text );
439void QskSubcontrolLayoutEngine::setFixedContent(
442 if(
auto e = element( subcontrol ) )
443 e->setSizePolicy( QskSizePolicy::Maximum, e->sizePolicy().verticalPolicy() );
445 Qt::Edges extraSpacing;
447 switch( orientation )
451 extraSpacing |= ( extraSpacingAt() & ( Qt::TopEdge | Qt::BottomEdge ) );
453 if( alignment & Qt::AlignLeft )
455 extraSpacing |= Qt::RightEdge;
457 else if( alignment & Qt::AlignRight )
459 extraSpacing |= Qt::LeftEdge;
461 else if( alignment & Qt::AlignHCenter )
463 extraSpacing |= Qt::LeftEdge | Qt::RightEdge;
469 extraSpacing |= ( extraSpacingAt() & ( Qt::LeftEdge | Qt::RightEdge ) );
471 if( alignment & Qt::AlignTop )
473 extraSpacing |= Qt::BottomEdge;
475 else if( alignment & Qt::AlignBottom )
477 extraSpacing |= Qt::TopEdge;
479 else if( alignment & Qt::AlignVCenter )
481 extraSpacing |= Qt::TopEdge | Qt::BottomEdge;
487 setExtraSpacingAt( extraSpacing );
490void QskSubcontrolLayoutEngine::addElement( LayoutElement* element )
492 m_data->elements += element;
497 if ( index >= 0 && index < count() )
498 return m_data->elements[ index ];
506 for (
auto element : m_data->elements )
508 if ( element->subControl() == subControl )
515QskSizePolicy QskSubcontrolLayoutEngine::sizePolicyAt(
int index )
const
517 if ( index >= 0 && index < count() )
519 if (
auto element = m_data->elements[ index ] )
520 return element->sizePolicy();
526int QskSubcontrolLayoutEngine::count()
const
528 return m_data->elements.count();
531void QskSubcontrolLayoutEngine::layoutItems()
536 int& index = m_data->orientation == Qt::Horizontal ? col : row;
538 for (
auto element : m_data->elements )
540 const auto rect = geometryAt( element, QRect( col, row, 1, 1 ) );
541 element->setGeometry( rect );
547int QskSubcontrolLayoutEngine::effectiveCount( Qt::Orientation orientation )
const
549 const auto count = m_data->elements.count();
551 if ( orientation == m_data->orientation )
554 return ( count >= 1 ) ? 1 : 0;
559 if (
const auto el = element( subControl ) )
560 return el->geometry();
562 return QRectF( 0.0, 0.0, -1.0, -1.0 );
565void QskSubcontrolLayoutEngine::invalidateElementCache()
569void QskSubcontrolLayoutEngine::setupChain( Qt::Orientation orientation,
570 const QskLayoutChain::Segments& constraints,
QskLayoutChain& chain )
const
575 const bool isLayoutOrientation = ( orientation == m_data->orientation );
577 for (
auto element : m_data->elements )
579 qreal constraint = -1.0;
580 if ( !constraints.isEmpty() )
581 constraint = constraints[ index1 ].length;
583 const auto cell = qskCell( element, orientation, isLayoutOrientation, constraint );
584 chain.expandCell( index2, cell );
586 if ( isLayoutOrientation )
Subcontrol
For use within the rendering or lay-outing of a specific QskSkinnable.
Qt::Alignment alignmentHint(QskAspect, Qt::Alignment=Qt::Alignment()) const
Retrieves an alignment hint.