2023-02-26 17:04:47 +01:00
|
|
|
/******************************************************************************
|
|
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
|
|
*****************************************************************************/
|
|
|
|
|
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"
|
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
#include <qfontmetrics.h>
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
namespace
|
2023-02-08 21:49:04 +01:00
|
|
|
{
|
2023-02-26 17:04:47 +01:00
|
|
|
QskAspect::States statesForIndex( const QskRadioBox* radioBox, int index )
|
|
|
|
{
|
|
|
|
using Q = QskRadioBox;
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
auto states = radioBox->skinStates();
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
if( radioBox->selectedIndex() == index )
|
|
|
|
states |= Q::Selected;
|
|
|
|
|
|
|
|
if( radioBox->pressedIndex() == index )
|
|
|
|
states |= Q::Pressed;
|
|
|
|
|
|
|
|
if( radioBox->positionHint( Q::Ripple ) == index )
|
|
|
|
states |= Q::Focused;
|
2023-02-12 02:42:33 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
return states;
|
2023-02-12 02:42:33 +01:00
|
|
|
}
|
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
qreal lineHeight( const QskRadioBox* radioBox )
|
|
|
|
{
|
|
|
|
using Q = QskRadioBox;
|
|
|
|
|
|
|
|
auto strutHight = qMax( radioBox->strutSizeHint( Q::Button ).height(),
|
|
|
|
radioBox->strutSizeHint( Q::Text ).height() );
|
|
|
|
|
|
|
|
const auto textMargins = radioBox->marginHint( Q::Text );
|
|
|
|
|
|
|
|
auto fontHeight = radioBox->effectiveFontHeight( Q::Text );
|
|
|
|
fontHeight += textMargins.top() + textMargins.bottom();
|
|
|
|
|
|
|
|
return qMax( strutHight, fontHeight );
|
2023-02-12 02:42:33 +01:00
|
|
|
}
|
2023-02-26 17:04:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QskRadioBoxSkinlet::QskRadioBoxSkinlet( QskSkin* )
|
|
|
|
{
|
|
|
|
setNodeRoles( { PanelRole, ButtonRole, SymbolRole, TextRole, RippleRole } );
|
|
|
|
}
|
2023-02-12 02:42:33 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
QskRadioBoxSkinlet::~QskRadioBoxSkinlet()
|
|
|
|
{
|
2023-02-12 02:42:33 +01:00
|
|
|
}
|
|
|
|
|
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-26 17:04:47 +01:00
|
|
|
auto radioBox = static_cast< const QskRadioBox* >( skinnable );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
if( subcontrol == QskRadioBox::Ripple )
|
|
|
|
return rippleRect( radioBox, 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-26 17:04:47 +01:00
|
|
|
using Q = QskRadioBox;
|
|
|
|
|
|
|
|
auto radioBox = 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-26 17:04:47 +01:00
|
|
|
|
2023-02-08 21:49:04 +01:00
|
|
|
qreal maxTextWidth = 0;
|
2023-02-26 17:04:47 +01:00
|
|
|
for( const auto& item : radioBox->items() )
|
|
|
|
maxTextWidth = std::max( maxTextWidth, qskHorizontalAdvance( font, item ) );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
auto buttonWidth = radioBox->strutSizeHint( Q::Button ).width();
|
|
|
|
auto symbolWidth = radioBox->strutSizeHint( Q::Symbol ).width();
|
2023-02-08 21:49:04 +01:00
|
|
|
|
|
|
|
maxTextWidth += textMargins.left() + textMargins.right();
|
2023-02-25 22:25:43 +01:00
|
|
|
buttonWidth += buttonMargins.left() + buttonMargins.right();
|
2023-02-08 21:49:04 +01:00
|
|
|
symbolWidth += symbolMargins.left() + symbolMargins.right();
|
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
auto spacing = radioBox->spacingHint( Q::Panel );
|
2023-02-25 23:31:29 +01:00
|
|
|
return QSizeF( maxTextWidth + qMax( buttonWidth, symbolWidth ),
|
2023-02-26 17:04:47 +01:00
|
|
|
( lineHeight( radioBox ) + spacing ) * radioBox->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-26 17:04:47 +01:00
|
|
|
using Q = QskRadioBox;
|
2023-02-08 21:49:04 +01:00
|
|
|
|
|
|
|
switch( nodeRole )
|
|
|
|
{
|
|
|
|
case PanelRole:
|
2023-02-26 17:04:47 +01:00
|
|
|
return updateBoxNode( skinnable, node, Q::Panel );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-11 21:13:32 +01:00
|
|
|
case ButtonRole:
|
2023-02-26 17:04:47 +01:00
|
|
|
return updateSeriesNode( skinnable, Q::Button, node );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
|
|
|
case SymbolRole:
|
2023-02-26 17:04:47 +01:00
|
|
|
return updateSeriesNode( skinnable, Q::Symbol, node );
|
|
|
|
|
2023-02-08 21:49:04 +01:00
|
|
|
case TextRole:
|
2023-02-26 17:04:47 +01:00
|
|
|
return updateSeriesNode( skinnable, Q::Text, node );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
|
|
|
case RippleRole:
|
2023-02-26 17:04:47 +01:00
|
|
|
{
|
|
|
|
auto radioBox = static_cast< const QskRadioBox* >( skinnable );
|
2023-02-12 02:42:33 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
QskSkinStateChanger changer( radioBox );
|
|
|
|
auto ripplePosition = radioBox->positionHint( Q::Ripple );
|
|
|
|
changer.setStates( statesForIndex( radioBox, ripplePosition ) );
|
|
|
|
|
|
|
|
return updateBoxNode( radioBox, node, Q::Ripple );
|
|
|
|
}
|
2023-02-08 21:49:04 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
return Inherited::updateSubNode( skinnable, nodeRole, node );
|
|
|
|
}
|
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
int QskRadioBoxSkinlet::sampleCount(
|
|
|
|
const QskSkinnable* skinnable, QskAspect::Subcontrol ) const
|
|
|
|
{
|
|
|
|
const auto radioBox = static_cast< const QskRadioBox* >( skinnable );
|
|
|
|
return radioBox->items().count();
|
2023-02-08 21:49:04 +01:00
|
|
|
}
|
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
QSizeF QskRadioBoxSkinlet::buttonSymbolSize( const QskRadioBox* radioBox ) const
|
|
|
|
{
|
|
|
|
using Q = QskRadioBox;
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
auto buttonStrut = radioBox->strutSizeHint( Q::Button );
|
|
|
|
auto symbolStrut = radioBox->strutSizeHint( Q::Symbol );
|
2023-02-25 22:25:43 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
buttonStrut = buttonStrut.grownBy( radioBox->marginHint( Q::Button ) );
|
|
|
|
symbolStrut = symbolStrut.grownBy( radioBox->marginHint( Q::Symbol ) );
|
2023-02-25 22:25:43 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
return QSizeF( qMax( buttonStrut.width(), symbolStrut.width() ),
|
2023-02-25 22:25:43 +01:00
|
|
|
qMax( buttonStrut.height(), symbolStrut.height() ) );
|
|
|
|
}
|
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
QRectF QskRadioBoxSkinlet::rippleRect(
|
|
|
|
const QskRadioBox* radioBox, const QRectF& rect ) const
|
|
|
|
{
|
|
|
|
using Q = QskRadioBox;
|
|
|
|
|
|
|
|
auto ripplePosition = radioBox->positionHint( Q::Ripple );
|
2023-02-25 22:25:43 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
if( ripplePosition < 0 )
|
|
|
|
return QRectF();
|
|
|
|
|
|
|
|
auto rippleSize = 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 );
|
|
|
|
|
|
|
|
return r;
|
2023-02-25 22:25:43 +01:00
|
|
|
}
|
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
QRectF QskRadioBoxSkinlet::buttonRect( const QskRadioBox* radioBox,
|
|
|
|
const QskAspect::Subcontrol target, const QRectF& rect, double index ) const
|
|
|
|
{
|
|
|
|
using Q = QskRadioBox;
|
|
|
|
|
|
|
|
if( index < 0 )
|
|
|
|
return QRectF();
|
2023-02-12 02:42:33 +01:00
|
|
|
|
2023-02-08 21:49:04 +01:00
|
|
|
auto result = rect;
|
2023-02-26 17:04:47 +01:00
|
|
|
result.setSize( radioBox->strutSizeHint( target ) );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
auto spacing = radioBox->spacingHint( Q::Panel );
|
|
|
|
result.moveTop( ( lineHeight( radioBox ) + spacing ) * index );
|
2023-02-25 22:25:43 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
auto margins = radioBox->marginHint( target );
|
2023-02-25 22:25:43 +01:00
|
|
|
auto withMargins = result.size().grownBy( margins );
|
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
auto maxSize = buttonSymbolSize( radioBox );
|
|
|
|
auto alignment = radioBox->alignmentHint( target );
|
2023-02-25 22:25:43 +01:00
|
|
|
|
|
|
|
// Vertical positioning
|
2023-02-26 17:04:47 +01:00
|
|
|
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 );
|
|
|
|
|
2023-02-25 23:31:29 +01:00
|
|
|
result.moveTop( result.top() + margins.top() );
|
2023-02-25 22:25:43 +01:00
|
|
|
|
|
|
|
// Horizontal positioning
|
|
|
|
auto alignWidth = 0;
|
2023-02-26 17:04:47 +01:00
|
|
|
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 );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
QRectF QskRadioBoxSkinlet::textRect( const QskRadioBox* radioBox,
|
|
|
|
const QRectF& rect, int index ) const
|
|
|
|
{
|
|
|
|
using Q = QskRadioBox;
|
2023-02-25 22:24:39 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
const auto text = radioBox->items()[ index ];
|
|
|
|
if( text.isEmpty() )
|
|
|
|
return QRectF();
|
2023-02-25 22:24:39 +01:00
|
|
|
|
2023-02-08 21:49:04 +01:00
|
|
|
QRectF result = rect;
|
2023-02-26 17:04:47 +01:00
|
|
|
auto spacing = radioBox->spacingHint( Q::Panel );
|
|
|
|
auto lh = lineHeight( radioBox );
|
|
|
|
const auto textMargins = radioBox->marginHint( Q::Text );
|
|
|
|
const auto font = radioBox->effectiveFont( Q::Text );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
|
|
|
result.moveTop( index * ( lh + spacing )
|
2023-02-26 17:04:47 +01:00
|
|
|
+ lh - QFontMetricsF( font ).height() + textMargins.top() );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-15 00:20:19 +01:00
|
|
|
result.setHeight( lh );
|
2023-02-25 22:25:43 +01:00
|
|
|
result.setWidth( qskHorizontalAdvance( font, text ) );
|
2023-02-11 23:15:07 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
const auto button = buttonRect( radioBox, Q::Button, rect, index );
|
|
|
|
const auto buttonsMargins = radioBox->marginHint( Q::Button );
|
|
|
|
const auto buttonWidth = button.marginsAdded( buttonsMargins ).width();
|
2023-02-25 22:25:43 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
if( radioBox->layoutMirroring() )
|
|
|
|
{
|
|
|
|
result.moveLeft( rect.width() - textMargins.right()
|
|
|
|
- result.width() - buttonWidth);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result.moveLeft( buttonWidth + textMargins.left() );
|
2023-02-08 21:49:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF QskRadioBoxSkinlet::sampleRect( const QskSkinnable* skinnable,
|
2023-02-26 17:04:47 +01:00
|
|
|
const QRectF& rect, QskAspect::Subcontrol subcontrol, int index ) const
|
|
|
|
{
|
|
|
|
using Q = QskRadioBox;
|
2023-02-25 23:31:29 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
auto radioBox = static_cast< const QskRadioBox* >( skinnable );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
if( subcontrol == Q::Text )
|
|
|
|
return textRect( radioBox, rect, index );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
return buttonRect( radioBox, subcontrol, rect, index);
|
2023-02-08 21:49:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
QskAspect::States QskRadioBoxSkinlet::sampleStates( const QskSkinnable* skinnable,
|
2023-02-26 17:04:47 +01:00
|
|
|
QskAspect::Subcontrol subControl, int index ) const
|
|
|
|
{
|
|
|
|
auto radioBox = static_cast< const QskRadioBox* >( skinnable );
|
|
|
|
auto states = Inherited::sampleStates( skinnable, subControl, index );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
return states | statesForIndex( radioBox, index );
|
2023-02-08 21:49:04 +01:00
|
|
|
}
|
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
QSGNode* QskRadioBoxSkinlet::updateSampleNode( const QskSkinnable* skinnable,
|
|
|
|
QskAspect::Subcontrol subcontrol, int index, QSGNode* node ) const
|
|
|
|
{
|
|
|
|
using Q = QskRadioBox;
|
2023-02-25 23:31:29 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
auto radioBox = static_cast< const QskRadioBox* >( skinnable );
|
2023-02-08 21:49:04 +01:00
|
|
|
|
2023-02-26 17:04:47 +01:00
|
|
|
auto rect = sampleRect( skinnable, radioBox->contentsRect(), subcontrol, index );
|
|
|
|
|
|
|
|
if( subcontrol == Q::Text )
|
|
|
|
{
|
|
|
|
return QskSkinlet::updateTextNode( radioBox, node, rect, Qt::AlignLeft,
|
|
|
|
radioBox->items()[ index ], subcontrol );
|
|
|
|
}
|
|
|
|
else if ( subcontrol == Q::Button )
|
|
|
|
{
|
|
|
|
return QskSkinlet::updateBoxNode( radioBox, node, rect, subcontrol );
|
|
|
|
}
|
|
|
|
else if( subcontrol == Q::Symbol )
|
|
|
|
{
|
2023-02-08 21:49:04 +01:00
|
|
|
auto symbol = QskStandardSymbol::NoSymbol;
|
2023-02-26 17:04:47 +01:00
|
|
|
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 );
|
2023-02-08 21:49:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
2023-02-20 22:13:14 +01:00
|
|
|
|
|
|
|
#include "moc_QskRadioBoxSkinlet.cpp"
|