QskRadioBox updated. Adding a new subcontrol that corresponds to a

single button ( check indicator + text ), what allows better
configuration from the skin
This commit is contained in:
Uwe Rathmann 2023-03-05 16:31:55 +01:00
parent 75d9b4fd31
commit 73610cdb61
7 changed files with 124 additions and 148 deletions

View File

@ -458,16 +458,19 @@ void Editor::setupRadioBox()
using A = QskAspect;
setSpacing( Q::Panel, 10_dp );
setSpacing( Q::Button, 10_dp );
setStrutSize( Q::Button, 20_dp, 20_dp );
setStrutSize( Q::CheckIndicatorPanel, 20_dp, 20_dp );
setStrutSize( Q::Ripple, 40_dp, 40_dp );
for ( auto subControl : { Q::Button, Q::Indicator, Q::Ripple } )
for ( auto subControl : { Q::CheckIndicatorPanel, Q::CheckIndicator, Q::Ripple } )
setBoxShape( subControl, 100, Qt::RelativeSize ); // circular
setBoxBorderMetrics( Q::Button, 2_dp );
setBoxBorderColors( Q::Button, m_pal.onBackground );
setPadding( Q::Button, 5_dp );
setBoxBorderMetrics( Q::CheckIndicatorPanel, 2_dp );
setBoxBorderColors( Q::CheckIndicatorPanel, m_pal.onBackground );
setPadding( Q::CheckIndicatorPanel, 5_dp );
setGradient( Q::Button, QskGradient() );
setColor( Q::Text, m_pal.onBackground );
setColor( Q::Text | Q::Disabled, m_pal.onSurface38 );
@ -476,15 +479,16 @@ void Editor::setupRadioBox()
setColor( Q::Ripple | Q::Selected,
stateLayerColor( m_pal.primary, m_pal.focusOpacity ) );
setColor( Q::Indicator, Qt::transparent);
setColor( Q::Indicator | Q::Selected, m_pal.primary );
setColor( Q::Indicator | Q::Selected | Q::Disabled, m_pal.onSurface38 );
setColor( Q::CheckIndicator, Qt::transparent);
setColor( Q::CheckIndicator | Q::Selected, m_pal.primary );
setColor( Q::CheckIndicator | 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 );
setBoxBorderColors( Q::CheckIndicatorPanel | Q::Selected, m_pal.primary );
setBoxBorderColors( Q::CheckIndicatorPanel | Q::Disabled, m_pal.onSurface38 );
setBoxBorderColors(
Q::CheckIndicatorPanel | Q::Disabled | Q::Selected, m_pal.onSurface38 );
setAnimation( Q::Ripple | A::Metric | A::Position, qskDuration );
}

View File

@ -689,17 +689,21 @@ void Editor::setupRadioBox()
using Q = QskRadioBox;
setSpacing( Q::Panel, 10_dp );
setStrutSize( Q::Button, 20_dp, 20_dp );
setSpacing( Q::Button, 10_dp );
for ( auto subControl : { Q::Button, Q::Indicator, Q::Ripple } )
setStrutSize( Q::CheckIndicatorPanel, 20_dp, 20_dp );
for ( auto subControl : { Q::CheckIndicatorPanel, Q::CheckIndicator, Q::Ripple } )
setBoxShape( subControl, 100, Qt::RelativeSize ); // circular
setBoxBorderMetrics( Q::Button, 1_dp );
setBoxBorderMetrics( Q::CheckIndicatorPanel, 1_dp );
setBoxBorderColors( Q::Button, m_pal.darker125 );
setBoxBorderColors( Q::Button | Q::Disabled, m_pal.theme );
setBoxBorderColors( Q::CheckIndicatorPanel, m_pal.darker125 );
setBoxBorderColors( Q::CheckIndicatorPanel | Q::Disabled, m_pal.theme );
setPadding( Q::Button, 5_dp );
setPadding( Q::CheckIndicatorPanel, 5_dp );
setGradient( Q::Button, QskGradient() );
setColor( Q::Text, m_pal.themeForeground );
setColor( Q::Text | Q::Disabled, m_pal.darker200 );
@ -707,11 +711,11 @@ void Editor::setupRadioBox()
setColor( Q::Panel, m_pal.lighter125 );
setColor( Q::Panel | Q::Disabled, m_pal.lighter125 );
setColor( Q::Button | Q::Disabled, m_pal.lighter110 );
setColor( Q::CheckIndicatorPanel | Q::Disabled, m_pal.lighter110 );
setColor( Q::Indicator, Qt::transparent);
setColor( Q::Indicator | Q::Selected, m_pal.themeForeground );
setColor( Q::Indicator | Q::Selected | Q::Disabled, m_pal.darker200 );
setColor( Q::CheckIndicator, Qt::transparent);
setColor( Q::CheckIndicator | Q::Selected, m_pal.themeForeground );
setColor( Q::CheckIndicator | Q::Selected | Q::Disabled, m_pal.darker200 );
}
void Editor::setupDialogButtonBox()

