309 lines
8.8 KiB
C++
309 lines
8.8 KiB
C++
/******************************************************************************
|
|
* QSkinny - Copyright (C) 2016 Uwe Rathmann
|
|
* This file may be used under the terms of the QSkinny License, Version 1.0
|
|
*****************************************************************************/
|
|
|
|
#include "QskRadioBoxSkinlet.h"
|
|
|
|
#include "QskRadioBox.h"
|
|
#include "QskSkinStateChanger.h"
|
|
#include "QskFunctions.h"
|
|
|
|
#include <qfontmetrics.h>
|
|
|
|
namespace
|
|
{
|
|
QskAspect::States statesForIndex( const QskRadioBox* radioBox, int index )
|
|
{
|
|
using Q = QskRadioBox;
|
|
|
|
auto states = radioBox->skinStates();
|
|
|
|
if( radioBox->selectedIndex() == index )
|
|
states |= Q::Selected;
|
|
|
|
if( radioBox->pressedIndex() == index )
|
|
states |= Q::Pressed;
|
|
|
|
if( radioBox->positionHint( Q::Ripple ) == index )
|
|
states |= Q::Focused;
|
|
|
|
return states;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
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, IndicatorRole, TextRole, RippleRole } );
|
|
}
|
|
|
|
QskRadioBoxSkinlet::~QskRadioBoxSkinlet()
|
|
{
|
|
}
|
|
|
|
QRectF QskRadioBoxSkinlet::subControlRect( const QskSkinnable* skinnable,
|
|
const QRectF& contentsRect, QskAspect::Subcontrol subcontrol ) const
|
|
{
|
|
using Q = QskRadioBox;
|
|
|
|
auto radioBox = static_cast< const QskRadioBox* >( skinnable );
|
|
|
|
if( subcontrol == Q::Ripple )
|
|
return rippleRect( radioBox, contentsRect );
|
|
|
|
return contentsRect;
|
|
}
|
|
|
|
QSGNode* QskRadioBoxSkinlet::updateSubNode( const QskSkinnable* skinnable,
|
|
quint8 nodeRole, QSGNode* node ) const
|
|
{
|
|
using Q = QskRadioBox;
|
|
|
|
switch( nodeRole )
|
|
{
|
|
case PanelRole:
|
|
return updateBoxNode( skinnable, node, Q::Panel );
|
|
|
|
case ButtonRole:
|
|
return updateSeriesNode( skinnable, Q::Button, node );
|
|
|
|
case IndicatorRole:
|
|
return updateSeriesNode( skinnable, Q::Indicator, node );
|
|
|
|
case TextRole:
|
|
return updateSeriesNode( skinnable, Q::Text, node );
|
|
|
|
case RippleRole:
|
|
{
|
|
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 );
|
|
}
|
|
};
|
|
|
|
return Inherited::updateSubNode( skinnable, nodeRole, node );
|
|
}
|
|
|
|
int QskRadioBoxSkinlet::sampleCount(
|
|
const QskSkinnable* skinnable, QskAspect::Subcontrol ) const
|
|
{
|
|
const auto radioBox = static_cast< const QskRadioBox* >( skinnable );
|
|
return radioBox->options().count();
|
|
}
|
|
|
|
QRectF QskRadioBoxSkinlet::rippleRect(
|
|
const QskRadioBox* radioBox, const QRectF& rect ) const
|
|
{
|
|
using Q = QskRadioBox;
|
|
|
|
auto index = radioBox->positionHint( Q::Ripple );
|
|
if( index < 0 )
|
|
return QRectF();
|
|
|
|
QRectF r;
|
|
r.setSize( radioBox->strutSizeHint( Q::Ripple ) );
|
|
|
|
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 QRectF& rect, int index ) const
|
|
{
|
|
using Q = QskRadioBox;
|
|
|
|
auto r = lineRect( radioBox, rect, index );
|
|
r = r.marginsRemoved( radioBox->paddingHint( Q::Panel ) );
|
|
|
|
auto alignment = radioBox->alignmentHint( Q::Button, 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 ) );
|
|
|
|
return qskAlignedRectF( r, size.width(), size.height(), alignment );
|
|
}
|
|
|
|
QRectF QskRadioBoxSkinlet::textRect( 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 ) );
|
|
|
|
const auto buttonRect = sampleRect( radioBox, rect, Q::Button, index );
|
|
const auto spacing = lineSpacing( radioBox );
|
|
|
|
if ( !radioBox->layoutMirroring() )
|
|
r.setLeft( buttonRect.right() + spacing );
|
|
else
|
|
r.setRight( buttonRect.left() - spacing );
|
|
|
|
return r;
|
|
}
|
|
|
|
QRectF QskRadioBoxSkinlet::sampleRect( const QskSkinnable* skinnable,
|
|
const QRectF& rect, QskAspect::Subcontrol subControl, int index ) const
|
|
{
|
|
using Q = QskRadioBox;
|
|
|
|
auto radioBox = static_cast< const QskRadioBox* >( skinnable );
|
|
|
|
if( subControl == Q::Text )
|
|
return textRect( radioBox, 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,
|
|
QskAspect::Subcontrol subControl, int index ) const
|
|
{
|
|
auto radioBox = static_cast< const QskRadioBox* >( skinnable );
|
|
auto states = Inherited::sampleStates( skinnable, subControl, index );
|
|
|
|
return states | statesForIndex( radioBox, index );
|
|
}
|
|
|
|
QSGNode* QskRadioBoxSkinlet::updateSampleNode( const QskSkinnable* skinnable,
|
|
QskAspect::Subcontrol subcontrol, int index, QSGNode* node ) const
|
|
{
|
|
using Q = QskRadioBox;
|
|
|
|
auto radioBox = static_cast< const QskRadioBox* >( skinnable );
|
|
|
|
auto rect = sampleRect( skinnable, radioBox->contentsRect(), subcontrol, index );
|
|
|
|
if( subcontrol == Q::Text )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
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"
|