diff --git a/examples/gallery/button/ButtonPage.cpp b/examples/gallery/button/ButtonPage.cpp index ca88acd4..5422e345 100644 --- a/examples/gallery/button/ButtonPage.cpp +++ b/examples/gallery/button/ButtonPage.cpp @@ -166,10 +166,12 @@ namespace RadioButtonBox( QQuickItem* parent = nullptr ) : ButtonBox( Qt::Horizontal, parent ) { - new QskRadioBox( { "One", "Two", "Three" }, this ); + auto radioBox1 = new QskRadioBox( { "One", "Two", "Three" }, this ); + radioBox1->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); - auto radioBox = new QskRadioBox( { "One", "Two", "Three" }, this ); - radioBox->setLayoutMirroring( true ); + auto radioBox2 = new QskRadioBox( { "One", "Two", "Three" }, this ); + radioBox2->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); + radioBox2->setLayoutMirroring( true ); } }; } diff --git a/skins/material3/QskMaterial3Skin.cpp b/skins/material3/QskMaterial3Skin.cpp index b9516e03..ea661714 100644 --- a/skins/material3/QskMaterial3Skin.cpp +++ b/skins/material3/QskMaterial3Skin.cpp @@ -491,37 +491,36 @@ void Editor::setupRadioBox() using Q = QskRadioBox; using A = QskAspect; - setAnimation( Q::Ripple | A::Metric | A::Position, qskDuration ); - setSpacing( Q::Panel, 10_dp ); - setStrutSize( Q::Button, { 20_dp, 20_dp } ); - setStrutSize( Q::Symbol, { 10_dp, 10_dp } ); - setStrutSize( Q::Ripple, { 40_dp, 40_dp } ); + setStrutSize( Q::Button, 20_dp, 20_dp ); + setStrutSize( Q::Ripple, 40_dp, 40_dp ); - setAlignment( Q::Symbol, Qt::AlignCenter ); - setAlignment( Q::Text, Qt::AlignBottom ); + for ( auto subControl : { Q::Button, Q::Indicator, Q::Ripple } ) + setBoxShape( subControl, 100, Qt::RelativeSize ); // circular - setMargin( Q::Text, QskMargins( 10_dp, 0, 10_dp, 0 ) ); - - setBoxShape( Q::Button, 20_dp ); - setBoxShape( Q::Ripple, 40_dp ); setBoxBorderMetrics( Q::Button, 2_dp ); setBoxBorderColors( Q::Button, m_pal.onBackground ); - setColor( Q::Text, m_pal.onBackground ); - setColor( Q::Symbol, m_pal.primary ); - setColor( Q::Ripple, stateLayerColor( m_pal.onSurface, m_pal.focusOpacity ) ); + setPadding( Q::Button, 5_dp ); - // Selected + setColor( Q::Text, m_pal.onBackground ); + setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); + + setColor( Q::Ripple, stateLayerColor( m_pal.onSurface, m_pal.focusOpacity ) ); setColor( Q::Ripple | Q::Selected, stateLayerColor( m_pal.primary, m_pal.focusOpacity ) ); - setBoxBorderColors( Q::Button | Q::Selected, m_pal.primary ); - // Disabled + setColor( Q::Indicator, Qt::transparent); + setColor( Q::Indicator | Q::Selected, m_pal.primary ); + setColor( Q::Indicator | Q::Selected | Q::Disabled, m_pal.onSurface38 ); + + // Selected + + setBoxBorderColors( Q::Button | Q::Selected, m_pal.primary ); setBoxBorderColors( Q::Button | Q::Disabled, m_pal.onSurface38 ); setBoxBorderColors( Q::Button | Q::Disabled | Q::Selected, m_pal.onSurface38 ); - setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); - setColor( Q::Symbol | Q::Disabled, m_pal.onSurface38 ); + + setAnimation( Q::Ripple | A::Metric | A::Position, qskDuration ); } void Editor::setupFocusIndicator() diff --git a/skins/squiek/QskSquiekSkin.cpp b/skins/squiek/QskSquiekSkin.cpp index 23d70f42..64806af2 100644 --- a/skins/squiek/QskSquiekSkin.cpp +++ b/skins/squiek/QskSquiekSkin.cpp @@ -635,33 +635,30 @@ void Editor::setupRadioBox() { using Q = QskRadioBox; - setSpacing(Q::Panel, qskDpiScaled( 10 ) ); + setSpacing( Q::Panel, qskDpiScaled( 10 ) ); + setStrutSize( Q::Button, qskDpiScaled( 20 ), qskDpiScaled( 20 ) ); - setStrutSize( Q::Button, { qskDpiScaled( 20 ), qskDpiScaled( 20 ) } ); - setStrutSize( Q::Symbol, { qskDpiScaled( 9 ), qskDpiScaled( 9 ) }); + for ( auto subControl : { Q::Button, Q::Indicator, Q::Ripple } ) + setBoxShape( subControl, 100, Qt::RelativeSize ); // circular - setBoxShape( Q::Button, qskDpiScaled( 20 ) ); - setBoxShape( Q::Ripple, qskDpiScaled( 40 ) ); setBoxBorderMetrics( Q::Button, qskDpiScaled( 1 ) ); setBoxBorderColors( Q::Button, m_pal.darker125 ); setBoxBorderColors( Q::Button | Q::Disabled, m_pal.theme ); + setPadding( Q::Button, qskDpiScaled( 5 ) ); + setColor( Q::Text, m_pal.themeForeground ); - setColor( Q::Symbol, m_pal.themeForeground ); + setColor( Q::Text | Q::Disabled, m_pal.darker200 ); + setColor( Q::Panel, m_pal.lighter125 ); setColor( Q::Panel | Q::Disabled, m_pal.lighter125 ); setColor( Q::Button | Q::Disabled, m_pal.lighter110 ); - setColor( Q::Text | Q::Disabled, m_pal.darker200 ); - - setColor( Q::Symbol | Q::Disabled, m_pal.darker200 ); - - setMargin( Q::Text, QskMargins( qskDpiScaled( 10 ), 0, qskDpiScaled( 10 ), 0 ) ); - - setAlignment( Q::Symbol, Qt::AlignCenter ); - setAlignment( Q::Text, Qt::AlignBottom ); + setColor( Q::Indicator, Qt::transparent); + setColor( Q::Indicator | Q::Selected, m_pal.themeForeground ); + setColor( Q::Indicator | Q::Selected | Q::Disabled, m_pal.darker200 ); } void Editor::setupDialogButtonBox() diff --git a/src/controls/QskRadioBox.cpp b/src/controls/QskRadioBox.cpp index 9cd6b356..7eb3790d 100644 --- a/src/controls/QskRadioBox.cpp +++ b/src/controls/QskRadioBox.cpp @@ -8,20 +8,47 @@ #include "QskAnimationHint.h" #include "QskSkinlet.h" +#if 1 +/* + Maybe we should have the following subControls: + + - Text + - IndicatorPanel + - Indicator + - Button: IndicatorPanel + Text ( a complete line ) + - Ripple + - Panel + + Then we can define spacings/margins for Button and + optional borders/colors - even if radio buttons usually don't have this + + Then selection can then be done by: + + effectiveSkinlet()->sampleIndexAt( this, + contentsRect(), QskRadioBox::Button, pos ); + + The focusRectangle can be found by: + + effectiveSkinlet()->sampleRect( this, + contentsRect(), QskRadioBox::Button, index ); + + TODO ... + */ +#endif + QSK_SUBCONTROL( QskRadioBox, Panel ) QSK_SUBCONTROL( QskRadioBox, Button ) -QSK_SUBCONTROL( QskRadioBox, Symbol ) +QSK_SUBCONTROL( QskRadioBox, Indicator ) QSK_SUBCONTROL( QskRadioBox, Text ) QSK_SUBCONTROL( QskRadioBox, Ripple ) QSK_STATE( QskRadioBox, Selected, QskAspect::FirstUserState << 1 ) QSK_STATE( QskRadioBox, Pressed, QskAspect::FirstUserState << 2 ) -QSK_STATE( QskRadioBox, Focused, QskAspect::FirstUserState << 3 ) class QskRadioBox::PrivateData { public: - QStringList items; + QStringList options; int selectedIndex = -1; int focusedIndex = -1; @@ -32,21 +59,18 @@ QskRadioBox::QskRadioBox( QQuickItem* parent ) : Inherited( parent ) , m_data( new PrivateData() ) { - setFocusPolicy( Qt::NoFocus ); - setAcceptedMouseButtons( Qt::LeftButton ); + initSizePolicy( QskSizePolicy::Minimum, QskSizePolicy::Minimum ); - connect(this, &QskRadioBox::itemsChanged, this, - [this]( const QStringList& items ) - { setFocusPolicy( items.count() > 0 ? Qt::StrongFocus : Qt::NoFocus ); } - ); + setFocusPolicy( Qt::StrongFocus ); + setAcceptedMouseButtons( Qt::LeftButton ); setFocusedIndex( -1 ); } -QskRadioBox::QskRadioBox( const QStringList& list, QQuickItem* parent ) +QskRadioBox::QskRadioBox( const QStringList& options, QQuickItem* parent ) : QskRadioBox( parent ) { - setItems( list ); + setOptions( options ); } QskRadioBox::QskRadioBox( const QStringList& items, @@ -63,42 +87,29 @@ QskRadioBox::~QskRadioBox() QRectF QskRadioBox::focusIndicatorRect() const { - if( m_data->focusedIndex > -1) + if ( m_data->focusedIndex < 0 ) + return QRectF(); + + const auto rect = contentsRect(); + + auto skinlet = effectiveSkinlet(); + + auto buttonRect = skinlet->sampleRect( this, + rect, QskRadioBox::Button, m_data->focusedIndex ); + + auto y = buttonRect.y(); + auto h = buttonRect.height(); + + auto textRect = skinlet->sampleRect( this, + rect, QskRadioBox::Text, m_data->focusedIndex ); + + if( textRect.height() > 0.0 ) { - auto skinlet = effectiveSkinlet(); - - auto textRect = skinlet->sampleRect( this, - contentsRect(), QskRadioBox::Text, m_data->focusedIndex ); - - auto buttonRect = skinlet->sampleRect( this, - contentsRect(), QskRadioBox::Button, m_data->focusedIndex ); - - if( textRect == QRectF() ) - return buttonRect; - - auto result = QRectF( - qMin( textRect.x(), buttonRect.x() ), - qMin( textRect.y(), buttonRect.y() ), - buttonRect.width() + textRect.width(), - qMax( buttonRect.height(), textRect.height() ) ); - - if( layoutMirroring() ) - { - result.setWidth( - result.width() + marginHint( Text ).right() - + marginHint( Button ).left() ); - } - else - { - result.setWidth( - result.width() + marginHint( Text ).left() - + marginHint( Button ).right() ); - } - - return result; + y = std::min( y, textRect.y() ); + h = std::max( h, textRect.height() ); } - return QRectF(); + return QRectF( rect.x(), y, rect.width(), h ); } int QskRadioBox::selectedIndex() const @@ -106,9 +117,14 @@ int QskRadioBox::selectedIndex() const return m_data->selectedIndex; } -const QStringList& QskRadioBox::items() const +QStringList QskRadioBox::options() const { - return m_data->items; + return m_data->options; +} + +QString QskRadioBox::option( int index ) const +{ + return m_data->options.value( index ); } int QskRadioBox::pressedIndex() const @@ -118,7 +134,7 @@ int QskRadioBox::pressedIndex() const void QskRadioBox::setSelectedIndex( int index ) { - if( index == m_data->selectedIndex || index >= m_data->items.count() ) + if( index == m_data->selectedIndex || index >= m_data->options.count() ) return; if( index < 0 ) @@ -126,20 +142,20 @@ void QskRadioBox::setSelectedIndex( int index ) else m_data->selectedIndex = index; - selectedIndexChanged( m_data->selectedIndex ); + Q_EMIT selectedIndexChanged( m_data->selectedIndex ); } -void QskRadioBox::setItems( const QStringList& items ) +void QskRadioBox::setOptions( const QStringList& options ) { - if( m_data->items == items ) + if( m_data->options == options ) return; - m_data->items = items; + m_data->options = options; - itemsChanged( items ); + Q_EMIT optionsChanged( options ); setSelectedIndex( m_data->selectedIndex ); - if( m_data->focusedIndex > items.size() ) + if( m_data->focusedIndex > options.size() ) setFocusedIndex( 0 ); } @@ -160,7 +176,8 @@ void QskRadioBox::keyPressEvent( QKeyEvent* event ) case Qt::Key_Right: { m_data->selectedIndex = qMin( m_data->selectedIndex + 1, - items().size() - 1 ); + m_data->options.size() - 1 ); + setFocusedIndex( m_data->selectedIndex ); update(); @@ -180,7 +197,7 @@ void QskRadioBox::keyPressEvent( QKeyEvent* event ) const auto currentTabIndex = m_data->focusedIndex; const auto nextTabIndex = currentTabIndex + qskFocusChainIncrement( event ); - if( nextTabIndex >= items().size() || nextTabIndex < 0 ) + if( nextTabIndex >= m_data->options.size() || nextTabIndex < 0 ) { Inherited::keyPressEvent( event ); setFocusedIndex( -1 ); @@ -234,7 +251,7 @@ void QskRadioBox::focusInEvent( QFocusEvent* event ) } else if( event->reason() == Qt::BacktabFocusReason ) { - setFocusedIndex( items().size() - 1 ); + setFocusedIndex( m_data->options.size() - 1 ); } update(); @@ -249,22 +266,27 @@ void QskRadioBox::focusOutEvent( QFocusEvent* event ) Inherited::focusOutEvent( event ); } -int QskRadioBox::indexAt( const QPointF& target ) const +int QskRadioBox::indexAt( const QPointF& pos ) const { - const auto itemHeight = contentsRect().height() / items().size(); - auto index = target.y() / itemHeight; + const auto skinlet = effectiveSkinlet(); + const auto cr = contentsRect(); - if( index < 0 || index >= items().size() ) - return -1; + for ( int i = 0; i < m_data->options.size(); i++ ) + { + const auto r = skinlet->sampleRect( this, cr, QskRadioBox::Button, i ); + if ( r.top() <= pos.y() && r.bottom() >= pos.y() ) + return i; + } - return index; + return -1; } void QskRadioBox::setFocusedIndex( int index ) { m_data->focusedIndex = index; setPositionHint( Ripple, index ); - focusIndicatorRectChanged(); + + Q_EMIT focusIndicatorRectChanged(); } #include "moc_QskRadioBox.cpp" diff --git a/src/controls/QskRadioBox.h b/src/controls/QskRadioBox.h index c510bb96..a3eef668 100644 --- a/src/controls/QskRadioBox.h +++ b/src/controls/QskRadioBox.h @@ -16,14 +16,14 @@ class QSK_EXPORT QskRadioBox : public QskControl Q_PROPERTY( int selectedIndex READ selectedIndex WRITE setSelectedIndex NOTIFY selectedIndexChanged FINAL ) - Q_PROPERTY( QStringList items READ items - WRITE setItems NOTIFY itemsChanged FINAL ) + Q_PROPERTY( QStringList options READ options + WRITE setOptions NOTIFY optionsChanged FINAL ) using Inherited = QskControl; public: - QSK_SUBCONTROLS( Panel, Button, Symbol, Text, Ripple ) - QSK_STATES( Selected, Pressed, Focused ) + QSK_SUBCONTROLS( Panel, Button, Indicator, Text, Ripple ) + QSK_STATES( Selected, Pressed ) QskRadioBox( QQuickItem* parent = nullptr ); QskRadioBox( const QStringList&, QQuickItem* parent = nullptr ); @@ -33,18 +33,19 @@ class QSK_EXPORT QskRadioBox : public QskControl QRectF focusIndicatorRect() const override; - const QStringList& items() const; + QStringList options() const; + QString option( int ) const; int selectedIndex() const; int pressedIndex() const; public Q_SLOTS: void setSelectedIndex( int ); - void setItems( const QStringList& ); + void setOptions( const QStringList& ); Q_SIGNALS: void selectedIndexChanged( int ); - void itemsChanged( const QStringList& ); + void optionsChanged( const QStringList& ); protected: void keyPressEvent( QKeyEvent* ) override; diff --git a/src/controls/QskRadioBoxSkinlet.cpp b/src/controls/QskRadioBoxSkinlet.cpp index 13cff7e0..88c34794 100644 --- a/src/controls/QskRadioBoxSkinlet.cpp +++ b/src/controls/QskRadioBoxSkinlet.cpp @@ -5,15 +5,9 @@ #include "QskRadioBoxSkinlet.h" -#include "QskAspect.h" #include "QskRadioBox.h" - #include "QskSkinStateChanger.h" -#include "QskStandardSymbol.h" -#include "QskColorFilter.h" -#include "QskGraphic.h" #include "QskFunctions.h" -#include "QskSkin.h" #include @@ -51,11 +45,48 @@ namespace return qMax( strutHight, fontHeight ); } + + QRectF lineRect( const QskRadioBox* radioBox, const QRectF& rect, int index ) + { + using Q = QskRadioBox; + + auto h = lineHeight( radioBox ); + + auto y = rect.top(); + if ( index > 0 ) + { + const auto spacing = radioBox->spacingHint( Q::Panel ); + y += index * ( h + spacing ); + } + + return QRectF( rect.x(), y, rect.width(), h ); + } + + inline qreal maxTextWidth( const QskRadioBox* radioBox ) + { + qreal w = 0.0; + + const QFontMetrics fm( radioBox->effectiveFont( QskRadioBox::Text ) ); + + const auto options = radioBox->options(); + for( const auto& option : options ) + w = std::max( w, qskHorizontalAdvance( fm, option ) ); + + return w; + } + +#if 1 + inline qreal lineSpacing( const QskRadioBox* ) + { + // skinHint TODO ... + return 10; + } +#endif } QskRadioBoxSkinlet::QskRadioBoxSkinlet( QskSkin* ) { - setNodeRoles( { PanelRole, ButtonRole, SymbolRole, TextRole, RippleRole } ); + setNodeRoles( { PanelRole, ButtonRole, IndicatorRole, TextRole, RippleRole } ); } QskRadioBoxSkinlet::~QskRadioBoxSkinlet() @@ -64,41 +95,15 @@ QskRadioBoxSkinlet::~QskRadioBoxSkinlet() QRectF QskRadioBoxSkinlet::subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const -{ - auto radioBox = static_cast< const QskRadioBox* >( skinnable ); - - if( subcontrol == QskRadioBox::Ripple ) - return rippleRect( radioBox, contentsRect ); - - return contentsRect; -} - -QSizeF QskRadioBoxSkinlet::sizeHint( const QskSkinnable* skinnable, - Qt::SizeHint, const QSizeF& ) const { using Q = QskRadioBox; auto radioBox = static_cast< const QskRadioBox* >( skinnable ); - const auto font = skinnable->effectiveFont( Q::Text ); - const auto textMargins = skinnable->marginHint( Q::Text ); - const auto buttonMargins = skinnable->marginHint( Q::Button ); - const auto symbolMargins = skinnable->marginHint( Q::Symbol ); + if( subcontrol == Q::Ripple ) + return rippleRect( radioBox, contentsRect ); - qreal maxTextWidth = 0; - for( const auto& item : radioBox->items() ) - maxTextWidth = std::max( maxTextWidth, qskHorizontalAdvance( font, item ) ); - - auto buttonWidth = radioBox->strutSizeHint( Q::Button ).width(); - auto symbolWidth = radioBox->strutSizeHint( Q::Symbol ).width(); - - maxTextWidth += textMargins.left() + textMargins.right(); - buttonWidth += buttonMargins.left() + buttonMargins.right(); - symbolWidth += symbolMargins.left() + symbolMargins.right(); - - auto spacing = radioBox->spacingHint( Q::Panel ); - return QSizeF( maxTextWidth + qMax( buttonWidth, symbolWidth ), - ( lineHeight( radioBox ) + spacing ) * radioBox->items().size() - spacing ); + return contentsRect; } QSGNode* QskRadioBoxSkinlet::updateSubNode( const QskSkinnable* skinnable, @@ -114,8 +119,8 @@ QSGNode* QskRadioBoxSkinlet::updateSubNode( const QskSkinnable* skinnable, case ButtonRole: return updateSeriesNode( skinnable, Q::Button, node ); - case SymbolRole: - return updateSeriesNode( skinnable, Q::Symbol, node ); + case IndicatorRole: + return updateSeriesNode( skinnable, Q::Indicator, node ); case TextRole: return updateSeriesNode( skinnable, Q::Text, node ); @@ -124,9 +129,11 @@ QSGNode* QskRadioBoxSkinlet::updateSubNode( const QskSkinnable* skinnable, { auto radioBox = static_cast< const QskRadioBox* >( skinnable ); +#if 1 QskSkinStateChanger changer( radioBox ); auto ripplePosition = radioBox->positionHint( Q::Ripple ); changer.setStates( statesForIndex( radioBox, ripplePosition ) ); +#endif return updateBoxNode( radioBox, node, Q::Ripple ); } @@ -139,21 +146,7 @@ int QskRadioBoxSkinlet::sampleCount( const QskSkinnable* skinnable, QskAspect::Subcontrol ) const { const auto radioBox = static_cast< const QskRadioBox* >( skinnable ); - return radioBox->items().count(); -} - -QSizeF QskRadioBoxSkinlet::buttonSymbolSize( const QskRadioBox* radioBox ) const -{ - using Q = QskRadioBox; - - auto buttonStrut = radioBox->strutSizeHint( Q::Button ); - auto symbolStrut = radioBox->strutSizeHint( Q::Symbol ); - - buttonStrut = buttonStrut.grownBy( radioBox->marginHint( Q::Button ) ); - symbolStrut = symbolStrut.grownBy( radioBox->marginHint( Q::Symbol ) ); - - return QSizeF( qMax( buttonStrut.width(), symbolStrut.width() ), - qMax( buttonStrut.height(), symbolStrut.height() ) ); + return radioBox->options().count(); } QRectF QskRadioBoxSkinlet::rippleRect( @@ -161,64 +154,39 @@ QRectF QskRadioBoxSkinlet::rippleRect( { using Q = QskRadioBox; - auto ripplePosition = radioBox->positionHint( Q::Ripple ); - - if( ripplePosition < 0 ) + auto index = radioBox->positionHint( Q::Ripple ); + if( index < 0 ) return QRectF(); - auto rippleSize = radioBox->strutSizeHint( Q::Ripple ); + QRectF r; + r.setSize( radioBox->strutSizeHint( Q::Ripple ) ); - auto r = buttonRect( radioBox, Q::Button, rect, ripplePosition ); - - r.moveLeft( r.x() - ( rippleSize.width() - r.width() ) / 2 ); - r.moveTop( r.y() - ( rippleSize.height() - r.height() ) / 2 ); - r.setSize( rippleSize ); + if ( !r.isEmpty() ) + { + const auto buttonRect = sampleRect( radioBox, rect, Q::Button, index ); + r.moveCenter( buttonRect.center() ); + } return r; } QRectF QskRadioBoxSkinlet::buttonRect( const QskRadioBox* radioBox, - const QskAspect::Subcontrol target, const QRectF& rect, double index ) const + const QRectF& rect, int index ) const { using Q = QskRadioBox; - if( index < 0 ) - return QRectF(); + auto r = lineRect( radioBox, rect, index ); + r = r.marginsRemoved( radioBox->paddingHint( Q::Panel ) ); - auto result = rect; - result.setSize( radioBox->strutSizeHint( target ) ); + auto alignment = radioBox->alignmentHint( Q::Button, Qt::AlignCenter ); - auto spacing = radioBox->spacingHint( Q::Panel ); - result.moveTop( ( lineHeight( radioBox ) + spacing ) * index ); + alignment &= Qt::AlignVertical_Mask; + alignment |= radioBox->layoutMirroring() ? Qt::AlignRight : Qt::AlignLeft; - auto margins = radioBox->marginHint( target ); - auto withMargins = result.size().grownBy( margins ); + auto size = radioBox->strutSizeHint( Q::Button ); + size = size.grownBy( radioBox->marginHint( Q::Button ) ); - auto maxSize = buttonSymbolSize( radioBox ); - auto alignment = radioBox->alignmentHint( target ); - - // Vertical positioning - const auto alignHeight = maxSize.height() - withMargins.height(); - if( alignment.testFlag( Qt::AlignVCenter ) ) - result.moveTop( result.top() + alignHeight / 2 ); - else if( alignment.testFlag( Qt::AlignBottom ) ) - result.moveTop( result.top() + alignHeight ); - - result.moveTop( result.top() + margins.top() ); - - // Horizontal positioning - auto alignWidth = 0; - if( alignment.testFlag( Qt::AlignHCenter ) ) - alignWidth = ( maxSize.width() - withMargins.width() ) / 2; - else if ( alignment.testFlag( Qt::AlignRight ) ) - alignWidth = maxSize.width() - withMargins.width(); - - if( radioBox->layoutMirroring() ) - result.moveRight( rect.width() - ( alignWidth + margins.right() ) ); - else - result.moveLeft( margins.left() + alignWidth ); - - return result; + return qskAlignedRectF( r, size.width(), size.height(), alignment ); } QRectF QskRadioBoxSkinlet::textRect( const QskRadioBox* radioBox, @@ -226,50 +194,42 @@ QRectF QskRadioBoxSkinlet::textRect( const QskRadioBox* radioBox, { using Q = QskRadioBox; - const auto text = radioBox->items()[ index ]; - if( text.isEmpty() ) - return QRectF(); + auto r = lineRect( radioBox, rect, index ); + r = r.marginsRemoved( radioBox->paddingHint( Q::Panel ) ); - QRectF result = rect; - auto spacing = radioBox->spacingHint( Q::Panel ); - auto lh = lineHeight( radioBox ); - const auto textMargins = radioBox->marginHint( Q::Text ); - const auto font = radioBox->effectiveFont( Q::Text ); + const auto buttonRect = sampleRect( radioBox, rect, Q::Button, index ); + const auto spacing = lineSpacing( radioBox ); - result.moveTop( index * ( lh + spacing ) - + lh - QFontMetricsF( font ).height() + textMargins.top() ); - - result.setHeight( lh ); - result.setWidth( qskHorizontalAdvance( font, text ) ); - - const auto button = buttonRect( radioBox, Q::Button, rect, index ); - const auto buttonsMargins = radioBox->marginHint( Q::Button ); - const auto buttonWidth = button.marginsAdded( buttonsMargins ).width(); - - if( radioBox->layoutMirroring() ) - { - result.moveLeft( rect.width() - textMargins.right() - - result.width() - buttonWidth); - } + if ( !radioBox->layoutMirroring() ) + r.setLeft( buttonRect.right() + spacing ); else - { - result.moveLeft( buttonWidth + textMargins.left() ); - } + r.setRight( buttonRect.left() - spacing ); - return result; + return r; } QRectF QskRadioBoxSkinlet::sampleRect( const QskSkinnable* skinnable, - const QRectF& rect, QskAspect::Subcontrol subcontrol, int index ) const + const QRectF& rect, QskAspect::Subcontrol subControl, int index ) const { using Q = QskRadioBox; auto radioBox = static_cast< const QskRadioBox* >( skinnable ); - if( subcontrol == Q::Text ) + if( subControl == Q::Text ) return textRect( radioBox, rect, index ); - return buttonRect( radioBox, subcontrol, rect, index); + if( subControl == Q::Button ) + return buttonRect( radioBox, rect, index); + + if( subControl == Q::Indicator ) + { + auto r = sampleRect( radioBox, rect, Q::Button, index ); + r = r.marginsRemoved( radioBox->paddingHint( Q::Button ) ); + + return r; + } + + return QRectF(); } QskAspect::States QskRadioBoxSkinlet::sampleStates( const QskSkinnable* skinnable, @@ -292,42 +252,57 @@ QSGNode* QskRadioBoxSkinlet::updateSampleNode( const QskSkinnable* skinnable, if( subcontrol == Q::Text ) { - return QskSkinlet::updateTextNode( radioBox, node, rect, Qt::AlignLeft, - radioBox->items()[ index ], subcontrol ); + Qt::Alignment alignment = Qt::AlignVCenter; + alignment |= ( radioBox->layoutMirroring() ? Qt::AlignRight : Qt::AlignLeft ); + + alignment = radioBox->alignmentHint( Q::Text, Qt::AlignCenter ); + alignment &= Qt::AlignVertical_Mask; + alignment |= radioBox->layoutMirroring() ? Qt::AlignRight : Qt::AlignLeft; + + return updateTextNode( radioBox, node, rect, + alignment, radioBox->option( index ), subcontrol ); } - else if ( subcontrol == Q::Button ) - { - return QskSkinlet::updateBoxNode( radioBox, node, rect, subcontrol ); - } - else if( subcontrol == Q::Symbol ) - { - auto symbol = QskStandardSymbol::NoSymbol; - auto color = radioBox->color( subcontrol ).rgb(); - if( radioBox->selectedIndex() == index ) - { - symbol = QskStandardSymbol::Bullet; - color = radioBox->color( subcontrol | Q::Selected ).rgb(); - } - - auto graphic = radioBox->effectiveSkin()->symbol( symbol ); - - /* - Our default skins do not have the concept of colorRoles - implemented. Until then we do the recoloring manually here - */ - QskColorFilter filter; - filter.addColorSubstitution( Qt::black, color ); - - auto colorSub = radioBox->color( subcontrol | statesForIndex( radioBox, index ) ); - filter.addColorSubstitution( Qt::black, colorSub.rgb() ); - - QskGraphic::fromGraphic( graphic, filter ); - - return updateGraphicNode( radioBox, node, graphic, filter, rect ); - } + if ( subcontrol == Q::Button || subcontrol == Q::Indicator ) + return updateBoxNode( radioBox, node, rect, subcontrol ); return node; } +QSizeF QskRadioBoxSkinlet::sizeHint( const QskSkinnable* skinnable, + Qt::SizeHint which, const QSizeF& constraint ) const +{ + using Q = QskRadioBox; + + const auto radioBox = static_cast< const QskRadioBox* >( skinnable ); + + if ( which != Qt::PreferredSize ) + return QSizeF(); + + if ( constraint.width() >= 0.0 ) + { + // heightForWidth would make sense when word wrapping is enabled TODO ... + } + + QSizeF textSize( maxTextWidth( radioBox ), 0.0 ); + + textSize = textSize.expandedTo( skinnable->strutSizeHint( Q::Text ) ); + textSize = textSize.grownBy( skinnable->marginHint( Q::Text ) ); + + QSizeF buttonSize = skinnable->strutSizeHint( Q::Button ); + buttonSize = buttonSize.grownBy( skinnable->marginHint( Q::Button ) ); + + const auto count = std::max( ( int )radioBox->options().count(), 1 ); + + const qreal w = textSize.width() + lineSpacing( radioBox ) + buttonSize.width(); + const qreal h = count * std::max( textSize.height(), buttonSize.height() ) + + ( count - 1 ) * skinnable->spacingHint( Q::Panel ); + + QSizeF hint( w, h ); + hint = hint.grownBy( skinnable->paddingHint( Q::Panel ) ); + hint = hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) ); + + return hint; +} + #include "moc_QskRadioBoxSkinlet.cpp" diff --git a/src/controls/QskRadioBoxSkinlet.h b/src/controls/QskRadioBoxSkinlet.h index c1c4f359..0c9cdad4 100644 --- a/src/controls/QskRadioBoxSkinlet.h +++ b/src/controls/QskRadioBoxSkinlet.h @@ -21,7 +21,7 @@ class QSK_EXPORT QskRadioBoxSkinlet : public QskSkinlet { PanelRole, ButtonRole, - SymbolRole, + IndicatorRole, TextRole, RippleRole, @@ -54,10 +54,7 @@ class QSK_EXPORT QskRadioBoxSkinlet : public QskSkinlet private: QRectF textRect( const QskRadioBox*, const QRectF&, int ) const; - - QSizeF buttonSymbolSize( const QskRadioBox* ) const; - QRectF buttonRect( const QskRadioBox*, - const QskAspect::Subcontrol, const QRectF&, double ) const; + QRectF buttonRect( const QskRadioBox*, const QRectF&, int index ) const; QRectF rippleRect( const QskRadioBox*, const QRectF& ) const; };