View File

@ -49,7 +49,7 @@ void QskBoundedRangeInput::setLowerValue( qreal value )
{
if ( isComponentComplete() )
{
value = std::min( value, m_range.upperBound() );
value = qMin( value, m_range.upperBound() );
value = boundedValue( value );
}
@ -65,7 +65,7 @@ void QskBoundedRangeInput::setUpperValue( qreal value )
{
if ( isComponentComplete() )
{
value = std::max( m_range.lowerBound(), value );
value = qMax( m_range.lowerBound(), value );
value = boundedValue( value );
}

View File

@ -8,37 +8,10 @@
#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, Indicator )
QSK_SUBCONTROL( QskRadioBox, CheckIndicatorPanel )
QSK_SUBCONTROL( QskRadioBox, CheckIndicator )
QSK_SUBCONTROL( QskRadioBox, Text )
QSK_SUBCONTROL( QskRadioBox, Ripple )
@ -94,19 +67,19 @@ QRectF QskRadioBox::focusIndicatorRect() const
auto skinlet = effectiveSkinlet();
auto buttonRect = skinlet->sampleRect( this,
rect, QskRadioBox::Button, m_data->focusedIndex );
const auto panelRect = skinlet->sampleRect( this,
rect, QskRadioBox::CheckIndicatorPanel, m_data->focusedIndex );
auto y = buttonRect.y();
auto h = buttonRect.height();
auto y = panelRect.y();
auto h = panelRect.height();
auto textRect = skinlet->sampleRect( this,
const auto textRect = skinlet->sampleRect( this,
rect, QskRadioBox::Text, m_data->focusedIndex );
if( textRect.height() > 0.0 )
{
y = std::min( y, textRect.y() );
h = std::max( h, textRect.height() );
y = qMin( y, textRect.y() );
h = qMax( h, textRect.height() );
}
return QRectF( rect.x(), y, rect.width(), h );
@ -122,7 +95,7 @@ QStringList QskRadioBox::options() const
return m_data->options;
}
QString QskRadioBox::option( int index ) const
QString QskRadioBox::optionAt( int index ) const
{
return m_data->options.value( index );
}
@ -268,17 +241,8 @@ void QskRadioBox::focusOutEvent( QFocusEvent* event )
int QskRadioBox::indexAt( const QPointF& pos ) const
{
const auto skinlet = effectiveSkinlet();
const auto cr = contentsRect();
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 -1;
return effectiveSkinlet()->sampleIndexAt( this,
contentsRect(), QskRadioBox::Button, pos );
}
void QskRadioBox::setFocusedIndex( int index )

View File

@ -22,7 +22,7 @@ class QSK_EXPORT QskRadioBox : public QskControl
using Inherited = QskControl;
public:
QSK_SUBCONTROLS( Panel, Button, Indicator, Text, Ripple )
QSK_SUBCONTROLS( Panel, Button, CheckIndicatorPanel, CheckIndicator, Text, Ripple )
QSK_STATES( Selected, Pressed )
QskRadioBox( QQuickItem* parent = nullptr );
@ -34,7 +34,7 @@ class QSK_EXPORT QskRadioBox : public QskControl
QRectF focusIndicatorRect() const override;
QStringList options() const;
QString option( int ) const;
QString optionAt( int ) const;
int selectedIndex() const;
int pressedIndex() const;

View File

@ -12,62 +12,34 @@
namespace
{
qreal lineHeight( const QskRadioBox* radioBox )
QSizeF buttonSizeHint( const QskSkinnable* skinnable,
const QFontMetricsF& fm, const QString& text )
{
using Q = QskRadioBox;
auto strutHight = qMax( radioBox->strutSizeHint( Q::Button ).height(),
radioBox->strutSizeHint( Q::Text ).height() );
auto hint = skinnable->strutSizeHint( Q::CheckIndicatorPanel );
const auto textMargins = radioBox->marginHint( Q::Text );
hint.rwidth() += skinnable->spacingHint( Q::Button )
+ qskHorizontalAdvance( fm, text );
hint.rheight() = qMax( hint.height(), fm.height() );
auto fontHeight = radioBox->effectiveFontHeight( Q::Text );
fontHeight += textMargins.top() + textMargins.bottom();
hint = hint.grownBy( skinnable->paddingHint( Q::Button ) );
hint = hint.expandedTo( skinnable->strutSizeHint( Q::Button ) );
return qMax( strutHight, fontHeight );
return hint;
}
QRectF lineRect( const QskRadioBox* radioBox, const QRectF& rect, int index )
QSizeF buttonSizeHint( const QskRadioBox* radioBox, 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;
return buttonSizeHint( radioBox, fm, radioBox->optionAt( index ) );
}
#if 1
inline qreal lineSpacing( const QskRadioBox* )
{
// skinHint TODO ...
return 10;
}
#endif
}
QskRadioBoxSkinlet::QskRadioBoxSkinlet( QskSkin* )
{
setNodeRoles( { PanelRole, ButtonRole, IndicatorRole, TextRole, RippleRole } );
setNodeRoles( { PanelRole, ButtonRole, CheckPanelRole,
CheckIndicatorRole, TextRole, RippleRole } );
}
QskRadioBoxSkinlet::~QskRadioBoxSkinlet()
@ -100,8 +72,11 @@ QSGNode* QskRadioBoxSkinlet::updateSubNode( const QskSkinnable* skinnable,
case ButtonRole:
return updateSeriesNode( skinnable, Q::Button, node );
case IndicatorRole:
return updateSeriesNode( skinnable, Q::Indicator, node );
case CheckPanelRole:
return updateSeriesNode( skinnable, Q::CheckIndicatorPanel, node );
case CheckIndicatorRole:
return updateSeriesNode( skinnable, Q::CheckIndicator, node );
case TextRole:
return updateSeriesNode( skinnable, Q::Text, node );
@ -134,28 +109,47 @@ QRectF QskRadioBoxSkinlet::rippleRect(
if ( !r.isEmpty() )
{
const auto buttonRect = sampleRect( radioBox, rect, Q::Button, index );
r.moveCenter( buttonRect.center() );
const auto checkBoxRect = sampleRect(
radioBox, rect, Q::CheckIndicatorPanel, index );
r.moveCenter( checkBoxRect.center() );
}
return r;
}
QRectF QskRadioBoxSkinlet::buttonRect( const QskRadioBox* radioBox,
QRectF QskRadioBoxSkinlet::buttonRect(
const QskRadioBox* radioBox, const QRectF& rect, int index ) const
{
using Q = QskRadioBox;
/*
code only works when all buttons have the same height
- what might be wron, when lineWrapping is enabled TODO ...
*/
const auto h = buttonSizeHint( radioBox, index ).height();
const auto y = index * ( h + radioBox->spacingHint( Q::Panel ) );
const auto r = radioBox->subControlContentsRect( rect, Q::Panel );
return QRectF( r.left(), r.top() + y, r.width(), h );
}
QRectF QskRadioBoxSkinlet::checkPanelRect( const QskRadioBox* radioBox,
const QRectF& rect, int index ) const
{
using Q = QskRadioBox;
auto r = lineRect( radioBox, rect, index );
r = r.marginsRemoved( radioBox->paddingHint( Q::Panel ) );
auto r = sampleRect( radioBox, rect, Q::Button, index );
r = radioBox->innerBox( Q::Button, r );
auto alignment = radioBox->alignmentHint( Q::Button, Qt::AlignCenter );
auto alignment = radioBox->alignmentHint( Q::CheckIndicatorPanel, Qt::AlignCenter );
alignment &= Qt::AlignVertical_Mask;
alignment |= radioBox->layoutMirroring() ? Qt::AlignRight : Qt::AlignLeft;
auto size = radioBox->strutSizeHint( Q::Button );
size = size.grownBy( radioBox->marginHint( Q::Button ) );
auto size = radioBox->strutSizeHint( Q::CheckIndicatorPanel );
size = size.grownBy( radioBox->marginHint( Q::CheckIndicatorPanel ) );
return qskAlignedRectF( r, size.width(), size.height(), alignment );
}
@ -165,16 +159,18 @@ QRectF QskRadioBoxSkinlet::textRect( const QskRadioBox* radioBox,
{
using Q = QskRadioBox;
auto r = lineRect( radioBox, rect, index );
r = r.marginsRemoved( radioBox->paddingHint( Q::Panel ) );
auto r = sampleRect( radioBox, rect, Q::Button, index );
r = radioBox->innerBox( Q::Button, r );
const auto buttonRect = sampleRect( radioBox, rect, Q::Button, index );
const auto spacing = lineSpacing( radioBox );
const auto checkPanelRect = sampleRect(
radioBox, rect, Q::CheckIndicatorPanel, index );
const auto spacing = radioBox->spacingHint( Q::Button );
if ( !radioBox->layoutMirroring() )
r.setLeft( buttonRect.right() + spacing );
r.setLeft( checkPanelRect.right() + spacing );
else
r.setRight( buttonRect.left() - spacing );
r.setRight( checkPanelRect.left() - spacing );
return r;
}
@ -192,10 +188,13 @@ QRectF QskRadioBoxSkinlet::sampleRect( const QskSkinnable* skinnable,
if( subControl == Q::Button )
return buttonRect( radioBox, rect, index );
if( subControl == Q::Indicator )
if( subControl == Q::CheckIndicatorPanel )
return checkPanelRect( radioBox, rect, index );
if( subControl == Q::CheckIndicator )
{
auto r = sampleRect( radioBox, rect, Q::Button, index );
r = r.marginsRemoved( radioBox->paddingHint( Q::Button ) );
auto r = sampleRect( radioBox, rect, Q::CheckIndicatorPanel, index );
r = r.marginsRemoved( radioBox->paddingHint( Q::CheckIndicatorPanel ) );
return r;
}
@ -247,10 +246,10 @@ QSGNode* QskRadioBoxSkinlet::updateSampleNode( const QskSkinnable* skinnable,
alignment |= radioBox->layoutMirroring() ? Qt::AlignRight : Qt::AlignLeft;
return updateTextNode( radioBox, node, rect,
alignment, radioBox->option( index ), subcontrol );
alignment, radioBox->optionAt( index ), subcontrol );
}
if ( subcontrol == Q::Button || subcontrol == Q::Indicator )
if ( subcontrol == Q::CheckIndicatorPanel || subcontrol == Q::CheckIndicator )
return updateBoxNode( radioBox, node, rect, subcontrol );
return node;
@ -261,8 +260,6 @@ QSizeF QskRadioBoxSkinlet::sizeHint( const QskSkinnable* skinnable,
{
using Q = QskRadioBox;
const auto radioBox = static_cast< const QskRadioBox* >( skinnable );
if ( which != Qt::PreferredSize )
return QSizeF();
@ -271,19 +268,24 @@ QSizeF QskRadioBoxSkinlet::sizeHint( const QskSkinnable* skinnable,
// heightForWidth would make sense when word wrapping is enabled TODO ...
}
QSizeF textSize( maxTextWidth( radioBox ), 0.0 );
const auto radioBox = static_cast< const QskRadioBox* >( skinnable );
textSize = textSize.expandedTo( skinnable->strutSizeHint( Q::Text ) );
textSize = textSize.grownBy( skinnable->marginHint( Q::Text ) );
const QFontMetrics fm( radioBox->effectiveFont( QskRadioBox::Text ) );
QSizeF buttonSize = skinnable->strutSizeHint( Q::Button );
buttonSize = buttonSize.grownBy( skinnable->marginHint( Q::Button ) );
qreal w = 0.0;
qreal h = 0.0;
const auto count = std::max( ( int )radioBox->options().count(), 1 );
const auto options = radioBox->options();
for( const auto& option : options )
{
const auto buttonSize = buttonSizeHint( radioBox, fm, option );
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 );
w = qMax( w, buttonSize.width() );
h += buttonSize.height();
}
if ( auto count = radioBox->options().count() )
h += ( count - 1 ) * skinnable->spacingHint( Q::Panel );
QSizeF hint( w, h );
hint = hint.grownBy( skinnable->paddingHint( Q::Panel ) );

View File

@ -21,7 +21,8 @@ class QSK_EXPORT QskRadioBoxSkinlet : public QskSkinlet
{
PanelRole,
ButtonRole,
IndicatorRole,
CheckPanelRole,
CheckIndicatorRole,
TextRole,
RippleRole,
@ -54,6 +55,7 @@ class QSK_EXPORT QskRadioBoxSkinlet : public QskSkinlet
private:
QRectF textRect( const QskRadioBox*, const QRectF&, int ) const;
QRectF checkPanelRect( const QskRadioBox*, const QRectF&, int index ) const;
QRectF buttonRect( const QskRadioBox*, const QRectF&, int index ) const;
QRectF rippleRect( const QskRadioBox*, const QRectF& ) const;