qskinny/src/controls/QskRadioBoxSkinlet.cpp

299 lines
8.5 KiB
C++
Raw Normal View History

2023-02-26 17:04:47 +01:00
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
2023-04-06 09:23:37 +02:00
* SPDX-License-Identifier: BSD-3-Clause
2023-02-26 17:04:47 +01:00
*****************************************************************************/
2023-02-08 21:49:04 +01:00
#include "QskRadioBoxSkinlet.h"
#include "QskRadioBox.h"
#include "QskFunctions.h"
2023-02-26 17:04:47 +01:00
#include <qfontmetrics.h>
2023-04-04 09:05:16 +02:00
#include <qmath.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
{
QSizeF buttonSizeHint( const QskSkinnable* skinnable,
const QFontMetricsF& fm, const QString& text )
2023-02-26 17:04:47 +01:00
{
using Q = QskRadioBox;
auto hint = skinnable->strutSizeHint( Q::CheckIndicatorPanel );
2023-02-26 17:04:47 +01:00
hint.rwidth() += skinnable->spacingHint( Q::Button )
+ qskHorizontalAdvance( fm, text );
hint.rheight() = qMax( hint.height(), fm.height() );
2023-02-26 17:04:47 +01:00
hint = hint.grownBy( skinnable->paddingHint( Q::Button ) );
hint = hint.expandedTo( skinnable->strutSizeHint( Q::Button ) );
2023-02-26 17:04:47 +01:00
return hint;
}
QSizeF buttonSizeHint( const QskRadioBox* radioBox, int index )
{
const QFontMetrics fm( radioBox->effectiveFont( QskRadioBox::Text ) );
return buttonSizeHint( radioBox, fm, radioBox->optionAt( index ) );
}
2023-02-26 17:04:47 +01:00
}
QskRadioBoxSkinlet::QskRadioBoxSkinlet( QskSkin* )
{
setNodeRoles( { PanelRole, ButtonRole, CheckPanelRole,
CheckIndicatorRole, TextRole, RippleRole } );
2023-02-26 17:04:47 +01:00
}
2023-02-26 17:04:47 +01:00
QskRadioBoxSkinlet::~QskRadioBoxSkinlet()
{
}
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
using Q = QskRadioBox;
auto radioBox = static_cast< const QskRadioBox* >( skinnable );
2023-02-08 21:49:04 +01:00
if( subcontrol == Q::Ripple )
return rippleRect( radioBox, contentsRect );
2023-02-08 21:49:04 +01:00
return contentsRect;
2023-02-26 17:04:47 +01:00
}
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 CheckPanelRole:
return updateSeriesNode( skinnable, Q::CheckIndicatorPanel, node );
case CheckIndicatorRole:
return updateSeriesNode( skinnable, Q::CheckIndicator, node );
2023-02-26 17:04:47 +01:00
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-03-03 13:01:07 +01:00
return updateBoxNode( skinnable, node, Q::Ripple );
2023-04-04 08:49:11 +02:00
}
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->options().count();
}
2023-02-26 17:04:47 +01:00
QRectF QskRadioBoxSkinlet::rippleRect(
const QskRadioBox* radioBox, const QRectF& rect ) const
{
using Q = QskRadioBox;
2023-04-04 09:05:16 +02:00
const auto index = qFloor( radioBox->positionHint( Q::Ripple ) );
if( index < 0 )
2023-02-26 17:04:47 +01:00
return QRectF();
QRectF r;
r.setSize( radioBox->strutSizeHint( Q::Ripple ) );
2023-02-26 17:04:47 +01:00
if ( !r.isEmpty() )
{
const auto checkBoxRect = sampleRect(
radioBox, rect, Q::CheckIndicatorPanel, index );
r.moveCenter( checkBoxRect.center() );
}
2023-02-26 17:04:47 +01:00
return r;
}
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
2023-02-26 17:04:47 +01:00
{
using Q = QskRadioBox;
auto r = sampleRect( radioBox, rect, Q::Button, index );
r = radioBox->innerBox( Q::Button, r );
auto alignment = radioBox->alignmentHint( Q::CheckIndicatorPanel, Qt::AlignCenter );
alignment &= Qt::AlignVertical_Mask;
alignment |= radioBox->layoutMirroring() ? Qt::AlignRight : Qt::AlignLeft;
2023-02-26 17:04:47 +01:00
auto size = radioBox->strutSizeHint( Q::CheckIndicatorPanel );
size = size.grownBy( radioBox->marginHint( Q::CheckIndicatorPanel ) );
return qskAlignedRectF( r, size.width(), size.height(), alignment );
2023-02-08 21:49:04 +01:00
}
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
auto r = sampleRect( radioBox, rect, Q::Button, index );
r = radioBox->innerBox( Q::Button, r );
2023-02-08 21:49:04 +01:00
const auto checkPanelRect = sampleRect(
radioBox, rect, Q::CheckIndicatorPanel, index );
const auto spacing = radioBox->spacingHint( Q::Button );
2023-02-08 21:49:04 +01:00
if ( !radioBox->layoutMirroring() )
r.setLeft( checkPanelRect.right() + spacing );
2023-02-26 17:04:47 +01:00
else
r.setRight( checkPanelRect.left() - spacing );
2023-02-08 21:49:04 +01:00
return r;
2023-02-08 21:49:04 +01:00
}
QRectF QskRadioBoxSkinlet::sampleRect( const QskSkinnable* skinnable,
const QRectF& rect, QskAspect::Subcontrol subControl, int index ) const
2023-02-26 17:04:47 +01:00
{
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
if( subControl == Q::Text )
2023-02-26 17:04:47 +01:00
return textRect( radioBox, rect, index );
2023-02-08 21:49:04 +01:00
if( subControl == Q::Button )
return buttonRect( radioBox, rect, index );
if( subControl == Q::CheckIndicatorPanel )
return checkPanelRect( radioBox, rect, index );
if( subControl == Q::CheckIndicator )
{
auto r = sampleRect( radioBox, rect, Q::CheckIndicatorPanel, index );
r = r.marginsRemoved( radioBox->paddingHint( Q::CheckIndicatorPanel ) );
return r;
}
return QRectF();
2023-02-08 21:49:04 +01:00
}
2023-03-03 13:01:07 +01:00
QskAspect::States QskRadioBoxSkinlet::sampleStates(
const QskSkinnable* skinnable, QskAspect::Subcontrol, int index ) const
2023-02-26 17:04:47 +01:00
{
2023-03-03 13:01:07 +01:00
using Q = QskRadioBox;
2023-02-26 17:04:47 +01:00
auto radioBox = static_cast< const QskRadioBox* >( skinnable );
2023-02-08 21:49:04 +01:00
2023-03-03 13:01:07 +01:00
auto states = radioBox->skinStates();
if( radioBox->selectedIndex() == index )
states |= Q::Selected;
if( radioBox->pressedIndex() == index )
states |= Q::Pressed;
#if 1
if( radioBox->positionHint( Q::Ripple ) == index )
states |= Q::Focused;
else
states &= ~Q::Focused;
#endif
return states;
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 )
{
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->optionAt( index ), subcontrol );
2023-02-26 17:04:47 +01:00
}
if ( subcontrol == Q::CheckIndicatorPanel || subcontrol == Q::CheckIndicator )
return updateBoxNode( radioBox, node, rect, subcontrol );
return node;
}
QSizeF QskRadioBoxSkinlet::sizeHint( const QskSkinnable* skinnable,
Qt::SizeHint which, const QSizeF& constraint ) const
{
using Q = QskRadioBox;
if ( which != Qt::PreferredSize )
return QSizeF();
if ( constraint.width() >= 0.0 )
2023-02-26 17:04:47 +01:00
{
// heightForWidth would make sense when word wrapping is enabled TODO ...
2023-02-26 17:04:47 +01:00
}
const auto radioBox = static_cast< const QskRadioBox* >( skinnable );
const QFontMetrics fm( radioBox->effectiveFont( QskRadioBox::Text ) );
2023-02-26 17:04:47 +01:00
qreal w = 0.0;
qreal h = 0.0;
2023-02-26 17:04:47 +01:00
const auto options = radioBox->options();
for( const auto& option : options )
{
const auto buttonSize = buttonSizeHint( radioBox, fm, option );
2023-02-26 17:04:47 +01:00
w = qMax( w, buttonSize.width() );
h += buttonSize.height();
}
2023-02-26 17:04:47 +01:00
if ( auto count = radioBox->options().count() )
h += ( count - 1 ) * skinnable->spacingHint( Q::Panel );
2023-02-26 17:04:47 +01:00
QSizeF hint( w, h );
hint = hint.grownBy( skinnable->paddingHint( Q::Panel ) );
hint = hint.expandedTo( skinnable->strutSizeHint( Q::Panel ) );
2023-02-08 21:49:04 +01:00
return hint;
2023-02-08 21:49:04 +01:00
}
2023-02-20 22:13:14 +01:00
#include "moc_QskRadioBoxSkinlet.cpp"