QskSubcontrolLayoutEngine introduced
This commit is contained in:
parent
896145ac11
commit
b84dfc8226
@ -115,11 +115,17 @@ Qsk.Window
|
||||
enabled: false
|
||||
}
|
||||
|
||||
|
||||
TestButton
|
||||
{
|
||||
graphicSource: "image://shapes/Diamond/SandyBrown"
|
||||
|
||||
graphicStrutSize
|
||||
{
|
||||
// no strutSize, so that the graphic is adjustd
|
||||
width: -1
|
||||
height : -1
|
||||
}
|
||||
|
||||
shape
|
||||
{
|
||||
sizeMode: Qt.RelativeSize
|
||||
|
@ -490,16 +490,15 @@ void Editor::setupPushButton()
|
||||
|
||||
setBoxShape( Q::Panel, 100, Qt::RelativeSize );
|
||||
|
||||
setAlignment( Q::Graphic | A::Alignment, Qt::AlignCenter );
|
||||
setAlignment( Q::Graphic, Qt::AlignCenter );
|
||||
setStrutSize( Q::Graphic, 24_dp, 24_dp );
|
||||
setPadding( Q::Graphic, 0 );
|
||||
|
||||
setFontRole( Q::Text, QskMaterial3Skin::M3LabelLarge );
|
||||
setPadding( Q::Text, 0 );
|
||||
|
||||
setAlignment( Q::Text | A::Alignment, Qt::AlignCenter );
|
||||
setAlignment( Q::Text | A::Alignment | A::Horizontal,
|
||||
Qt::AlignLeft | Qt::AlignVCenter );
|
||||
setAlignment( Q::Text | A::Vertical, Qt::AlignCenter );
|
||||
setAlignment( Q::Text | A::Horizontal, Qt::AlignLeft | Qt::AlignVCenter );
|
||||
|
||||
// normal buttons (i.e. Filled):
|
||||
|
||||
|
@ -607,13 +607,15 @@ void Editor::setupPushButton()
|
||||
setTextOptions( Q::Text, Qt::ElideMiddle, QskTextOptions::NoWrap );
|
||||
|
||||
setFlagHint( Q::Text | Q::Disabled | A::Style, Qsk::Sunken );
|
||||
setAlignment( Q::Text, Qt::AlignCenter );
|
||||
|
||||
setAlignment( Q::Text | A::Vertical, Qt::AlignCenter );
|
||||
setAlignment( Q::Text | A::Horizontal, Qt::AlignLeft | Qt::AlignVCenter );
|
||||
|
||||
setColor( Q::Text, m_pal.themeForeground );
|
||||
setColor( Q::Text | Q::Disabled, m_pal.darker200 );
|
||||
|
||||
// Graphic
|
||||
setPadding( Q::Graphic, 2 );
|
||||
setAlignment( Q::Graphic, Qt::AlignCenter );
|
||||
}
|
||||
|
||||
void Editor::setupDialogButton()
|
||||
|
@ -8,13 +8,9 @@
|
||||
|
||||
#include "QskAnimationHint.h"
|
||||
#include "QskGraphic.h"
|
||||
#include "QskTextOptions.h"
|
||||
#include "QskFunctions.h"
|
||||
#include "QskSubcontrolLayoutEngine.h"
|
||||
#include "QskSGNode.h"
|
||||
|
||||
#include <qfontmetrics.h>
|
||||
#include <qmath.h>
|
||||
|
||||
static inline Qt::Orientation qskOrientation( const QskPushButton* button )
|
||||
{
|
||||
// For the moment we only handle the orientation TODO ...
|
||||
@ -28,6 +24,76 @@ static inline Qt::Orientation qskOrientation( const QskPushButton* button )
|
||||
return Qt::Vertical;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class LayoutEngine : public QskSubcontrolLayoutEngine
|
||||
{
|
||||
public:
|
||||
LayoutEngine( const QskPushButton* button )
|
||||
: QskSubcontrolLayoutEngine( qskOrientation( button ) )
|
||||
{
|
||||
using Q = QskPushButton;
|
||||
|
||||
const auto graphicSourceSize = button->graphic().defaultSize();
|
||||
|
||||
const bool hasText = !button->text().isEmpty();
|
||||
const bool hasGraphic = !graphicSourceSize.isEmpty();
|
||||
|
||||
auto graphicElement = new GraphicElement( button, Q::Graphic );
|
||||
graphicElement->setSourceSize( graphicSourceSize );
|
||||
graphicElement->setIgnored( !hasGraphic );
|
||||
|
||||
auto textElement = new TextElement( button, Q::Text );
|
||||
textElement->setText( button->text() );
|
||||
textElement->setIgnored( !hasText );
|
||||
|
||||
using SP = QskSizePolicy;
|
||||
|
||||
if ( hasText && !hasGraphic )
|
||||
{
|
||||
textElement->setSizePolicy( SP::Preferred, SP::Constrained );
|
||||
}
|
||||
else if ( hasGraphic && !hasText )
|
||||
{
|
||||
const auto size = graphicElement->effectiveStrutSize();
|
||||
|
||||
if ( !size.isEmpty() )
|
||||
graphicElement->setFixedSize( size );
|
||||
else
|
||||
graphicElement->setSizePolicy( SP::Ignored, SP::ConstrainedExpanding );
|
||||
}
|
||||
else if ( hasText && hasGraphic )
|
||||
{
|
||||
if ( orientation() == Qt::Horizontal )
|
||||
{
|
||||
graphicElement->setSizePolicy( SP::Constrained, SP::Fixed );
|
||||
textElement->setSizePolicy( SP::Preferred, SP::Preferred );
|
||||
}
|
||||
else
|
||||
{
|
||||
graphicElement->setSizePolicy( SP::Fixed, SP::Fixed );
|
||||
textElement->setSizePolicy( SP::Preferred, SP::Constrained );
|
||||
}
|
||||
|
||||
auto size = graphicElement->effectiveStrutSize();
|
||||
|
||||
if ( size.isEmpty() )
|
||||
{
|
||||
const auto h = 1.5 * button->effectiveFontHeight( Q::Text );
|
||||
|
||||
size.setWidth( graphicElement->widthForHeight( h ) );
|
||||
size.setHeight( h );
|
||||
}
|
||||
|
||||
graphicElement->setPreferredSize( size );
|
||||
}
|
||||
|
||||
setElementAt( 0, graphicElement );
|
||||
setElementAt( 1, textElement );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
QskPushButtonSkinlet::QskPushButtonSkinlet( QskSkin* skin )
|
||||
: Inherited( skin )
|
||||
{
|
||||
@ -101,29 +167,12 @@ QRectF QskPushButtonSkinlet::textRect(
|
||||
{
|
||||
using Q = QskPushButton;
|
||||
|
||||
auto r = button->subControlContentsRect( contentsRect, Q::Panel );
|
||||
if ( r.isEmpty() || !button->hasGraphic() )
|
||||
return r;
|
||||
const auto r = button->subControlContentsRect( contentsRect, Q::Panel );
|
||||
|
||||
if ( qskOrientation( button ) == Qt::Horizontal )
|
||||
{
|
||||
/*
|
||||
For horizontal layouts Text depends on Graphic, while
|
||||
for vertical Graphic depends on Text. Confusing ...
|
||||
*/
|
||||
const auto graphicsRect = subControlRect( button, contentsRect, Q::Graphic );
|
||||
const auto spacing = button->spacingHint( Q::Panel );
|
||||
LayoutEngine layoutEngine( button );
|
||||
layoutEngine.setGeometries( r );
|
||||
|
||||
r.setX( r.x() + graphicsRect.width() + spacing );
|
||||
}
|
||||
else
|
||||
{
|
||||
const qreal h = button->effectiveFontHeight( Q::Text );
|
||||
if ( h < r.height() )
|
||||
r.setTop( r.bottom() - h );
|
||||
}
|
||||
|
||||
return r;
|
||||
return layoutEngine.elementAt( 1 )->geometry();
|
||||
}
|
||||
|
||||
QRectF QskPushButtonSkinlet::graphicRect(
|
||||
@ -131,81 +180,12 @@ QRectF QskPushButtonSkinlet::graphicRect(
|
||||
{
|
||||
using Q = QskPushButton;
|
||||
|
||||
auto r = button->subControlContentsRect( contentsRect, Q::Panel );
|
||||
if ( r.isEmpty() || button->text().isEmpty() )
|
||||
return r;
|
||||
const auto r = button->subControlContentsRect( contentsRect, Q::Panel );
|
||||
|
||||
const auto orientation = qskOrientation( button );
|
||||
const auto maxSize = button->strutSizeHint( Q::Graphic );
|
||||
LayoutEngine layoutEngine( button );
|
||||
layoutEngine.setGeometries( r );
|
||||
|
||||
const qreal maxW = maxSize.width();
|
||||
const qreal maxH = maxSize.height();
|
||||
|
||||
if ( orientation == Qt::Vertical )
|
||||
{
|
||||
/*
|
||||
For horizontal layouts Text depends on Graphic, while
|
||||
for vertical Graphic depends on Text. Confusing ...
|
||||
*/
|
||||
const auto textRect = subControlRect( button, contentsRect, Q::Text );
|
||||
const auto h = textRect.height() + button->spacingHint( Q::Panel );
|
||||
|
||||
if ( h > r.height() )
|
||||
return QRectF();
|
||||
|
||||
r.setBottom( r.bottom() - h );
|
||||
|
||||
if ( maxW >= 0 || maxH >= 0 )
|
||||
{
|
||||
// limiting the size by maxSize
|
||||
|
||||
if ( maxW >= 0.0 && maxW < r.width() )
|
||||
{
|
||||
r.setX( r.center().x() - 0.5 * maxW );
|
||||
r.setWidth( maxW );
|
||||
}
|
||||
|
||||
if ( maxH >= 0.0 && maxH < r.height() )
|
||||
{
|
||||
r.setY( r.center().y() - 0.5 * maxH );
|
||||
r.setHeight( maxH );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( maxW >= 0 || maxH >= 0 )
|
||||
{
|
||||
if ( maxW >= 0.0 && maxW < r.width() )
|
||||
{
|
||||
r.setWidth( maxW );
|
||||
}
|
||||
|
||||
if ( maxH >= 0.0 && maxH < r.height() )
|
||||
{
|
||||
r.setY( r.center().y() - 0.5 * maxH );
|
||||
r.setHeight( maxH );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r = r.marginsRemoved( button->paddingHint( Q::Graphic ) );
|
||||
|
||||
if ( !r.isEmpty() )
|
||||
{
|
||||
auto sz = button->graphic().defaultSize();
|
||||
if ( !sz.isEmpty() )
|
||||
{
|
||||
sz.scale( r.size(), Qt::KeepAspectRatio );
|
||||
|
||||
if ( orientation == Qt::Vertical )
|
||||
r = qskAlignedRectF( r, sz, Qt::AlignCenter );
|
||||
else
|
||||
r = qskAlignedRectF( r, sz, Qt::AlignLeft | Qt::AlignVCenter );
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
return layoutEngine.elementAt( 0 )->geometry();
|
||||
}
|
||||
|
||||
QRectF QskPushButtonSkinlet::rippleRect(
|
||||
@ -226,7 +206,7 @@ QRectF QskPushButtonSkinlet::rippleRect(
|
||||
rect.setSize( 2.0 * panelRect.size() * ratio );
|
||||
rect.moveCenter( pos );
|
||||
}
|
||||
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
@ -271,7 +251,7 @@ QSGNode* QskPushButtonSkinlet::updateRippleNode(
|
||||
if ( boxNode->parent() != clipNode )
|
||||
clipNode->appendChildNode( boxNode );
|
||||
}
|
||||
|
||||
|
||||
return clipNode;
|
||||
}
|
||||
|
||||
@ -285,35 +265,9 @@ QSizeF QskPushButtonSkinlet::sizeHint( const QskSkinnable* skinnable,
|
||||
|
||||
const auto button = static_cast< const QskPushButton* >( skinnable );
|
||||
|
||||
QSizeF size( 0, 0 );
|
||||
LayoutEngine layoutEngine( button );
|
||||
|
||||
const QFontMetricsF fm( button->effectiveFont( Q::Text ) );
|
||||
|
||||
if ( !button->text().isEmpty() )
|
||||
{
|
||||
// in elide mode we might want to ignore the text width ???
|
||||
|
||||
size += fm.size( Qt::TextShowMnemonic, button->text() );
|
||||
}
|
||||
|
||||
if ( button->hasGraphic() )
|
||||
{
|
||||
const auto hint = graphicSizeHint( button );
|
||||
|
||||
const auto padding = button->paddingHint( Q::Graphic );
|
||||
const auto orientation = qskOrientation( button );
|
||||
|
||||
if( orientation == Qt::Horizontal )
|
||||
{
|
||||
size.rwidth() += padding.left() + hint.width() + padding.right();
|
||||
size.rheight() = qMax( size.height(), hint.height() );
|
||||
}
|
||||
else
|
||||
{
|
||||
size.rheight() += padding.top() + hint.height() + padding.bottom();
|
||||
size.rwidth() = qMax( size.width(), hint.width() );
|
||||
}
|
||||
}
|
||||
auto size = layoutEngine.sizeHint( which, QSizeF() );
|
||||
|
||||
size = size.expandedTo( button->strutSizeHint( Q::Panel ) );
|
||||
size = button->outerBoxSize( Q::Panel, size );
|
||||
@ -321,32 +275,4 @@ QSizeF QskPushButtonSkinlet::sizeHint( const QskSkinnable* skinnable,
|
||||
return size;
|
||||
}
|
||||
|
||||
QSizeF QskPushButtonSkinlet::graphicSizeHint( const QskPushButton* button ) const
|
||||
{
|
||||
using Q = QskPushButton;
|
||||
|
||||
auto size = button->strutSizeHint( Q::Graphic );
|
||||
if ( !size.isEmpty() )
|
||||
return size;
|
||||
|
||||
const auto& graphic = button->graphic();
|
||||
|
||||
auto w = size.width();
|
||||
auto h = size.height();
|
||||
|
||||
if ( w > 0.0 )
|
||||
{
|
||||
h = graphic.heightForWidth( w );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( h <= 0.0 )
|
||||
h = 1.5 * button->effectiveFontHeight( Q::Text );
|
||||
|
||||
w = graphic.widthForHeight( h );
|
||||
}
|
||||
|
||||
return QSizeF( w, h );
|
||||
}
|
||||
|
||||
#include "moc_QskPushButtonSkinlet.cpp"
|
||||
|
@ -45,8 +45,6 @@ class QSK_EXPORT QskPushButtonSkinlet : public QskSkinlet
|
||||
QRectF graphicRect( const QskPushButton*, const QRectF& ) const;
|
||||
QRectF rippleRect( const QskPushButton*, const QRectF& ) const;
|
||||
|
||||
QSizeF graphicSizeHint( const QskPushButton* ) const;
|
||||
|
||||
QSGNode* updateTextNode( const QskPushButton*, QSGNode* ) const;
|
||||
QSGNode* updateRippleNode( const QskPushButton*, QSGNode* ) const;
|
||||
};
|
||||
|
@ -28,17 +28,17 @@ class QskLayoutElement
|
||||
|
||||
QSizeF sizeConstraint( Qt::SizeHint, const QSizeF& constraint ) const;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY( QskLayoutElement )
|
||||
|
||||
qreal metric( Qt::Orientation, Qt::SizeHint, qreal constraint ) const;
|
||||
|
||||
virtual QSizeF sizeHint( Qt::SizeHint,
|
||||
const QSizeF& constraint = QSizeF() ) const = 0;
|
||||
|
||||
qreal heightForWidth( qreal ) const;
|
||||
qreal widthForHeight( qreal ) const;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY( QskLayoutElement )
|
||||
|
||||
qreal metric( Qt::Orientation, Qt::SizeHint, qreal constraint ) const;
|
||||
|
||||
qreal boundedSize( Qt::Orientation, qreal ) const;
|
||||
};
|
||||
|
||||
|
398
src/layouts/QskSubcontrolLayoutEngine.cpp
Normal file
398
src/layouts/QskSubcontrolLayoutEngine.cpp
Normal file
@ -0,0 +1,398 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskSubcontrolLayoutEngine.h"
|
||||
#include "QskLayoutElement.h"
|
||||
#include "QskLayoutChain.h"
|
||||
#include "QskTextRenderer.h"
|
||||
#include "QskSkinnable.h"
|
||||
#include "QskMargins.h"
|
||||
#include "QskTextOptions.h"
|
||||
|
||||
#include <qfont.h>
|
||||
#include <qfontmetrics.h>
|
||||
#include <qmath.h>
|
||||
|
||||
QskSubcontrolLayoutEngine::LayoutElement::LayoutElement(
|
||||
const QskSkinnable* skinnable, const QskAspect::Subcontrol subControl )
|
||||
: m_skinnable( skinnable )
|
||||
, m_subControl( subControl )
|
||||
{
|
||||
}
|
||||
|
||||
Qt::Alignment QskSubcontrolLayoutEngine::LayoutElement::alignment() const
|
||||
{
|
||||
return m_skinnable->alignmentHint( m_subControl );
|
||||
}
|
||||
|
||||
void QskSubcontrolLayoutEngine::LayoutElement::setMaximumSize( const QSizeF& size )
|
||||
{
|
||||
setExplicitSizeHint( Qt::MaximumSize, size );
|
||||
}
|
||||
|
||||
void QskSubcontrolLayoutEngine::LayoutElement::setMinimumSize( const QSizeF& size )
|
||||
{
|
||||
setExplicitSizeHint( Qt::MinimumSize, size );
|
||||
}
|
||||
|
||||
void QskSubcontrolLayoutEngine::LayoutElement::setPreferredSize( const QSizeF& size )
|
||||
{
|
||||
setExplicitSizeHint( Qt::PreferredSize, size );
|
||||
}
|
||||
|
||||
void QskSubcontrolLayoutEngine::LayoutElement::setFixedSize( const QSizeF& size )
|
||||
{
|
||||
setSizePolicy( QskSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ) );
|
||||
|
||||
const auto newSize = size.expandedTo( QSizeF( 0, 0 ) );
|
||||
setExplicitSizeHint( Qt::PreferredSize, newSize );
|
||||
}
|
||||
|
||||
void QskSubcontrolLayoutEngine::LayoutElement::setExplicitSizeHint(
|
||||
Qt::SizeHint which, const QSizeF& size )
|
||||
{
|
||||
if ( which >= Qt::MinimumSize && which <= Qt::MaximumSize )
|
||||
{
|
||||
const QSizeF newSize( ( size.width() < 0 ) ? -1.0 : size.width(),
|
||||
( size.height() < 0 ) ? -1.0 : size.height() );
|
||||
|
||||
m_explicitSizeHints[ which ] = size;
|
||||
}
|
||||
}
|
||||
|
||||
QSizeF QskSubcontrolLayoutEngine::LayoutElement::sizeHint(
|
||||
Qt::SizeHint which, const QSizeF& constraint ) const
|
||||
{
|
||||
if ( which < Qt::MinimumSize || which > Qt::MaximumSize )
|
||||
return QSizeF( 0, 0 );
|
||||
|
||||
if ( constraint.isValid() )
|
||||
return constraint;
|
||||
|
||||
const bool isConstrained =
|
||||
constraint.width() >= 0 || constraint.height() >= 0;
|
||||
|
||||
QSizeF hint;
|
||||
|
||||
if ( !isConstrained )
|
||||
{
|
||||
// explicit size hints are never constrained
|
||||
hint = m_explicitSizeHints[ which ];
|
||||
}
|
||||
|
||||
if ( which == Qt::PreferredSize )
|
||||
{
|
||||
if ( isConstrained )
|
||||
{
|
||||
const QskMargins padding = m_skinnable->paddingHint( m_subControl );
|
||||
|
||||
auto innerConstraint = constraint;
|
||||
|
||||
if ( constraint.width() > 0 )
|
||||
{
|
||||
const auto w = constraint.width() - padding.width();
|
||||
if ( w <= 0 )
|
||||
return QSizeF();
|
||||
|
||||
innerConstraint.setWidth( w );
|
||||
}
|
||||
else if ( constraint.height() > 0 )
|
||||
{
|
||||
const auto h = constraint.height() - padding.height();
|
||||
if ( h <= 0 )
|
||||
return QSizeF();
|
||||
|
||||
innerConstraint.setHeight( h );
|
||||
}
|
||||
|
||||
hint = implicitSize( innerConstraint );
|
||||
|
||||
if ( hint.width() >= 0 )
|
||||
hint.setWidth( hint.width() + padding.width() );
|
||||
|
||||
if ( hint.height() >= 0 )
|
||||
hint.setHeight( hint.height() + padding.height() );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !hint.isValid() )
|
||||
{
|
||||
const auto sz = implicitSize( constraint );
|
||||
|
||||
if ( hint.width() < 0 )
|
||||
hint.setWidth( sz.width() );
|
||||
|
||||
if ( hint.height() < 0 )
|
||||
hint.setHeight( sz.height() );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( isConstrained )
|
||||
hint = QSizeF(); // not implemented
|
||||
}
|
||||
|
||||
return hint;
|
||||
}
|
||||
|
||||
QSizeF QskSubcontrolLayoutEngine::TextElement::implicitSize( const QSizeF& constraint ) const
|
||||
{
|
||||
const auto font = skinnable()->effectiveFont( subControl() );
|
||||
const auto textOptions = skinnable()->textOptionsHint( subControl() );
|
||||
|
||||
QSizeF hint;
|
||||
|
||||
const qreal lineHeight = QFontMetricsF( font ).height();
|
||||
|
||||
if ( m_text.isEmpty() )
|
||||
{
|
||||
if ( constraint.height() < 0.0 )
|
||||
hint.setHeight( qCeil( lineHeight ) );
|
||||
}
|
||||
else if ( constraint.width() >= 0.0 )
|
||||
{
|
||||
if ( textOptions.effectiveElideMode() != Qt::ElideNone )
|
||||
{
|
||||
hint.setHeight( qCeil( lineHeight ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
In case of QskTextOptions::NoWrap we could count
|
||||
the line numbers and calculate the height from
|
||||
lineHeight. TODO ...
|
||||
*/
|
||||
|
||||
qreal maxHeight = std::numeric_limits< qreal >::max();
|
||||
if ( maxHeight / lineHeight > textOptions.maximumLineCount() )
|
||||
{
|
||||
// be careful with overflows
|
||||
maxHeight = textOptions.maximumLineCount() * lineHeight;
|
||||
}
|
||||
|
||||
QSizeF size( constraint.width(), maxHeight );
|
||||
size = QskTextRenderer::textSize( m_text, font, textOptions, size );
|
||||
|
||||
hint.setHeight( qCeil( size.height() ) );
|
||||
}
|
||||
}
|
||||
else if ( constraint.height() >= 0.0 )
|
||||
{
|
||||
const qreal maxWidth = std::numeric_limits< qreal >::max();
|
||||
|
||||
QSizeF size( maxWidth, constraint.height() );
|
||||
size = QskTextRenderer::textSize( m_text, font, textOptions, size );
|
||||
|
||||
hint.setWidth( qCeil( size.width() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
hint = QskTextRenderer::textSize( m_text, font, textOptions );
|
||||
}
|
||||
|
||||
return hint;
|
||||
}
|
||||
|
||||
QSizeF QskSubcontrolLayoutEngine::GraphicElement::effectiveStrutSize() const
|
||||
{
|
||||
auto size = skinnable()->strutSizeHint( subControl() );
|
||||
|
||||
if ( size.isEmpty() )
|
||||
{
|
||||
const qreal aspectRatio = m_sourceSize.width() / m_sourceSize.height();
|
||||
|
||||
if ( size.width() > 0 )
|
||||
{
|
||||
size.setHeight( size.width() / aspectRatio );
|
||||
}
|
||||
else if ( size.height() > 0 )
|
||||
{
|
||||
size.setWidth( size.height() * aspectRatio );
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
QSizeF QskSubcontrolLayoutEngine::GraphicElement::implicitSize( const QSizeF& constraint ) const
|
||||
{
|
||||
auto hint = m_sourceSize;
|
||||
|
||||
if ( !hint.isEmpty() )
|
||||
{
|
||||
const qreal aspectRatio = hint.width() / hint.height();
|
||||
|
||||
if ( constraint.width() >= 0.0 )
|
||||
{
|
||||
hint.setHeight( constraint.width() / aspectRatio );
|
||||
hint.setWidth( -1.0 );
|
||||
}
|
||||
else if ( constraint.height() > 0.0 )
|
||||
{
|
||||
hint.setWidth( constraint.height() * aspectRatio );
|
||||
hint.setHeight( -1.0 );
|
||||
}
|
||||
}
|
||||
|
||||
return hint;
|
||||
}
|
||||
|
||||
|
||||
static QskLayoutChain::CellData qskCell( const QskSubcontrolLayoutEngine::LayoutElement* element,
|
||||
Qt::Orientation orientation, bool isLayoutOrientation, qreal constraint )
|
||||
{
|
||||
QskLayoutChain::CellData cell;
|
||||
cell.isValid = true;
|
||||
|
||||
const auto policy = element->sizePolicy().policy( orientation );
|
||||
|
||||
if ( isLayoutOrientation )
|
||||
{
|
||||
const auto stretch = element->stretch();
|
||||
|
||||
if ( stretch < 0 )
|
||||
cell.stretch = ( policy & QskSizePolicy::ExpandFlag ) ? 1 : 0;
|
||||
else
|
||||
cell.stretch = stretch;
|
||||
}
|
||||
|
||||
cell.canGrow = policy & QskSizePolicy::GrowFlag;
|
||||
cell.metrics = element->metrics( orientation, constraint );
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
class QskSubcontrolLayoutEngine::PrivateData
|
||||
{
|
||||
public:
|
||||
PrivateData( Qt::Orientation orientation )
|
||||
: orientation( orientation )
|
||||
{
|
||||
}
|
||||
|
||||
Qt::Orientation orientation;
|
||||
LayoutElement* elements[2] = {};
|
||||
};
|
||||
|
||||
QskSubcontrolLayoutEngine::QskSubcontrolLayoutEngine( Qt::Orientation orientation )
|
||||
: m_data( new PrivateData( orientation ) )
|
||||
{
|
||||
setExtraSpacingAt( Qt::TopEdge | Qt::BottomEdge | Qt::LeftEdge | Qt::RightEdge );
|
||||
}
|
||||
|
||||
QskSubcontrolLayoutEngine::~QskSubcontrolLayoutEngine()
|
||||
{
|
||||
for ( auto element : m_data->elements )
|
||||
delete element;
|
||||
}
|
||||
|
||||
bool QskSubcontrolLayoutEngine::setOrientation( Qt::Orientation orientation )
|
||||
{
|
||||
if ( m_data->orientation != orientation )
|
||||
{
|
||||
m_data->orientation = orientation;
|
||||
invalidate( LayoutCache );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::Orientation QskSubcontrolLayoutEngine::orientation() const
|
||||
{
|
||||
return m_data->orientation;
|
||||
}
|
||||
|
||||
void QskSubcontrolLayoutEngine::setElementAt( int index, LayoutElement* element )
|
||||
{
|
||||
if ( index >= 0 && index < count() )
|
||||
{
|
||||
delete m_data->elements[ index ];
|
||||
m_data->elements[ index ] = element;
|
||||
}
|
||||
}
|
||||
|
||||
QskSubcontrolLayoutEngine::LayoutElement* QskSubcontrolLayoutEngine::elementAt( int index ) const
|
||||
{
|
||||
if ( index >= 0 && index < count() )
|
||||
return m_data->elements[ index ];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QskSizePolicy QskSubcontrolLayoutEngine::sizePolicyAt( int index ) const
|
||||
{
|
||||
if ( index >= 0 && index < count() )
|
||||
{
|
||||
if ( auto element = m_data->elements[ index ] )
|
||||
return element->sizePolicy();
|
||||
}
|
||||
|
||||
return QskSizePolicy();
|
||||
}
|
||||
|
||||
int QskSubcontrolLayoutEngine::count() const
|
||||
{
|
||||
return sizeof( m_data->elements ) / sizeof( m_data->elements[0] );
|
||||
}
|
||||
|
||||
void QskSubcontrolLayoutEngine::layoutItems()
|
||||
{
|
||||
int row = 0;
|
||||
int col = 0;
|
||||
|
||||
for ( auto element : m_data->elements )
|
||||
{
|
||||
if ( element && !element->isIgnored() )
|
||||
{
|
||||
const auto rect = geometryAt( element, QRect( col, row, 1, 1 ) );
|
||||
element->setGeometry( rect );
|
||||
|
||||
if ( m_data->orientation == Qt::Horizontal )
|
||||
col++;
|
||||
else
|
||||
row++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int QskSubcontrolLayoutEngine::effectiveCount( Qt::Orientation orientation ) const
|
||||
{
|
||||
return ( orientation == m_data->orientation ) ? 2 : 1;
|
||||
}
|
||||
|
||||
void QskSubcontrolLayoutEngine::invalidateElementCache()
|
||||
{
|
||||
}
|
||||
|
||||
void QskSubcontrolLayoutEngine::setupChain( Qt::Orientation orientation,
|
||||
const QskLayoutChain::Segments& constraints, QskLayoutChain& chain ) const
|
||||
{
|
||||
uint index1 = 0;
|
||||
uint index2 = 0;
|
||||
|
||||
const bool isLayoutOrientation = ( orientation == m_data->orientation );
|
||||
|
||||
for ( auto element : m_data->elements )
|
||||
{
|
||||
if ( element == nullptr || element->isIgnored() )
|
||||
continue;
|
||||
|
||||
qreal constraint = -1.0;
|
||||
if ( !constraints.isEmpty() )
|
||||
constraint = constraints[index1].length;
|
||||
|
||||
const auto cell = qskCell( element, orientation, isLayoutOrientation, constraint );
|
||||
chain.expandCell( index2, cell );
|
||||
|
||||
if ( isLayoutOrientation )
|
||||
index2++;
|
||||
else
|
||||
index1++;
|
||||
}
|
||||
}
|
139
src/layouts/QskSubcontrolLayoutEngine.h
Normal file
139
src/layouts/QskSubcontrolLayoutEngine.h
Normal file
@ -0,0 +1,139 @@
|
||||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
||||
* This file may be used under the terms of the QSkinny License, Version 1.0
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_SUBCONTROL_LAYOUT_ENGINE_H
|
||||
#define QSK_SUBCONTROL_LAYOUT_ENGINE_H
|
||||
|
||||
#include "QskGlobal.h"
|
||||
#include "QskLayoutEngine2D.h"
|
||||
#include "QskLayoutElement.h"
|
||||
#include "QskAspect.h"
|
||||
|
||||
#include <qnamespace.h>
|
||||
#include <memory>
|
||||
|
||||
class QskSkinnable;
|
||||
|
||||
/*
|
||||
For the moment this layout is tailored for arranging one text and one graphic
|
||||
horizontally/vertically. Candidate for becoming something more general in the future..
|
||||
*/
|
||||
class QskSubcontrolLayoutEngine : public QskLayoutEngine2D
|
||||
{
|
||||
public:
|
||||
class LayoutElement : public QskLayoutElement
|
||||
{
|
||||
public:
|
||||
LayoutElement( const QskSkinnable*, const QskAspect::Subcontrol );
|
||||
|
||||
inline const QskSkinnable* skinnable() const { return m_skinnable; }
|
||||
inline QskAspect::Subcontrol subControl() const { return m_subControl; }
|
||||
|
||||
inline void setSizePolicy(
|
||||
QskSizePolicy::Policy horizontalPolicy,
|
||||
QskSizePolicy::Policy verticalPolicy )
|
||||
{
|
||||
setSizePolicy( QskSizePolicy( horizontalPolicy, verticalPolicy ) );
|
||||
}
|
||||
|
||||
inline void setSizePolicy( QskSizePolicy policy ) { m_sizePolicy = policy; }
|
||||
inline QskSizePolicy sizePolicy() const override { return m_sizePolicy; }
|
||||
|
||||
virtual Qt::Alignment alignment() const override;
|
||||
|
||||
inline void setIgnored( bool on ) { m_ignored = on; }
|
||||
inline bool isIgnored() const { return m_ignored; }
|
||||
|
||||
inline void setStretch( int stretch ) { m_stretch = stretch; }
|
||||
inline int stretch() const { return m_stretch; }
|
||||
|
||||
inline void setGeometry( const QRectF& rect ) { m_geometry = rect; }
|
||||
inline const QRectF& geometry() const { return m_geometry; }
|
||||
|
||||
void setMaximumSize( const QSizeF& );
|
||||
void setMinimumSize( const QSizeF& );
|
||||
void setPreferredSize( const QSizeF& );
|
||||
void setFixedSize( const QSizeF& );
|
||||
|
||||
void setExplicitSizeHint( Qt::SizeHint, const QSizeF& );
|
||||
|
||||
private:
|
||||
QSizeF sizeHint( Qt::SizeHint, const QSizeF& ) const override;
|
||||
virtual QSizeF implicitSize( const QSizeF& ) const = 0;
|
||||
|
||||
int m_stretch = -1;
|
||||
bool m_ignored = false;
|
||||
|
||||
QskSizePolicy m_sizePolicy;
|
||||
|
||||
QSizeF m_explicitSizeHints[3];
|
||||
QRectF m_geometry;
|
||||
|
||||
const QskSkinnable* m_skinnable;
|
||||
const QskAspect::Subcontrol m_subControl;
|
||||
};
|
||||
|
||||
class TextElement : public LayoutElement
|
||||
{
|
||||
public:
|
||||
TextElement( const QskSkinnable* skinnable, QskAspect::Subcontrol subControl )
|
||||
: LayoutElement( skinnable, subControl )
|
||||
{
|
||||
}
|
||||
|
||||
inline void setText( const QString& text ) { m_text = text; }
|
||||
inline QString text() const { return m_text; }
|
||||
|
||||
private:
|
||||
QSizeF implicitSize( const QSizeF& ) const override;
|
||||
QString m_text;
|
||||
};
|
||||
|
||||
class GraphicElement : public LayoutElement
|
||||
{
|
||||
public:
|
||||
GraphicElement( const QskSkinnable* skinnable, QskAspect::Subcontrol subControl )
|
||||
: LayoutElement( skinnable, subControl )
|
||||
{
|
||||
}
|
||||
|
||||
inline void setSourceSize( const QSizeF& size ) { m_sourceSize = size; }
|
||||
inline QSizeF sourceSize() const { return m_sourceSize; }
|
||||
|
||||
QSizeF effectiveStrutSize() const;
|
||||
|
||||
private:
|
||||
QSizeF implicitSize( const QSizeF& ) const override;
|
||||
|
||||
QSizeF m_sourceSize;
|
||||
};
|
||||
|
||||
explicit QskSubcontrolLayoutEngine( Qt::Orientation );
|
||||
~QskSubcontrolLayoutEngine() override;
|
||||
|
||||
Qt::Orientation orientation() const;
|
||||
bool setOrientation( Qt::Orientation );
|
||||
|
||||
void setElementAt( int index, LayoutElement* );
|
||||
LayoutElement* elementAt( int ) const;
|
||||
|
||||
int count() const override final;
|
||||
|
||||
private:
|
||||
QskSizePolicy sizePolicyAt( int index ) const override;
|
||||
void layoutItems() override;
|
||||
|
||||
int effectiveCount( Qt::Orientation ) const override;
|
||||
|
||||
void invalidateElementCache() override;
|
||||
|
||||
virtual void setupChain( Qt::Orientation,
|
||||
const QskLayoutChain::Segments&, QskLayoutChain& ) const override;
|
||||
|
||||
class PrivateData;
|
||||
std::unique_ptr< PrivateData > m_data;
|
||||
};
|
||||
|
||||
#endif
|
@ -322,7 +322,8 @@ HEADERS += \
|
||||
layouts/QskLinearBox.h \
|
||||
layouts/QskLinearLayoutEngine.h \
|
||||
layouts/QskStackBoxAnimator.h \
|
||||
layouts/QskStackBox.h
|
||||
layouts/QskStackBox.h \
|
||||
layouts/QskSubcontrolLayoutEngine.h
|
||||
|
||||
SOURCES += \
|
||||
layouts/QskGridBox.cpp \
|
||||
@ -335,7 +336,8 @@ SOURCES += \
|
||||
layouts/QskLinearBox.cpp \
|
||||
layouts/QskLinearLayoutEngine.cpp \
|
||||
layouts/QskStackBoxAnimator.cpp \
|
||||
layouts/QskStackBox.cpp
|
||||
layouts/QskStackBox.cpp \
|
||||
layouts/QskSubcontrolLayoutEngine.cpp
|
||||
|
||||
HEADERS += \
|
||||
dialogs/QskDialog.h \
|
||||
|
Loading…
x
Reference in New Issue
Block a user