2023-02-08 21:49:04 +01:00
|
|
|
#include "QskRadioBoxSkinlet.h"
|
|
|
|
|
|
|
|
#include "QskAspect.h"
|
|
|
|
#include "QskRadioBox.h"
|
|
|
|
|
2023-02-12 02:42:33 +01:00
|
|
|
#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"
|
|
|
|
#include <type_traits>
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
using Q = QskRadioBox;
|
|
|
|
};
|
|
|
|
|
|
|
|
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-12 02:42:33 +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,
|
|
|
|
const QRectF& contentsRect, QskAspect::Subcontrol subcontrol) const
|
|
|
|
{
|
|
|
|
auto radio = static_cast<const QskRadioBox*>( skinnable );
|
|
|
|
|
|
|
|
if( subcontrol == Q::Ripple ) {
|
2023-02-12 02:42:33 +01:00
|
|
|
return buttonRect(radio, Q::Ripple, contentsRect, radio->positionHint(Q::Ripple));
|
2023-02-08 21:49:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return contentsRect;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSizeF QskRadioBoxSkinlet::sizeHint( const QskSkinnable* skinnable,
|
|
|
|
Qt::SizeHint, const QSizeF& ) const
|
|
|
|
{
|
|
|
|
auto radio = static_cast<const QskRadioBox*>( skinnable );
|
|
|
|
|
|
|
|
const auto font = skinnable->effectiveFont( Q::Text );
|
|
|
|
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 );
|
|
|
|
|
|
|
|
qreal maxTextWidth = 0;
|
|
|
|
for(auto& item : radio->items() ) {
|
|
|
|
maxTextWidth = std::max( maxTextWidth, qskHorizontalAdvance( font, item ) );
|
|
|
|
}
|
|
|
|
|
2023-02-11 21:13:32 +01:00
|
|
|
auto radioWidth = radio->strutSizeHint(Q::Button).width();
|
2023-02-08 21:49:04 +01:00
|
|
|
auto symbolWidth = radio->strutSizeHint(Q::Symbol).width();
|
|
|
|
|
|
|
|
maxTextWidth += textMargins.left() + textMargins.right();
|
|
|
|
radioWidth += buttonMargins.left() + buttonMargins.right();
|
|
|
|
symbolWidth += symbolMargins.left() + symbolMargins.right();
|
|
|
|
|
|
|
|
auto spacing = radio->spacingHint(Q::Panel);
|
|
|
|
return QSizeF( maxTextWidth + qMax(radioWidth, symbolWidth),
|
|
|
|
( lineHeight( radio ) + spacing ) * radio->items().size()
|
|
|
|
- spacing );
|
|
|
|
}
|
|
|
|
|
|
|
|
QSGNode* QskRadioBoxSkinlet::updateSubNode( const QskSkinnable* skinnable,
|
|
|
|
quint8 nodeRole, QSGNode* node) const
|
|
|
|
{
|
2023-02-11 21:13:32 +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-12 02:42:33 +01:00
|
|
|
{
|
2023-02-14 20:06:52 +01:00
|
|
|
QskSkinStateChanger changer( radio );
|
|
|
|
changer.setStates( statesForIndex( radio, radio->positionHint( Q::Ripple ) ) );
|
2023-02-12 02:42:33 +01:00
|
|
|
|
2023-02-11 21:13:32 +01:00
|
|
|
return updateBoxNode( radio, node, Q::Ripple );
|
2023-02-12 02:42:33 +01:00
|
|
|
}
|
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-08 21:49:04 +01:00
|
|
|
target->strutSizeHint( Q::Text ).height() );
|
|
|
|
const auto textMargins = target->marginHint( Q::Text );
|
|
|
|
auto fontHeight = target->effectiveFontHeight( Q::Text );
|
|
|
|
fontHeight += textMargins.top() + textMargins.bottom();
|
|
|
|
|
|
|
|
return qMax( strutHight, fontHeight );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int QskRadioBoxSkinlet::sampleCount( const QskSkinnable* skinnable,
|
|
|
|
QskAspect::Subcontrol ) const {
|
|
|
|
const auto radio = static_cast< const QskRadioBox* >( skinnable );
|
|
|
|
return radio->items().count();
|
|
|
|
}
|
|
|
|
|
2023-02-11 21:13:32 +01:00
|
|
|
QRectF QskRadioBoxSkinlet::buttonRect( const QskRadioBox* radio,
|
2023-02-08 21:49:04 +01:00
|
|
|
const QskAspect::Subcontrol target,
|
2023-02-12 02:42:33 +01:00
|
|
|
const QRectF& rect, double index ) const {
|
2023-02-12 01:30:15 +01:00
|
|
|
if( index < 0 ) {
|
|
|
|
return QRectF();
|
|
|
|
}
|
2023-02-12 02:42:33 +01:00
|
|
|
|
2023-02-08 21:49:04 +01:00
|
|
|
auto result = rect;
|
|
|
|
result.setSize( radio->strutSizeHint( target ) );
|
|
|
|
|
|
|
|
auto spacing = radio->spacingHint(Q::Panel);
|
|
|
|
result.moveTop( ( lineHeight( radio ) + spacing ) * index
|
|
|
|
+ (lineHeight(radio) - result.size().height()) / 2);
|
|
|
|
|
2023-02-11 23:14:29 +01:00
|
|
|
auto maxWidth = qMax(radio->strutSizeHint( Q::Button ).width(),
|
|
|
|
radio->strutSizeHint( Q::Symbol ).width());
|
|
|
|
|
2023-02-08 21:49:04 +01:00
|
|
|
if( radio->layoutMirroring() ) {
|
2023-02-11 23:14:29 +01:00
|
|
|
result.moveRight( rect.width() - (maxWidth - result.width())/2);
|
2023-02-08 21:49:04 +01:00
|
|
|
} else {
|
2023-02-11 23:14:29 +01:00
|
|
|
result.moveLeft((maxWidth - result.width()) / 2);
|
2023-02-08 21:49:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF QskRadioBoxSkinlet::textRect( const QskRadioBox* radio,
|
|
|
|
const QRectF& rect, int index ) const {
|
|
|
|
QRectF result = rect;
|
|
|
|
auto spacing = radio->spacingHint(Q::Panel);
|
|
|
|
auto lh = lineHeight( radio );
|
|
|
|
const auto textMargins = radio->marginHint( Q::Text );
|
|
|
|
|
|
|
|
result.moveTop( index * ( lh + spacing )
|
|
|
|
+ lh - radio->effectiveFontHeight(Q::Text)
|
|
|
|
+ textMargins.top());
|
|
|
|
|
2023-02-11 23:15:07 +01:00
|
|
|
if( !radio->layoutMirroring() ) {
|
|
|
|
auto maxWidth = qMax( buttonRect( radio, Q::Symbol, rect, index ).width(),
|
|
|
|
buttonRect( radio, Q::Button, rect, index ).width());
|
|
|
|
|
|
|
|
result.moveLeft( maxWidth + textMargins.left());
|
2023-02-08 21:49:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF QskRadioBoxSkinlet::sampleRect( const QskSkinnable* skinnable,
|
|
|
|
const QRectF& rect, QskAspect::Subcontrol subcontrol,
|
|
|
|
int index ) const {
|
|
|
|
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-11 21:15:32 +01:00
|
|
|
auto radio = static_cast<const QskRadioBox*>( skinnable );
|
2023-02-08 21:49:04 +01:00
|
|
|
auto states = Inherited::sampleStates( skinnable, subControl, index );
|
|
|
|
|
2023-02-12 02:42:33 +01:00
|
|
|
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-11 21:15:32 +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-11 21:15:32 +01:00
|
|
|
return QskSkinlet::updateTextNode( radio,
|
2023-02-08 21:49:04 +01:00
|
|
|
node,
|
|
|
|
rect,
|
|
|
|
Qt::AlignLeft,
|
2023-02-11 21:15:32 +01:00
|
|
|
radio->items()[index],
|
2023-02-08 21:49:04 +01:00
|
|
|
subcontrol);
|
2023-02-11 21:13:32 +01:00
|
|
|
} else if (subcontrol == Q::Button) {
|
2023-02-12 02:42:33 +01:00
|
|
|
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-14 20:25:10 +01:00
|
|
|
auto colorSub = radio->color( subcontrol | statesForIndex(radio, index) );
|
|
|
|
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;
|
|
|
|
}
|