qskinny/src/controls/QskRadioBoxSkinlet.cpp

299 lines
9.3 KiB
C++
Raw Normal View History

2023-02-08 21:49:04 +01:00
#include "QskRadioBoxSkinlet.h"
#include "QskAspect.h"
#include "QskRadioBox.h"
#include "QskSkinStateChanger.h"
2023-02-08 21:49:04 +01:00
#include "QskStandardSymbol.h"
#include "QskColorFilter.h"
#include "QskGraphic.h"
#include "QskFunctions.h"
#include "QskSkin.h"
namespace {
using Q = QskRadioBox;
2023-02-26 15:45:57 +01:00
}
2023-02-08 21:49:04 +01:00
QskRadioBoxSkinlet::QskRadioBoxSkinlet( QskSkin* )
{
2023-02-11 21:13:32 +01:00
setNodeRoles( { PanelRole, ButtonRole, SymbolRole, TextRole, RippleRole } );
2023-02-08 21:49:04 +01:00
}
QskRadioBoxSkinlet::~QskRadioBoxSkinlet()
{
}
2023-02-25 23:31:29 +01:00
QskAspect::States statesForIndex( const QskRadioBox* radio, int index ) {
auto states = radio->skinStates();
if( radio->selectedIndex() == index ) {
states |= Q::Selected;
}
if( radio->pressedIndex() == index ) {
states |= Q::Pressed;
}
if( radio->positionHint( Q::Ripple ) == index ) {
states |= Q::Focused;
}
return states;
}
2023-02-08 21:49:04 +01:00
QRectF QskRadioBoxSkinlet::subControlRect( const QskSkinnable* skinnable,
2023-02-25 23:31:29 +01:00
const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const
2023-02-08 21:49:04 +01:00
{
2023-02-25 23:31:29 +01:00
auto radio = static_cast< const QskRadioBox* >( skinnable );
2023-02-08 21:49:04 +01:00
if( subcontrol == Q::Ripple ) {
2023-02-25 23:31:29 +01:00
return rippleRect( radio, contentsRect );
2023-02-08 21:49:04 +01:00
}
return contentsRect;
}
QSizeF QskRadioBoxSkinlet::sizeHint( const QskSkinnable* skinnable,
2023-02-25 23:31:29 +01:00
Qt::SizeHint, const QSizeF& ) const
2023-02-08 21:49:04 +01:00
{
2023-02-25 23:31:29 +01:00
auto radio = static_cast< const QskRadioBox* >( skinnable );
2023-02-08 21:49:04 +01:00
2023-02-25 23:31:29 +01:00
const auto font = skinnable->effectiveFont( Q::Text );
2023-02-08 21:49:04 +01:00
const auto textMargins = skinnable->marginHint( Q::Text );
2023-02-11 21:13:32 +01:00
const auto buttonMargins = skinnable->marginHint( Q::Button );
2023-02-08 21:49:04 +01:00
const auto symbolMargins = skinnable->marginHint( Q::Symbol );
2023-02-08 21:49:04 +01:00
qreal maxTextWidth = 0;
2023-02-25 23:31:29 +01:00
for( auto& item : radio->items() ) {
2023-02-08 21:49:04 +01:00
maxTextWidth = std::max( maxTextWidth, qskHorizontalAdvance( font, item ) );
}
2023-02-25 23:31:29 +01:00
auto buttonWidth = radio->strutSizeHint( Q::Button ).width();
auto symbolWidth = radio->strutSizeHint( Q::Symbol ).width();
2023-02-08 21:49:04 +01:00
maxTextWidth += textMargins.left() + textMargins.right();
buttonWidth += buttonMargins.left() + buttonMargins.right();
2023-02-08 21:49:04 +01:00
symbolWidth += symbolMargins.left() + symbolMargins.right();
2023-02-25 23:31:29 +01:00
auto spacing = radio->spacingHint( Q::Panel );
return QSizeF( maxTextWidth + qMax( buttonWidth, symbolWidth ),
( lineHeight( radio ) + spacing ) * radio->items().size() - spacing );
2023-02-08 21:49:04 +01:00
}
QSGNode* QskRadioBoxSkinlet::updateSubNode( const QskSkinnable* skinnable,
2023-02-25 23:31:29 +01:00
quint8 nodeRole, QSGNode* node ) const
2023-02-08 21:49:04 +01:00
{
2023-02-25 23:31:29 +01:00
auto radio = static_cast< const QskRadioBox* >( skinnable );
2023-02-08 21:49:04 +01:00
switch( nodeRole )
{
case PanelRole:
return updateBoxNode( skinnable, node, Q::Panel );
2023-02-11 21:13:32 +01:00
case ButtonRole:
return updateSeriesNode( radio, Q::Button, node );
2023-02-08 21:49:04 +01:00
case SymbolRole:
2023-02-11 21:13:32 +01:00
return updateSeriesNode( radio, Q::Symbol, node );
2023-02-08 21:49:04 +01:00
case TextRole:
2023-02-11 21:13:32 +01:00
return updateSeriesNode( radio, Q::Text, node );
2023-02-08 21:49:04 +01:00
case RippleRole:
{
2023-02-14 20:06:52 +01:00
QskSkinStateChanger changer( radio );
2023-02-25 23:31:29 +01:00
auto ripplePosition = radio->positionHint( Q::Ripple );
changer.setStates( statesForIndex( radio, ripplePosition ) );
2023-02-11 21:13:32 +01:00
return updateBoxNode( radio, node, Q::Ripple );
}
2023-02-08 21:49:04 +01:00
};
return Inherited::updateSubNode( skinnable, nodeRole, node );
}
qreal QskRadioBoxSkinlet::lineHeight(const QskRadioBox* target) const {
2023-02-11 21:13:32 +01:00
auto strutHight = qMax( target->strutSizeHint( Q::Button ).height(),
2023-02-25 23:31:29 +01:00
target->strutSizeHint( Q::Text ).height() );
2023-02-08 21:49:04 +01:00
const auto textMargins = target->marginHint( Q::Text );
auto fontHeight = target->effectiveFontHeight( Q::Text );
fontHeight += textMargins.top() + textMargins.bottom();
return qMax( strutHight, fontHeight );
}
2023-02-25 23:31:29 +01:00
int QskRadioBoxSkinlet::sampleCount( const QskSkinnable* skinnable, QskAspect::Subcontrol ) const {
2023-02-08 21:49:04 +01:00
const auto radio = static_cast< const QskRadioBox* >( skinnable );
return radio->items().count();
}
QSizeF QskRadioBoxSkinlet::buttonSymbolSize( const QskRadioBox* radio ) const {
auto buttonStrut = radio->strutSizeHint( Q::Button );
auto symbolStrut = radio->strutSizeHint( Q::Symbol );
buttonStrut = buttonStrut.grownBy( radio->marginHint( Q::Button ) );
symbolStrut = symbolStrut.grownBy( radio->marginHint( Q::Symbol ) );
return QSizeF(
qMax( buttonStrut.width(), symbolStrut.width() ),
qMax( buttonStrut.height(), symbolStrut.height() ) );
}
2023-02-25 23:31:29 +01:00
QRectF QskRadioBoxSkinlet::rippleRect( const QskRadioBox* radio, const QRectF& rect ) const {
auto ripplePosition = radio->positionHint( Q::Ripple );
if( ripplePosition < 0 ) {
return QRectF();
}
auto button = buttonRect( radio, Q::Button, rect, ripplePosition );
auto rippleSize = radio->strutSizeHint( Q::Ripple );
button.moveLeft( button.x() - ( rippleSize.width() - button.width() ) / 2 );
button.moveTop( button.y() - ( rippleSize.height() - button.height() ) / 2 );
button.setSize( rippleSize );
return button;
}
2023-02-11 21:13:32 +01:00
QRectF QskRadioBoxSkinlet::buttonRect( const QskRadioBox* radio,
2023-02-25 23:31:29 +01:00
const QskAspect::Subcontrol target, const QRectF& rect, double index ) const {
2023-02-12 01:30:15 +01:00
if( index < 0 ) {
return QRectF();
}
2023-02-08 21:49:04 +01:00
auto result = rect;
2023-02-25 23:31:29 +01:00
result.setSize( radio->strutSizeHint( target ) );
2023-02-08 21:49:04 +01:00
2023-02-25 23:31:29 +01:00
auto spacing = radio->spacingHint( Q::Panel );
result.moveTop( ( lineHeight( radio ) + spacing ) * index );
auto margins = radio->marginHint( target );
auto withMargins = result.size().grownBy( margins );
auto maxSize = buttonSymbolSize( radio );
auto alignment = radio->alignmentHint( target );
// Vertical positioning
auto alignHeight = maxSize.height() - withMargins.height();
2023-02-25 23:31:29 +01:00
if( alignment.testFlag( Qt::AlignVCenter ) ) {
result.moveTop( result.top() + alignHeight / 2 );
} else if( alignment.testFlag( Qt::AlignBottom ) ) {
result.moveTop( result.top() + alignHeight );
}
2023-02-25 23:31:29 +01:00
result.moveTop( result.top() + margins.top() );
// Horizontal positioning
auto alignWidth = 0;
if( alignment.testFlag( Qt::AlignHCenter ) ) {
2023-02-25 23:31:29 +01:00
alignWidth = ( maxSize.width() - withMargins.width() ) / 2;
} else if ( alignment.testFlag( Qt::AlignRight )) {
alignWidth = maxSize.width() - withMargins.width();
}
2023-02-08 21:49:04 +01:00
if( radio->layoutMirroring() ) {
2023-02-25 23:31:29 +01:00
result.moveRight( rect.width() - ( alignWidth + margins.right() ) );
2023-02-08 21:49:04 +01:00
} else {
result.moveLeft( margins.left() + alignWidth );
2023-02-08 21:49:04 +01:00
}
return result;
}
QRectF QskRadioBoxSkinlet::textRect( const QskRadioBox* radio,
2023-02-25 23:31:29 +01:00
const QRectF& rect, int index ) const {
auto text = radio->items()[ index ];
2023-02-25 22:24:39 +01:00
if(text.isEmpty()) {
return QRectF();
}
2023-02-08 21:49:04 +01:00
QRectF result = rect;
2023-02-25 23:31:29 +01:00
auto spacing = radio->spacingHint( Q::Panel );
2023-02-08 21:49:04 +01:00
auto lh = lineHeight( radio );
const auto textMargins = radio->marginHint( Q::Text );
2023-02-15 00:20:19 +01:00
const auto font = radio->effectiveFont( Q::Text );
2023-02-08 21:49:04 +01:00
result.moveTop( index * ( lh + spacing )
2023-02-25 23:31:29 +01:00
+ lh - radio->effectiveFontHeight( Q::Text )
+ textMargins.top());
2023-02-08 21:49:04 +01:00
2023-02-15 00:20:19 +01:00
result.setHeight( lh );
result.setWidth( qskHorizontalAdvance( font, text ) );
2023-02-11 23:15:07 +01:00
auto button = buttonRect( radio, Q::Button, rect, index );
2023-02-15 00:20:19 +01:00
auto buttonsMargins = radio->marginHint( Q::Button );
auto buttonWidth = button.marginsAdded( buttonsMargins ).width();
2023-02-15 00:20:19 +01:00
if( radio->layoutMirroring() ) {
result.moveLeft( rect.width() - textMargins.right()
2023-02-25 23:31:29 +01:00
- result.width() - buttonWidth);
2023-02-15 00:20:19 +01:00
} else {
result.moveLeft( buttonWidth + textMargins.left() );
2023-02-08 21:49:04 +01:00
}
return result;
}
QRectF QskRadioBoxSkinlet::sampleRect( const QskSkinnable* skinnable,
2023-02-25 23:31:29 +01:00
const QRectF& rect, QskAspect::Subcontrol subcontrol, int index ) const {
2023-02-08 21:49:04 +01:00
const auto radio = static_cast< const QskRadioBox* >( skinnable );
if( subcontrol == Q::Text ) {
return textRect( radio, rect, index );
}
2023-02-11 21:13:32 +01:00
return buttonRect( radio, subcontrol, rect, index);
2023-02-08 21:49:04 +01:00
}
QskAspect::States QskRadioBoxSkinlet::sampleStates( const QskSkinnable* skinnable,
QskAspect::Subcontrol subControl, int index ) const {
2023-02-25 23:31:29 +01:00
auto radio = static_cast< const QskRadioBox* >( skinnable );
2023-02-08 21:49:04 +01:00
auto states = Inherited::sampleStates( skinnable, subControl, index );
return states | statesForIndex( radio, index );
2023-02-08 21:49:04 +01:00
}
QSGNode* QskRadioBoxSkinlet::updateSampleNode( const QskSkinnable* skinnable,
QskAspect::Subcontrol subcontrol, int index, QSGNode* node ) const {
2023-02-25 23:31:29 +01:00
auto radio = static_cast< const QskRadioBox* >( skinnable );
2023-02-08 21:49:04 +01:00
2023-02-11 21:15:32 +01:00
auto rect = sampleRect( skinnable, radio->contentsRect(),
2023-02-08 21:49:04 +01:00
subcontrol, index );
if( subcontrol == Q::Text ) {
2023-02-25 23:31:29 +01:00
return QskSkinlet::updateTextNode( radio, node, rect, Qt::AlignLeft,
radio->items()[ index ], subcontrol );
} else if ( subcontrol == Q::Button ) {
return QskSkinlet::updateBoxNode( radio, node, rect, subcontrol );
2023-02-08 21:49:04 +01:00
} else if( subcontrol == Q::Symbol ) {
auto symbol = QskStandardSymbol::NoSymbol;
2023-02-11 21:15:32 +01:00
auto color = radio->color( subcontrol ).rgb();
2023-02-08 21:49:04 +01:00
2023-02-11 21:15:32 +01:00
if( radio->selectedIndex() == index ) {
2023-02-08 21:49:04 +01:00
symbol = QskStandardSymbol::Bullet;
2023-02-11 21:15:32 +01:00
color = radio->color( subcontrol | Q::Selected ).rgb();
2023-02-08 21:49:04 +01:00
}
2023-02-11 21:15:32 +01:00
auto graphic = radio->effectiveSkin()->symbol( symbol );
2023-02-08 21:49:04 +01:00
/*
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 );
2023-02-25 23:31:29 +01:00
auto colorSub = radio->color( subcontrol | statesForIndex( radio, index ) );
2023-02-14 20:25:10 +01:00
filter.addColorSubstitution( Qt::black, colorSub.rgb() );
2023-02-08 21:49:04 +01:00
QskGraphic::fromGraphic( graphic, filter );
2023-02-11 21:15:32 +01:00
return updateGraphicNode( radio, node, graphic, filter, rect );
2023-02-08 21:49:04 +01:00
}
return node;
}
2023-02-20 22:13:14 +01:00
#include "moc_QskRadioBoxSkinlet.cpp"