From ca1951d9a6836d479bc7a6df59f0652f614d5e3e Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 23 Oct 2024 15:20:42 +0200 Subject: [PATCH 1/9] using QskBoxRectangleNode for shadows without blur --- src/nodes/QskBoxNode.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/nodes/QskBoxNode.cpp b/src/nodes/QskBoxNode.cpp index 1e7363fd..e0b82300 100644 --- a/src/nodes/QskBoxNode.cpp +++ b/src/nodes/QskBoxNode.cpp @@ -20,6 +20,7 @@ namespace enum Role { ShadowRole, + ShadowFillRole, BoxRole, FillRole }; @@ -27,14 +28,15 @@ namespace static void qskUpdateChildren( QSGNode* parentNode, quint8 role, QSGNode* node ) { - static const QVector< quint8 > roles = { ShadowRole, BoxRole, FillRole }; + static const QVector< quint8 > roles = + { ShadowRole, ShadowFillRole, BoxRole, FillRole }; auto oldNode = QskSGNode::findChildNode( parentNode, role ); QskSGNode::replaceChildNode( roles, role, parentNode, oldNode, node ); } template< typename Node > -inline Node* qskNode( QSGNode* parentNode, quint8 role ) +static inline Node* qskNode( QSGNode* parentNode, quint8 role ) { using namespace QskSGNode; @@ -65,6 +67,7 @@ void QskBoxNode::updateNode( const QRectF& rect, using namespace QskSGNode; QskBoxShadowNode* shadowNode = nullptr; + QskBoxRectangleNode* shadowFillNode = nullptr; QskBoxRectangleNode* rectNode = nullptr; QskBoxRectangleNode* fillNode = nullptr; @@ -78,9 +81,20 @@ void QskBoxNode::updateNode( const QRectF& rect, if ( hasShadow ) { - shadowNode = qskNode< QskBoxShadowNode >( this, ShadowRole ); - shadowNode->setShadowData( shadowMetrics.shadowRect( rect ), - shape, shadowMetrics.blurRadius(), shadowColor ); + const auto shadowRect = shadowMetrics.shadowRect( rect ); + const auto blurRadius = shadowMetrics.blurRadius(); + + if ( blurRadius <= 0.0 ) + { + // QskBoxRectangleNode allows scene graph batching + shadowFillNode = qskNode< QskBoxRectangleNode >( this, ShadowFillRole ); + shadowFillNode->updateFilling( shadowRect, shape, shadowColor ); + } + else + { + shadowNode = qskNode< QskBoxShadowNode >( this, ShadowRole ); + shadowNode->setShadowData( shadowRect, shape, blurRadius, shadowColor ); + } } if ( hasBorder || hasFilling ) @@ -95,7 +109,7 @@ void QskBoxNode::updateNode( const QRectF& rect, if ( !doCombine ) fillNode = qskNode< QskBoxRectangleNode >( this, FillRole ); } - + if ( fillNode ) { rectNode->updateBorder( rect, shape, borderMetrics, borderColors ); @@ -109,6 +123,7 @@ void QskBoxNode::updateNode( const QRectF& rect, } qskUpdateChildren( this, ShadowRole, shadowNode ); + qskUpdateChildren( this, ShadowFillRole, shadowFillNode ); qskUpdateChildren( this, BoxRole, rectNode ); qskUpdateChildren( this, FillRole, fillNode ); } From 325c6214856148f794c50ac7b16c8a2558e05d1d Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 24 Oct 2024 09:59:32 +0200 Subject: [PATCH 2/9] using better English --- src/common/QskBoxBorderMetrics.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/common/QskBoxBorderMetrics.cpp b/src/common/QskBoxBorderMetrics.cpp index 1272f87e..fe010464 100644 --- a/src/common/QskBoxBorderMetrics.cpp +++ b/src/common/QskBoxBorderMetrics.cpp @@ -29,7 +29,7 @@ static void qskRegisterBoxBorderMetrics() Q_CONSTRUCTOR_FUNCTION( qskRegisterBoxBorderMetrics ) -static inline qreal qskAbsoluted( qreal length, qreal percentage ) +static inline qreal qskToAbsolute( qreal length, qreal percentage ) { // 100% means -> 0.5 of length percentage = qBound( 0.0, percentage, 100.0 ); @@ -69,10 +69,10 @@ QskBoxBorderMetrics QskBoxBorderMetrics::toAbsolute( const QSizeF& size ) const } else { - w.setLeft( qskAbsoluted( size.width(), w.left() ) ); - w.setTop( qskAbsoluted( size.height(), w.top() ) ); - w.setRight( qskAbsoluted( size.width(), w.right() ) ); - w.setBottom( qskAbsoluted( size.height(), w.bottom() ) ); + w.setLeft( qskToAbsolute( size.width(), w.left() ) ); + w.setTop( qskToAbsolute( size.height(), w.top() ) ); + w.setRight( qskToAbsolute( size.width(), w.right() ) ); + w.setBottom( qskToAbsolute( size.height(), w.bottom() ) ); } absoluted.m_sizeMode = Qt::AbsoluteSize; From 1258c175348c395747876381e9bf8762dce4b97d Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 24 Oct 2024 10:16:51 +0200 Subject: [PATCH 3/9] several fixes concerning box shadows --- src/controls/QskSkinlet.cpp | 22 +++++----------------- src/nodes/QskBoxNode.cpp | 25 +++++++++++++------------ src/nodes/QskBoxRectangleNode.cpp | 14 +++++++++----- src/nodes/QskBoxShadowNode.cpp | 4 +++- 4 files changed, 30 insertions(+), 35 deletions(-) diff --git a/src/controls/QskSkinlet.cpp b/src/controls/QskSkinlet.cpp index e9a90d9e..3a258445 100644 --- a/src/controls/QskSkinlet.cpp +++ b/src/controls/QskSkinlet.cpp @@ -196,26 +196,14 @@ static inline QSGNode* qskUpdateBoxNode( const QskBoxBorderColors& borderColors, const QskGradient& gradient, const QskShadowMetrics& shadowMetrics, const QColor& shadowColor ) { - if ( rect.isEmpty() ) + if ( rect.isEmpty() || !qskIsBoxVisible( borderMetrics, borderColors, gradient ) ) return nullptr; - const auto size = rect.size(); + auto boxNode = QskSGNode::ensureNode< QskBoxNode >( node ); + boxNode->updateNode( rect, shape, borderMetrics, + borderColors, gradient, shadowMetrics, shadowColor ); - const auto absoluteMetrics = borderMetrics.toAbsolute( size ); - - if ( qskIsBoxVisible( absoluteMetrics, borderColors, gradient ) ) - { - const auto absoluteShape = shape.toAbsolute( size ); - const auto absoluteShadowMetrics = shadowMetrics.toAbsolute( size ); - - auto boxNode = QskSGNode::ensureNode< QskBoxNode >( node ); - boxNode->updateNode( rect, absoluteShape, absoluteMetrics, - borderColors, gradient, absoluteShadowMetrics, shadowColor ); - - return boxNode; - } - - return nullptr; + return boxNode; } static inline QSGNode* qskUpdateArcNode( diff --git a/src/nodes/QskBoxNode.cpp b/src/nodes/QskBoxNode.cpp index e0b82300..78d21d59 100644 --- a/src/nodes/QskBoxNode.cpp +++ b/src/nodes/QskBoxNode.cpp @@ -13,6 +13,7 @@ #include "QskShadowMetrics.h" #include "QskBoxBorderMetrics.h" #include "QskBoxBorderColors.h" +#include "QskBoxShapeMetrics.h" #include "QskRgbValue.h" namespace @@ -60,7 +61,7 @@ QskBoxNode::~QskBoxNode() } void QskBoxNode::updateNode( const QRectF& rect, - const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics, + const QskBoxShapeMetrics& shapeMetrics, const QskBoxBorderMetrics& borderMetrics, const QskBoxBorderColors& borderColors, const QskGradient& gradient, const QskShadowMetrics& shadowMetrics, const QColor& shadowColor ) { @@ -75,25 +76,24 @@ void QskBoxNode::updateNode( const QRectF& rect, { const auto hasFilling = gradient.isVisible(); const auto hasBorder = !borderMetrics.isNull() && borderColors.isVisible(); - - const auto hasShadow = hasFilling && !shadowMetrics.isNull() - && QskRgb::isVisible( shadowColor ); + const auto hasShadow = !shadowMetrics.isNull() && QskRgb::isVisible( shadowColor ); if ( hasShadow ) { - const auto shadowRect = shadowMetrics.shadowRect( rect ); - const auto blurRadius = shadowMetrics.blurRadius(); + const auto shadow = shadowMetrics.toAbsolute( rect.size() ); + const auto shadowRect = shadow.shadowRect( rect ); - if ( blurRadius <= 0.0 ) + if ( shadow.blurRadius() <= 0.0 ) { // QskBoxRectangleNode allows scene graph batching shadowFillNode = qskNode< QskBoxRectangleNode >( this, ShadowFillRole ); - shadowFillNode->updateFilling( shadowRect, shape, shadowColor ); + shadowFillNode->updateFilling( shadowRect, shapeMetrics, shadowColor ); } else { shadowNode = qskNode< QskBoxShadowNode >( this, ShadowRole ); - shadowNode->setShadowData( shadowRect, shape, blurRadius, shadowColor ); + shadowNode->setShadowData( shadowRect, + shapeMetrics, shadow.blurRadius(), shadowColor ); } } @@ -112,12 +112,13 @@ void QskBoxNode::updateNode( const QRectF& rect, if ( fillNode ) { - rectNode->updateBorder( rect, shape, borderMetrics, borderColors ); - fillNode->updateFilling( rect, shape, borderMetrics, gradient ); + rectNode->updateBorder( rect, shapeMetrics, borderMetrics, borderColors ); + fillNode->updateFilling( rect, shapeMetrics, borderMetrics, gradient ); } else { - rectNode->updateBox( rect, shape, borderMetrics, borderColors, gradient ); + rectNode->updateBox( rect, shapeMetrics, + borderMetrics, borderColors, gradient ); } } } diff --git a/src/nodes/QskBoxRectangleNode.cpp b/src/nodes/QskBoxRectangleNode.cpp index 461e558b..62fcb8c5 100644 --- a/src/nodes/QskBoxRectangleNode.cpp +++ b/src/nodes/QskBoxRectangleNode.cpp @@ -12,7 +12,7 @@ #include "QskGradientDirection.h" #include "QskFillNodePrivate.h" -static inline bool qskHasBorder( +static inline bool qskHasBorder( const QskBoxBorderMetrics& metrics, const QskBoxBorderColors& colors ) { return !metrics.isNull() && colors.isVisible(); @@ -142,7 +142,7 @@ void QskBoxRectangleNode::updateFilling( const QRectF& rect, } void QskBoxRectangleNode::updateBorder( const QRectF& rect, - const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics, + const QskBoxShapeMetrics& shapeMetrics, const QskBoxBorderMetrics& borderMetrics, const QskBoxBorderColors& borderColors ) { Q_D( QskBoxRectangleNode ); @@ -153,6 +153,8 @@ void QskBoxRectangleNode::updateBorder( const QRectF& rect, return; } + const auto shape = shapeMetrics.toAbsolute( rect.size() ); + const bool coloredGeometry = hasHint( PreferColoredGeometry ) || !borderColors.isMonochrome(); @@ -189,7 +191,7 @@ void QskBoxRectangleNode::updateBorder( const QRectF& rect, } void QskBoxRectangleNode::updateBox( const QRectF& rect, - const QskBoxShapeMetrics& shape, const QskBoxBorderMetrics& borderMetrics, + const QskBoxShapeMetrics& shapeMetrics, const QskBoxBorderMetrics& borderMetrics, const QskBoxBorderColors& borderColors, const QskGradient& gradient ) { Q_D( QskBoxRectangleNode ); @@ -205,6 +207,8 @@ void QskBoxRectangleNode::updateBox( const QRectF& rect, if ( hasFill && hasBorder ) { + const auto shape = shapeMetrics.toAbsolute( rect.size() ); + const bool isDirty = d->updateMetrics( rect, shape, borderMetrics ) || d->updateColors( borderColors, gradient ) || !isGeometryColored(); @@ -232,11 +236,11 @@ void QskBoxRectangleNode::updateBox( const QRectF& rect, } else if ( hasFill ) { - updateFilling( rect, shape, borderMetrics, gradient ); + updateFilling( rect, shapeMetrics, borderMetrics, gradient ); } else if ( hasBorder ) { - updateBorder( rect, shape, borderMetrics, borderColors ); + updateBorder( rect, shapeMetrics, borderMetrics, borderColors ); } else { diff --git a/src/nodes/QskBoxShadowNode.cpp b/src/nodes/QskBoxShadowNode.cpp index cfb0099e..50b42229 100644 --- a/src/nodes/QskBoxShadowNode.cpp +++ b/src/nodes/QskBoxShadowNode.cpp @@ -269,7 +269,7 @@ QskBoxShadowNode::~QskBoxShadowNode() } void QskBoxShadowNode::setShadowData( - const QRectF& rect, const QskBoxShapeMetrics& shape, + const QRectF& rect, const QskBoxShapeMetrics& shapeMetrics, qreal blurRadius, const QColor& color ) { Q_D( QskBoxShadowNode ); @@ -299,6 +299,8 @@ void QskBoxShadowNode::setShadowData( } { + const auto shape = shapeMetrics.toAbsolute( rect.size() ); + const float t = std::min( d->rect.width(), d->rect.height() ); const float r1 = shape.radius( Qt::BottomRightCorner ).width(); From 635cd3c91f9a0b11a7b1003997bad242952e068a Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 24 Oct 2024 12:15:02 +0200 Subject: [PATCH 4/9] QskRadioBox: using the shadow ( instead of an additional Halo subcontrol ) --- designsystems/fusion/QskFusionSkin.cpp | 2 +- designsystems/material3/QskMaterial3Skin.cpp | 54 +++++++++++++++++--- src/controls/QskRadioBox.cpp | 9 ++-- src/controls/QskRadioBox.h | 2 +- src/controls/QskRadioBoxSkinlet.cpp | 40 ++------------- src/controls/QskRadioBoxSkinlet.h | 3 -- 6 files changed, 57 insertions(+), 53 deletions(-) diff --git a/designsystems/fusion/QskFusionSkin.cpp b/designsystems/fusion/QskFusionSkin.cpp index 50188ee8..5feceb48 100644 --- a/designsystems/fusion/QskFusionSkin.cpp +++ b/designsystems/fusion/QskFusionSkin.cpp @@ -489,7 +489,7 @@ void Editor::setupRadioBox() setStrutSize( Q::CheckIndicatorPanel, 20_dp, 20_dp ); - for ( auto subControl : { Q::CheckIndicatorPanel, Q::CheckIndicator, Q::Halo } ) + for ( auto subControl : { Q::CheckIndicatorPanel, Q::CheckIndicator } ) setBoxShape( subControl, 100, Qt::RelativeSize ); // circular setBoxBorderMetrics( Q::CheckIndicatorPanel, 1_dp ); diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 12a8aa53..d54a67a4 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -480,9 +480,17 @@ void Editor::setupRadioBox() setSpacing( Q::Button, 10_dp ); setStrutSize( Q::CheckIndicatorPanel, 20_dp, 20_dp ); - setStrutSize( Q::Halo, 40_dp, 40_dp ); - for ( auto subControl : { Q::CheckIndicatorPanel, Q::CheckIndicator, Q::Halo } ) + for ( const auto state1 : { Q::Hovered, Q::Focused, Q::Pressed } ) + { + for ( const auto state2 : { A::NoState, Q::Selected } ) + { + const auto aspect = Q::CheckIndicatorPanel | state1 | state2; + setShadowMetrics( aspect, { 10_dp, 0 } ); + } + } + + for ( auto subControl : { Q::CheckIndicatorPanel, Q::CheckIndicator } ) setBoxShape( subControl, 100, Qt::RelativeSize ); // circular setBoxBorderMetrics( Q::CheckIndicatorPanel, 2_dp ); @@ -494,11 +502,41 @@ void Editor::setupRadioBox() setColor( Q::Text, m_pal.onBackground ); setColor( Q::Text | Q::Disabled, m_pal.onSurface38 ); - setColor( Q::Halo, stateLayerColor( m_pal.onSurface, m_pal.focusOpacity ) ); - setColor( Q::Halo | Q::Selected, - stateLayerColor( m_pal.primary, m_pal.focusOpacity ) ); + for ( const auto state1 : { Q::Hovered, Q::Focused, Q::Pressed } ) + { + for ( const auto state2 : { A::NoState, Q::Selected } ) + { + const auto aspect = Q::CheckIndicatorPanel | state1 | state2; - setColor( Q::CheckIndicator, Qt::transparent); + QRgb rgb; + + if ( state1 == Q::Hovered ) + { + rgb = ( state2 == Q::Selected ) ? m_pal.primary8 : m_pal.onSurface8; + rgb = stateLayerColor( rgb, m_pal.hoverOpacity ); + + setShadowColor( aspect, rgb ); + } + else if ( state1 == Q::Focused ) + { + rgb = ( state2 == Q::Selected ) ? m_pal.primary12 : m_pal.onSurface12; + rgb = stateLayerColor( rgb, m_pal.focusOpacity ); + + setShadowColor( aspect, rgb ); + } + else + { + rgb = ( state2 == Q::Selected ) ? m_pal.onSurface12 : m_pal.primary12; + rgb = stateLayerColor( rgb, m_pal.pressedOpacity ); + + setShadowColor( aspect, rgb ); + setShadowColor( aspect | Q::Focused, rgb ); + } + + } + } + + setColor( Q::CheckIndicator, Qt::transparent ); setColor( Q::CheckIndicator | Q::Selected, m_pal.primary ); setColor( Q::CheckIndicator | Q::Selected | Q::Disabled, m_pal.onSurface38 ); @@ -509,7 +547,7 @@ void Editor::setupRadioBox() setBoxBorderColors( Q::CheckIndicatorPanel | Q::Disabled | Q::Selected, m_pal.onSurface38 ); - setAnimation( Q::Halo | A::Metric | A::Position, qskDuration ); + setAnimation( Q::CheckIndicator | A::Metric | A::Position, qskDuration ); } void Editor::setupFocusIndicator() @@ -1484,7 +1522,7 @@ static inline QFont createFont( int pointSize, int lineHeight, } checkFont = false; } - + font.setPixelSize( pixelSize ); if ( spacing > 0.0 ) diff --git a/src/controls/QskRadioBox.cpp b/src/controls/QskRadioBox.cpp index e7076222..7cde8fc5 100644 --- a/src/controls/QskRadioBox.cpp +++ b/src/controls/QskRadioBox.cpp @@ -13,7 +13,6 @@ QSK_SUBCONTROL( QskRadioBox, Button ) QSK_SUBCONTROL( QskRadioBox, CheckIndicatorPanel ) QSK_SUBCONTROL( QskRadioBox, CheckIndicator ) QSK_SUBCONTROL( QskRadioBox, Text ) -QSK_SUBCONTROL( QskRadioBox, Halo ) QSK_STATE( QskRadioBox, Selected, QskAspect::FirstUserState << 1 ) QSK_STATE( QskRadioBox, Pressed, QskAspect::FirstUserState << 2 ) @@ -38,7 +37,7 @@ QskRadioBox::QskRadioBox( QQuickItem* parent ) setFocusPolicy( Qt::StrongFocus ); setAcceptedMouseButtons( Qt::LeftButton ); - setPositionHint( Halo, -1 ); + setPositionHint( CheckIndicator, -1 ); setAcceptHoverEvents( true ); } @@ -182,7 +181,7 @@ void QskRadioBox::keyPressEvent( QKeyEvent* event ) { setFocusedIndex( nextTabIndex ); - const auto aspect = Halo | QskAspect::Metric | QskAspect::Position; + const auto aspect = CheckIndicator | QskAspect::Metric | QskAspect::Position; const auto hint = animationHint( aspect | skinStates() ); if( hint.isValid() ) @@ -277,7 +276,7 @@ void QskRadioBox::setHoveredIndex( int index ) return; m_data->hoveredIndex = index; - setPositionHint( Halo | Hovered, index ); + setPositionHint( CheckIndicator | Hovered, index ); update(); } @@ -288,7 +287,7 @@ void QskRadioBox::setFocusedIndex( int index ) return; m_data->focusedIndex = index; - setPositionHint( Halo, index ); + setPositionHint( CheckIndicator, index ); update(); diff --git a/src/controls/QskRadioBox.h b/src/controls/QskRadioBox.h index de65c457..92bb615e 100644 --- a/src/controls/QskRadioBox.h +++ b/src/controls/QskRadioBox.h @@ -22,7 +22,7 @@ class QSK_EXPORT QskRadioBox : public QskControl using Inherited = QskControl; public: - QSK_SUBCONTROLS( Panel, Button, CheckIndicatorPanel, CheckIndicator, Text, Halo ) + QSK_SUBCONTROLS( Panel, Button, CheckIndicatorPanel, CheckIndicator, Text ) QSK_STATES( Selected, Pressed ) QskRadioBox( QQuickItem* parent = nullptr ); diff --git a/src/controls/QskRadioBoxSkinlet.cpp b/src/controls/QskRadioBoxSkinlet.cpp index 98201ea5..5bbc5987 100644 --- a/src/controls/QskRadioBoxSkinlet.cpp +++ b/src/controls/QskRadioBoxSkinlet.cpp @@ -40,7 +40,7 @@ namespace QskRadioBoxSkinlet::QskRadioBoxSkinlet( QskSkin* ) { setNodeRoles( { PanelRole, ButtonRole, CheckPanelRole, - CheckIndicatorRole, TextRole, HaloRole } ); + CheckIndicatorRole, TextRole } ); } QskRadioBoxSkinlet::~QskRadioBoxSkinlet() @@ -50,12 +50,8 @@ 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::Halo ) - return haloRect( radioBox, contentsRect ); + Q_UNUSED( skinnable ); + Q_UNUSED( subcontrol ); return contentsRect; } @@ -81,9 +77,6 @@ QSGNode* QskRadioBoxSkinlet::updateSubNode( const QskSkinnable* skinnable, case TextRole: return updateSeriesNode( skinnable, Q::Text, node ); - - case HaloRole: - return updateBoxNode( skinnable, node, Q::Halo ); } return Inherited::updateSubNode( skinnable, nodeRole, node ); @@ -96,29 +89,6 @@ int QskRadioBoxSkinlet::sampleCount( return radioBox->options().count(); } -QRectF QskRadioBoxSkinlet::haloRect( - const QskRadioBox* radioBox, const QRectF& rect ) const -{ - using Q = QskRadioBox; - - const auto index = qFloor( radioBox->positionHint( Q::Halo ) ); - if( index < 0 ) - return QRectF(); - - QRectF r; - r.setSize( radioBox->strutSizeHint( Q::Halo ) ); - - if ( !r.isEmpty() ) - { - const auto checkBoxRect = sampleRect( - radioBox, rect, Q::CheckIndicatorPanel, index ); - - r.moveCenter( checkBoxRect.center() ); - } - - return r; -} - QRectF QskRadioBoxSkinlet::buttonRect( const QskRadioBox* radioBox, const QRectF& rect, int index ) const { @@ -219,12 +189,12 @@ QskAspect::States QskRadioBoxSkinlet::sampleStates( states |= Q::Pressed; #if 1 - if( radioBox->positionHint( Q::Halo | Q::Hovered ) == index ) + if( radioBox->positionHint( Q::CheckIndicator | Q::Hovered ) == index ) states |= Q::Hovered; else states &= ~Q::Hovered; - if( radioBox->positionHint( Q::Halo ) == index ) + if( radioBox->positionHint( Q::CheckIndicator ) == index ) states |= Q::Focused; else states &= ~Q::Focused; diff --git a/src/controls/QskRadioBoxSkinlet.h b/src/controls/QskRadioBoxSkinlet.h index 73db392b..7a3f17b4 100644 --- a/src/controls/QskRadioBoxSkinlet.h +++ b/src/controls/QskRadioBoxSkinlet.h @@ -24,7 +24,6 @@ class QSK_EXPORT QskRadioBoxSkinlet : public QskSkinlet CheckPanelRole, CheckIndicatorRole, TextRole, - HaloRole, RoleCount }; @@ -57,8 +56,6 @@ class QSK_EXPORT QskRadioBoxSkinlet : public QskSkinlet QRectF textRect( const QskRadioBox*, const QRectF&, int ) const; QRectF checkPanelRect( const QskRadioBox*, const QRectF&, int index ) const; QRectF buttonRect( const QskRadioBox*, const QRectF&, int index ) const; - - QRectF haloRect( const QskRadioBox*, const QRectF& ) const; }; #endif From 99b4bddac9ad0519ab1e6dc432acea92b737ebb9 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 25 Oct 2024 11:37:34 +0200 Subject: [PATCH 5/9] QskSwitchButton M3 fixes, using shadows instead of an additonal subcontrol --- designsystems/material3/QskMaterial3Skin.cpp | 77 ++++++++++++++------ designsystems/material3/QskMaterial3Skin.h | 2 + src/controls/QskSwitchButton.cpp | 1 - src/controls/QskSwitchButton.h | 2 +- src/controls/QskSwitchButtonSkinlet.cpp | 51 +------------ src/controls/QskSwitchButtonSkinlet.h | 2 - 6 files changed, 58 insertions(+), 77 deletions(-) diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index d54a67a4..c5278146 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -481,15 +481,6 @@ void Editor::setupRadioBox() setStrutSize( Q::CheckIndicatorPanel, 20_dp, 20_dp ); - for ( const auto state1 : { Q::Hovered, Q::Focused, Q::Pressed } ) - { - for ( const auto state2 : { A::NoState, Q::Selected } ) - { - const auto aspect = Q::CheckIndicatorPanel | state1 | state2; - setShadowMetrics( aspect, { 10_dp, 0 } ); - } - } - for ( auto subControl : { Q::CheckIndicatorPanel, Q::CheckIndicator } ) setBoxShape( subControl, 100, Qt::RelativeSize ); // circular @@ -508,6 +499,8 @@ void Editor::setupRadioBox() { const auto aspect = Q::CheckIndicatorPanel | state1 | state2; + setShadowMetrics( aspect, { 10_dp, 0 } ); + QRgb rgb; if ( state1 == Q::Hovered ) @@ -1030,26 +1023,53 @@ void Editor::setupSwitchButton() setBoxBorderMetrics( Q::Groove | Q::Checked, 0 ); setBoxShape( Q::Handle, 100, Qt::RelativeSize ); - setStrutSize( Q::Handle, 16_dp, 16_dp ); - setStrutSize( Q::Handle | Q::Checked, 24_dp, 24_dp, - { QskStateCombination::CombinationNoState, Q::Disabled } ); + setStrutSize( Q::Handle, 30_dp, 30_dp ); + setMargin( Q::Handle, 7_dp ); + setShadowMetrics( Q::Handle, { 17_dp, 0 } ); + setShadowColor( Q::Handle, QskRgb::Transparent ); setGradient( Q::Handle, m_pal.outline ); - setGradient( Q::Handle | Q::Checked, m_pal.primaryContainer ); + setGradient( Q::Handle | Q::Checked, m_pal.onPrimary ); + + for ( auto state1 : { A::NoState, Q::Hovered, Q::Focused, Q::Pressed } ) + { + const qreal opacity = m_pal.stateOpacity( state1 ); + + for ( const auto state2 : { A::NoState, Q::Checked } ) + { + const auto aspect = Q::Handle | state1 | state2; + + if ( state1 == Q::Pressed ) + { + setShadowMetrics( aspect, { 10_dp, 0 } ); + setMargin( aspect, 0.0 ); + } + else if ( state2 == Q::Checked ) + { + setShadowMetrics( aspect, { 13_dp, 0 } ); + setMargin( aspect, 3_dp ); + } + + if ( state1 ) + { + if ( state2 == Q::Checked ) + { + setGradient( aspect, m_pal.primaryContainer ); + setShadowColor( aspect, stateLayerColor( m_pal.primary, opacity ) ); + } + else + { + setGradient( aspect, m_pal.onSurfaceVariant ); + setShadowColor( aspect, stateLayerColor( m_pal.onSurface, opacity ) ); + } + } + } + } + setGradient( Q::Handle | Q::Disabled, m_pal.onSurface38 ); setGradient( Q::Handle | Q::Disabled | Q::Checked, m_pal.surface ); - // just to keep the strut size the same at all times: - setStrutSize( Q::Halo, 40_dp, 40_dp ); - setGradient( Q::Halo, Qt::transparent ); - - setStrutSize( Q::Halo | Q::Hovered, 40_dp, 40_dp ); - setBoxShape( Q::Halo, 100, Qt::RelativeSize ); - setGradient( Q::Halo | Q::Hovered, stateLayerColor( m_pal.onSurface, m_pal.focusOpacity ) ); - setGradient( Q::Halo | Q::Hovered | Q::Checked, - stateLayerColor( m_pal.primary, m_pal.focusOpacity ) ); - setBoxBorderColors( Q::Handle, m_pal.outline ); setBoxBorderColors( Q::Handle | Q::Checked, m_pal.primary ); @@ -1490,6 +1510,17 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme, shapeExtraSmallTop = QskBoxShapeMetrics( 4_dp, 4_dp, 0, 0 ); } +qreal QskMaterial3Theme::stateOpacity( int state ) const +{ + if ( state == QskControl::Hovered ) + return hoverOpacity; + + if ( state == QskControl::Focused ) + return focusOpacity; + + return state ? pressedOpacity : 0.0; +} + QskMaterial3Skin::QskMaterial3Skin( QObject* parent ) : Inherited( parent ) { diff --git a/designsystems/material3/QskMaterial3Skin.h b/designsystems/material3/QskMaterial3Skin.h index b790addb..c51b3a7d 100644 --- a/designsystems/material3/QskMaterial3Skin.h +++ b/designsystems/material3/QskMaterial3Skin.h @@ -93,6 +93,8 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Theme const qreal pressedOpacity = 0.12; const qreal draggedOpacity = 0.16; + qreal stateOpacity( int state ) const; + QskBoxShapeMetrics shapeExtraSmallTop; }; diff --git a/src/controls/QskSwitchButton.cpp b/src/controls/QskSwitchButton.cpp index 1703a5e2..c9988dd2 100644 --- a/src/controls/QskSwitchButton.cpp +++ b/src/controls/QskSwitchButton.cpp @@ -7,7 +7,6 @@ QSK_SUBCONTROL( QskSwitchButton, Handle ) QSK_SUBCONTROL( QskSwitchButton, Groove ) -QSK_SUBCONTROL( QskSwitchButton, Halo ) struct QskSwitchButton::PrivateData { diff --git a/src/controls/QskSwitchButton.h b/src/controls/QskSwitchButton.h index 955e8f25..c63835e3 100644 --- a/src/controls/QskSwitchButton.h +++ b/src/controls/QskSwitchButton.h @@ -22,7 +22,7 @@ class QSK_EXPORT QskSwitchButton : public QskAbstractButton WRITE setInverted NOTIFY invertedChanged FINAL ) public: - QSK_SUBCONTROLS( Groove, Handle, Halo ) + QSK_SUBCONTROLS( Groove, Handle ) QskSwitchButton( Qt::Orientation, QQuickItem* parent = nullptr ); QskSwitchButton( QQuickItem* parent = nullptr ); diff --git a/src/controls/QskSwitchButtonSkinlet.cpp b/src/controls/QskSwitchButtonSkinlet.cpp index 906e589d..2a62ac08 100644 --- a/src/controls/QskSwitchButtonSkinlet.cpp +++ b/src/controls/QskSwitchButtonSkinlet.cpp @@ -26,7 +26,7 @@ static inline qreal qskEffectivePosition( const QskSwitchButton* switchButton ) QskSwitchButtonSkinlet::QskSwitchButtonSkinlet( QskSkin* skin ) : Inherited( skin ) { - setNodeRoles( { GrooveRole, HandleRole, HaloRole } ); + setNodeRoles( { GrooveRole, HandleRole } ); } QskSwitchButtonSkinlet::~QskSwitchButtonSkinlet() @@ -48,12 +48,6 @@ QRectF QskSwitchButtonSkinlet::subControlRect( const QskSkinnable* skinnable, return grooveRect( skinnable, contentsRect ); } - if ( subControl == Q::Halo ) - { - return haloRect( skinnable, contentsRect ); - } - - return Inherited::subControlRect( skinnable, contentsRect, subControl ); } @@ -65,10 +59,8 @@ QSizeF QskSwitchButtonSkinlet::sizeHint( const QskSkinnable* skinnable, const auto grooveHint = skinnable->strutSizeHint( QskSwitchButton::Groove ); const auto handleHint = skinnable->strutSizeHint( QskSwitchButton::Handle ); - const auto haloHint = skinnable->strutSizeHint( QskSwitchButton::Halo ); auto hint = grooveHint; - hint = hint.expandedTo( haloHint ); hint = hint.expandedTo( handleHint ); return hint; @@ -81,9 +73,6 @@ QSGNode* QskSwitchButtonSkinlet::updateSubNode( const QskSkinnable* skinnable, switch ( nodeRole ) { - case HaloRole: - return updateBoxNode( skinnable, node, Q::Halo ); - case HandleRole: return updateBoxNode( skinnable, node, Q::Handle ); @@ -166,42 +155,4 @@ QRectF QskSwitchButtonSkinlet::handleRect( return r; } -QRectF QskSwitchButtonSkinlet::haloRect( - const QskSkinnable* skinnable, const QRectF& contentsRect ) const -{ - using Q = QskSwitchButton; - - const auto switchButton = static_cast< const Q* >( skinnable ); - - const auto grooveRect = subControlRect( skinnable, contentsRect, Q::Groove ); - const auto pos = qskEffectivePosition( switchButton ); - const auto sizeHandle = skinnable->strutSizeHint( Q::Handle ); - const auto sizeHalo = skinnable->strutSizeHint( Q::Halo ); - - qreal cx, cy; - - if( switchButton->orientation() == Qt::Vertical ) - { - const qreal y0 = grooveRect.y() + 0.5 * sizeHandle.height(); - const qreal h = grooveRect.height() - sizeHandle.height(); - - cx = grooveRect.x() + 0.5 * grooveRect.width(); - cy = y0 + pos * h; - } - else - { - const qreal x0 = grooveRect.x() + 0.5 * sizeHandle.width(); - const qreal w = grooveRect.width() - sizeHandle.width(); - - cx = x0 + pos * w; - cy = grooveRect.y() + 0.5 * grooveRect.height(); - } - - QRectF r; - r.setSize( sizeHalo ); - r.moveCenter( QPointF( cx, cy ) ); - - return r; -} - #include "moc_QskSwitchButtonSkinlet.cpp" diff --git a/src/controls/QskSwitchButtonSkinlet.h b/src/controls/QskSwitchButtonSkinlet.h index 4b8fa368..f39fbf3c 100644 --- a/src/controls/QskSwitchButtonSkinlet.h +++ b/src/controls/QskSwitchButtonSkinlet.h @@ -19,7 +19,6 @@ class QSK_EXPORT QskSwitchButtonSkinlet : public QskSkinlet { GrooveRole, HandleRole, - HaloRole, RoleCount }; @@ -40,7 +39,6 @@ class QSK_EXPORT QskSwitchButtonSkinlet : public QskSkinlet private: QRectF grooveRect( const QskSkinnable*, const QRectF& ) const; QRectF handleRect( const QskSkinnable*, const QRectF& ) const; - QRectF haloRect( const QskSkinnable*, const QRectF& ) const; }; #endif From 33980a58879b8cf6b6fec3633bbab5e1eb263bb2 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 25 Oct 2024 18:52:02 +0200 Subject: [PATCH 6/9] M3 radiobox setup simplified --- designsystems/material3/QskMaterial3Skin.cpp | 30 ++++---------------- designsystems/material3/QskMaterial3Skin.h | 1 + 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index c5278146..5fe6ef31 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -501,31 +501,10 @@ void Editor::setupRadioBox() setShadowMetrics( aspect, { 10_dp, 0 } ); - QRgb rgb; - - if ( state1 == Q::Hovered ) - { - rgb = ( state2 == Q::Selected ) ? m_pal.primary8 : m_pal.onSurface8; - rgb = stateLayerColor( rgb, m_pal.hoverOpacity ); - - setShadowColor( aspect, rgb ); - } - else if ( state1 == Q::Focused ) - { - rgb = ( state2 == Q::Selected ) ? m_pal.primary12 : m_pal.onSurface12; - rgb = stateLayerColor( rgb, m_pal.focusOpacity ); - - setShadowColor( aspect, rgb ); - } - else - { - rgb = ( state2 == Q::Selected ) ? m_pal.onSurface12 : m_pal.primary12; - rgb = stateLayerColor( rgb, m_pal.pressedOpacity ); - - setShadowColor( aspect, rgb ); - setShadowColor( aspect | Q::Focused, rgb ); - } + auto rgb = ( state2 == Q::Selected ) ? m_pal.primary : m_pal.onSurface; + rgb = stateLayerColor( rgb, m_pal.stateOpacity( state1 ) ); + setShadowColor( aspect, rgb ); } } @@ -1518,6 +1497,9 @@ qreal QskMaterial3Theme::stateOpacity( int state ) const if ( state == QskControl::Focused ) return focusOpacity; + if ( state == QskControl::Disabled ) + return disabledOpacity; + return state ? pressedOpacity : 0.0; } diff --git a/designsystems/material3/QskMaterial3Skin.h b/designsystems/material3/QskMaterial3Skin.h index c51b3a7d..b2790e02 100644 --- a/designsystems/material3/QskMaterial3Skin.h +++ b/designsystems/material3/QskMaterial3Skin.h @@ -92,6 +92,7 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Theme const qreal focusOpacity = 0.12; const qreal pressedOpacity = 0.12; const qreal draggedOpacity = 0.16; + const qreal disabledOpacity = 0.38; qreal stateOpacity( int state ) const; From 9d8666290fa0d9c37bfc6a679e90197074d1db86 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 25 Oct 2024 18:54:04 +0200 Subject: [PATCH 7/9] QskShadowMetrics::ShapeMode introduced --- src/common/QskShadowMetrics.cpp | 3 ++- src/common/QskShadowMetrics.h | 29 +++++++++++++++++++++++++++-- src/controls/QskSkinlet.cpp | 14 +++++++++++++- src/nodes/QskBoxNode.cpp | 17 +++++++++++++++-- 4 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/common/QskShadowMetrics.cpp b/src/common/QskShadowMetrics.cpp index b5331dc9..9f8e3878 100644 --- a/src/common/QskShadowMetrics.cpp +++ b/src/common/QskShadowMetrics.cpp @@ -95,7 +95,8 @@ QskHashValue QskShadowMetrics::hash( QskHashValue seed ) const noexcept hash = qHash( m_offset.y(), seed ); hash = qHash( m_spreadRadius, hash ); hash = qHash( m_blurRadius, hash ); - hash = qHash( static_cast< int >( m_sizeMode ), hash ); + hash = qHash( m_sizeMode, hash ); + hash = qHash( m_shapeMode, hash ); return hash; } diff --git a/src/common/QskShadowMetrics.h b/src/common/QskShadowMetrics.h index bb85a117..711e4cef 100644 --- a/src/common/QskShadowMetrics.h +++ b/src/common/QskShadowMetrics.h @@ -23,8 +23,18 @@ class QSK_EXPORT QskShadowMetrics Q_PROPERTY( qreal blurRadius READ blurRadius WRITE setBlurRadius ) Q_PROPERTY( Qt::SizeMode sizeMode READ sizeMode WRITE setSizeMode ) + Q_PROPERTY( ShapeMode shapeMode READ shapeMode WRITE setShapeMode ) public: + enum ShapeMode + { + Aligned = 0, // The shape is related to some external definition + + Ellipse, + Rectangle + }; + Q_ENUM( ShapeMode ) + constexpr QskShadowMetrics( const QPointF& offset = QPointF() ) noexcept; constexpr QskShadowMetrics( qreal spreadRadius, qreal blurRadius ) noexcept; @@ -56,6 +66,9 @@ class QSK_EXPORT QskShadowMetrics void setSizeMode( Qt::SizeMode ) noexcept; constexpr Qt::SizeMode sizeMode() const noexcept; + void setShapeMode( ShapeMode ) noexcept; + constexpr ShapeMode shapeMode() const noexcept; + QskShadowMetrics interpolated( const QskShadowMetrics&, qreal value ) const noexcept; @@ -72,7 +85,8 @@ class QSK_EXPORT QskShadowMetrics QPointF m_offset; qreal m_spreadRadius = 0.0; qreal m_blurRadius = 0.0; - Qt::SizeMode m_sizeMode = Qt::AbsoluteSize; + quint8 m_sizeMode = Qt::AbsoluteSize; + quint8 m_shapeMode = QskShadowMetrics::Aligned; }; inline constexpr QskShadowMetrics::QskShadowMetrics( const QPointF& offset ) noexcept @@ -101,6 +115,7 @@ inline constexpr bool QskShadowMetrics::operator==( const QskShadowMetrics& other ) const noexcept { return ( m_sizeMode == other.m_sizeMode ) + && ( m_shapeMode == other.m_shapeMode ) && ( m_offset == other.m_offset ) && ( m_spreadRadius == other.m_spreadRadius ) && ( m_blurRadius == other.m_blurRadius ) @@ -145,7 +160,17 @@ inline void QskShadowMetrics::setSizeMode( Qt::SizeMode sizeMode ) noexcept inline constexpr Qt::SizeMode QskShadowMetrics::sizeMode() const noexcept { - return m_sizeMode; + return static_cast< Qt::SizeMode >( m_sizeMode ); +} + +inline void QskShadowMetrics::setShapeMode( ShapeMode shapeMode ) noexcept +{ + m_shapeMode = shapeMode; +} + +inline constexpr QskShadowMetrics::ShapeMode QskShadowMetrics::shapeMode() const noexcept +{ + return static_cast< ShapeMode >( m_shapeMode ); } inline void QskShadowMetrics::setOffsetX( qreal dx ) noexcept diff --git a/src/controls/QskSkinlet.cpp b/src/controls/QskSkinlet.cpp index 3a258445..7befc837 100644 --- a/src/controls/QskSkinlet.cpp +++ b/src/controls/QskSkinlet.cpp @@ -141,6 +141,12 @@ static inline QSGNode* qskUpdateGraphicNode( return graphicNode; } +static inline bool qskIsShadowVisible( const QskShadowMetrics& shadowMetrics, + const QColor& shadowColor ) +{ + return !shadowMetrics.isNull() && shadowColor.isValid() && ( shadowColor.alpha() > 0 ); +} + static inline bool qskIsBoxVisible( const QskBoxBorderMetrics& borderMetrics, const QskBoxBorderColors& borderColors, const QskGradient& gradient ) { @@ -196,9 +202,15 @@ static inline QSGNode* qskUpdateBoxNode( const QskBoxBorderColors& borderColors, const QskGradient& gradient, const QskShadowMetrics& shadowMetrics, const QColor& shadowColor ) { - if ( rect.isEmpty() || !qskIsBoxVisible( borderMetrics, borderColors, gradient ) ) + if ( rect.isEmpty() ) return nullptr; + if ( !qskIsBoxVisible( borderMetrics, borderColors, gradient ) + && !qskIsShadowVisible( shadowMetrics, shadowColor ) ) + { + return nullptr; + } + auto boxNode = QskSGNode::ensureNode< QskBoxNode >( node ); boxNode->updateNode( rect, shape, borderMetrics, borderColors, gradient, shadowMetrics, shadowColor ); diff --git a/src/nodes/QskBoxNode.cpp b/src/nodes/QskBoxNode.cpp index 78d21d59..22cc78c5 100644 --- a/src/nodes/QskBoxNode.cpp +++ b/src/nodes/QskBoxNode.cpp @@ -83,17 +83,30 @@ void QskBoxNode::updateNode( const QRectF& rect, const auto shadow = shadowMetrics.toAbsolute( rect.size() ); const auto shadowRect = shadow.shadowRect( rect ); + auto shadowShape = shapeMetrics; + + switch( static_cast< int >( shadow.shapeMode() ) ) + { + case QskShadowMetrics::Ellipse: + shadowShape.setRadius( 100.0, Qt::RelativeSize ); + break; + + case QskShadowMetrics::Rectangle: + shadowShape.setRadius( 0.0, Qt::AbsoluteSize ); + break; + } + if ( shadow.blurRadius() <= 0.0 ) { // QskBoxRectangleNode allows scene graph batching shadowFillNode = qskNode< QskBoxRectangleNode >( this, ShadowFillRole ); - shadowFillNode->updateFilling( shadowRect, shapeMetrics, shadowColor ); + shadowFillNode->updateFilling( shadowRect, shadowShape, shadowColor ); } else { shadowNode = qskNode< QskBoxShadowNode >( this, ShadowRole ); shadowNode->setShadowData( shadowRect, - shapeMetrics, shadow.blurRadius(), shadowColor ); + shadowShape, shadow.blurRadius(), shadowColor ); } } From 80934fa07fc23df94359318a874f5d9c2c156c8b Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 25 Oct 2024 18:56:02 +0200 Subject: [PATCH 8/9] QskCheckBox::Halo replaced by using a box shadow --- designsystems/material3/QskMaterial3Skin.cpp | 127 ++++++++++--------- src/controls/QskCheckBox.cpp | 1 - src/controls/QskCheckBox.h | 2 +- src/controls/QskCheckBoxSkinlet.cpp | 23 +--- src/controls/QskCheckBoxSkinlet.h | 2 - 5 files changed, 71 insertions(+), 84 deletions(-) diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index 5fe6ef31..c440e193 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -194,83 +194,95 @@ namespace void Editor::setupCheckBox() { - // skin hints are ordered according to - // https://m3.material.io/components/checkbox/specs - using Q = QskCheckBox; + using A = QskAspect; + + // == metrics setSpacing( Q::Panel, 40_dp ); - setStrutSize( Q::Box, 18_dp, 18_dp ); - setBoxShape( Q::Box, 2_dp ); - - setBoxBorderColors( Q::Box, m_pal.onSurface ); -#if 1 - // hack: if border metrics == box shape, alpha value will be discarded - setBoxBorderMetrics( Q::Box, 1.99_dp ); -#endif - - setGradient( Q::Box, m_pal.background ); // not mentioned in the specs, but needed for animation - setGradient( Q::Box | Q::Checked, m_pal.primary ); - setBoxBorderMetrics( Q::Box | Q::Checked, 0 ); - - setPadding( Q::Box, 3_dp ); // "icon size" - - setGraphicRole( Q::Indicator, QskMaterial3Skin::GraphicRoleOnPrimary ); - - setBoxBorderColors( Q::Box | Q::Error, m_pal.error ); - - setGradient( Q::Box | Q::Checked | Q::Error, m_pal.error ); - - setGraphicRole( Q::Indicator | Q::Error, QskMaterial3Skin::GraphicRoleOnError ); - - const auto checkMark = symbol( "check_small" ); - for ( auto state : { QskAspect::NoState, Q::Disabled } ) { - const auto aspect = Q::Indicator | Q::Checked | state; - setSymbol( aspect, checkMark ); - setSymbol( aspect | Q::Error, checkMark ); + setStrutSize( Q::Box, 18_dp, 18_dp ); + setBoxBorderMetrics( Q::Box, 2_dp ); + setBoxShape( Q::Box, 2_dp ); + setPadding( Q::Box, 3_dp ); // "icon size" + + QskShadowMetrics shadowMetrics( 12_dp, 0.0 ); + shadowMetrics.setShapeMode( QskShadowMetrics::Ellipse ); + setShadowMetrics( Q::Box, shadowMetrics ); } - setStrutSize( Q::Halo, 40_dp, 40_dp ); - setBoxShape( Q::Halo, 100, Qt::RelativeSize ); - setGradient( Q::Halo, Qt::transparent ); + { + setGraphicRole( Q::Indicator, QskMaterial3Skin::GraphicRoleOnPrimary ); + setGraphicRole( Q::Indicator | Q::Error, QskMaterial3Skin::GraphicRoleOnError ); + setGraphicRole( Q::Indicator | Q::Disabled | Q::Checked, + QskMaterial3Skin::GraphicRoleSurface ); + + const auto checkMark = symbol( "check_small" ); + for ( auto state : { A::NoState, Q::Disabled } ) + { + const auto aspect = Q::Indicator | Q::Checked | state; + setSymbol( aspect, checkMark ); + setSymbol( aspect | Q::Error, checkMark ); + } + } + + // === colors setColor( Q::Text, m_pal.onBackground ); // not mentioned in the specs - // States + for ( auto state1 : { Q::Hovered, Q::Focused, Q::Pressed } ) + { + const auto opacity = m_pal.stateOpacity( state1 ); - // 2. Disabled + for ( auto state2 : { A::NoState, Q::Checked } ) + { + const auto aspect = Q::Box | state1 | state2; - setBoxBorderColors( Q::Box | Q::Disabled, m_pal.onSurface38 ); - setBoxShape( Q::Box | Q::Disabled, 2_dp ); + const auto rgb = ( state2 == Q::Checked ) ? m_pal.primary : m_pal.onSurface; + setShadowColor( aspect, stateLayerColor( rgb, opacity ) ); - setGradient( Q::Box | Q::Disabled | Q::Checked, m_pal.onSurface38 ); - setGradient( Q::Box | Q::Disabled | Q::Checked | Q::Error, m_pal.onSurface38 ); + setShadowColor( aspect | Q::Error, stateLayerColor( m_pal.error, opacity ) ); + } + } - setGraphicRole( Q::Indicator | Q::Disabled | Q::Checked, QskMaterial3Skin::GraphicRoleSurface ); + for ( auto state1 : { A::NoState, Q::Disabled, Q::Hovered, Q::Focused, Q::Pressed } ) + { + for ( auto state2 : { A::NoState, Q::Checked } ) + { + const auto aspect = Q::Box | state1 | state2; - // 3. Hovered + QRgb rgb, rgbError; - setGradient( Q::Halo | Q::Hovered | Q::Checked, m_pal.primary8 ); - setGradient( Q::Halo | Q::Hovered, m_pal.onSurface8 ); - setGradient( Q::Halo | Q::Error | Q::Hovered, m_pal.error8 ); - setGradient( Q::Halo | Q::Error | Q::Hovered | Q::Checked, m_pal.error8 ); + if ( state1 == A::NoState ) + { + rgb = ( state2 == Q::Checked ) + ? m_pal.primary : m_pal.onSurfaceVariant; - // 4. Focused + rgbError = m_pal.error; + } + else if ( state1 == Q::Disabled ) + { + rgb = rgbError = m_pal.onSurface38; + } + else + { + rgb = ( state2 == Q::Checked ) + ? m_pal.primary : m_pal.onSurfaceVariant; - setGradient( Q::Halo | Q::Focused | Q::Checked, m_pal.primary12 ); - setGradient( Q::Halo | Q::Focused, m_pal.onSurface12 ); - setGradient( Q::Halo | Q::Error | Q::Focused, m_pal.error12 ); - setGradient( Q::Halo | Q::Error | Q::Focused | Q::Checked, m_pal.error12 ); + rgbError = m_pal.error; + } - // 5. Pressed + setBoxBorderColors( aspect, rgb ); + setBoxBorderColors( aspect | Q::Error, rgbError ); - setGradient( Q::Halo | Q::Pressed, m_pal.primary12 ); - setGradient( Q::Halo | Q::Pressed | Q::Checked, m_pal.primary12 ); - setGradient( Q::Halo | Q::Hovered | Q::Pressed, m_pal.primary12 ); - setGradient( Q::Halo | Q::Error | Q::Pressed, m_pal.error12 ); - setGradient( Q::Halo | Q::Error | Q::Pressed | Q::Checked, m_pal.error12 ); + if ( state2 == Q::Checked ) + { + setGradient( aspect, rgb ); + setGradient( aspect | Q::Error, rgbError ); + } + } + } } void Editor::setupComboBox() @@ -1045,7 +1057,6 @@ void Editor::setupSwitchButton() } } - setGradient( Q::Handle | Q::Disabled, m_pal.onSurface38 ); setGradient( Q::Handle | Q::Disabled | Q::Checked, m_pal.surface ); diff --git a/src/controls/QskCheckBox.cpp b/src/controls/QskCheckBox.cpp index c457c9fc..3d42f02b 100644 --- a/src/controls/QskCheckBox.cpp +++ b/src/controls/QskCheckBox.cpp @@ -9,7 +9,6 @@ QSK_SUBCONTROL( QskCheckBox, Panel ) QSK_SUBCONTROL( QskCheckBox, Box ) QSK_SUBCONTROL( QskCheckBox, Indicator ) QSK_SUBCONTROL( QskCheckBox, Text ) -QSK_SUBCONTROL( QskCheckBox, Halo ) QSK_SYSTEM_STATE( QskCheckBox, Error, QskAspect::FirstSystemState << 1 ) diff --git a/src/controls/QskCheckBox.h b/src/controls/QskCheckBox.h index 2096ae75..6b5877b7 100644 --- a/src/controls/QskCheckBox.h +++ b/src/controls/QskCheckBox.h @@ -17,7 +17,7 @@ class QSK_EXPORT QskCheckBox : public QskAbstractButton using Inherited = QskAbstractButton; public: - QSK_SUBCONTROLS( Panel, Box, Indicator, Text, Halo ) + QSK_SUBCONTROLS( Panel, Box, Indicator, Text ) QSK_STATES( Error ) QskCheckBox( QQuickItem* parent = nullptr ); diff --git a/src/controls/QskCheckBoxSkinlet.cpp b/src/controls/QskCheckBoxSkinlet.cpp index 47193a8e..e279b97b 100644 --- a/src/controls/QskCheckBoxSkinlet.cpp +++ b/src/controls/QskCheckBoxSkinlet.cpp @@ -11,7 +11,7 @@ QskCheckBoxSkinlet::QskCheckBoxSkinlet( QskSkin* skin ) : QskSkinlet( skin ) { - setNodeRoles( { BoxRole, IndicatorRole, TextRole, HaloRole } ); + setNodeRoles( { BoxRole, IndicatorRole, TextRole } ); } QskCheckBoxSkinlet::~QskCheckBoxSkinlet() @@ -40,9 +40,6 @@ QRectF QskCheckBoxSkinlet::subControlRect( const QskSkinnable* skinnable, if ( subControl == Q::Text ) return textRect( checkBox, contentsRect ); - if ( subControl == Q::Halo ) - return haloRect( checkBox, contentsRect ); - return contentsRect; } @@ -83,21 +80,6 @@ QRectF QskCheckBoxSkinlet::boxRect( return r; } -QRectF QskCheckBoxSkinlet::haloRect( - const QskCheckBox* checkBox, const QRectF& rect ) const -{ - const auto haloSize = checkBox->strutSizeHint( QskCheckBox::Halo ); - const auto boxSize = checkBox->strutSizeHint( QskCheckBox::Box ); - - const auto w = ( haloSize.width() - boxSize.width() ) / 2; - const auto h = ( haloSize.height() - boxSize.height() ) / 2; - - auto r = boxRect( checkBox, rect ); - r = r.marginsAdded( { w, h, w, h } ); - - return r; -} - QSGNode* QskCheckBoxSkinlet::updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const { @@ -118,9 +100,6 @@ QSGNode* QskCheckBoxSkinlet::updateSubNode( case TextRole: return updateTextNode( checkBox, node ); - - case HaloRole: - return updateBoxNode( checkBox, node, Q::Halo ); } return Inherited::updateSubNode( skinnable, nodeRole, node ); diff --git a/src/controls/QskCheckBoxSkinlet.h b/src/controls/QskCheckBoxSkinlet.h index 60f46be5..c13b528c 100644 --- a/src/controls/QskCheckBoxSkinlet.h +++ b/src/controls/QskCheckBoxSkinlet.h @@ -23,7 +23,6 @@ class QSK_EXPORT QskCheckBoxSkinlet : public QskSkinlet BoxRole, IndicatorRole, TextRole, - HaloRole, RoleCount }; @@ -44,7 +43,6 @@ class QSK_EXPORT QskCheckBoxSkinlet : public QskSkinlet private: QRectF textRect( const QskCheckBox*, const QRectF& ) const; QRectF boxRect( const QskCheckBox*, const QRectF& ) const; - QRectF haloRect( const QskCheckBox*, const QRectF& ) const; QSGNode* updateTextNode( const QskCheckBox*, QSGNode* ) const; }; From b2f3220dfe7974246a703bc767b27e0e136b4904 Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Mon, 28 Oct 2024 08:14:32 +0100 Subject: [PATCH 9/9] switch button: Support icons (for M3) (#452) --- designsystems/material3/QskMaterial3Icons.qrc | 2 + designsystems/material3/QskMaterial3Skin.cpp | 26 ++++- designsystems/material3/QskMaterial3Skin.h | 4 + .../icons/qvg/switchbutton-checked.qvg | Bin 0 -> 607 bytes .../icons/qvg/switchbutton-unchecked.qvg | Bin 0 -> 727 bytes .../material3/icons/switchbutton-checked.svg | 4 + .../icons/switchbutton-unchecked.svg | 4 + examples/gallery/button/ButtonPage.cpp | 19 +++- src/controls/QskSwitchButton.cpp | 18 +++- src/controls/QskSwitchButton.h | 19 +++- src/controls/QskSwitchButtonSkinlet.cpp | 92 +++++++++++++----- src/controls/QskSwitchButtonSkinlet.h | 8 +- 12 files changed, 155 insertions(+), 41 deletions(-) create mode 100644 designsystems/material3/icons/qvg/switchbutton-checked.qvg create mode 100644 designsystems/material3/icons/qvg/switchbutton-unchecked.qvg create mode 100644 designsystems/material3/icons/switchbutton-checked.svg create mode 100644 designsystems/material3/icons/switchbutton-unchecked.svg diff --git a/designsystems/material3/QskMaterial3Icons.qrc b/designsystems/material3/QskMaterial3Icons.qrc index 4e55e3be..72725b7a 100644 --- a/designsystems/material3/QskMaterial3Icons.qrc +++ b/designsystems/material3/QskMaterial3Icons.qrc @@ -7,6 +7,8 @@ icons/qvg/combo-box-arrow-closed.qvg icons/qvg/combo-box-arrow-open.qvg icons/qvg/segmented-button-check.qvg + icons/qvg/switchbutton-checked.qvg + icons/qvg/switchbutton-unchecked.qvg diff --git a/designsystems/material3/QskMaterial3Skin.cpp b/designsystems/material3/QskMaterial3Skin.cpp index c440e193..ca8bd5eb 100644 --- a/designsystems/material3/QskMaterial3Skin.cpp +++ b/designsystems/material3/QskMaterial3Skin.cpp @@ -40,7 +40,6 @@ #include #include #include -#include #include #include #include @@ -998,6 +997,8 @@ void Editor::setupSwitchButton() using A = QskAspect; using Q = QskSwitchButton; + const QskStateCombination allStates ( QskStateCombination::CombinationNoState, QskAspect::AllStates ); + setBoxShape( Q::Groove, 100, Qt::RelativeSize ); const QSizeF strutSize( 52_dp, 32_dp ); setStrutSize( Q::Groove | A::Horizontal, strutSize ); @@ -1010,11 +1011,12 @@ void Editor::setupSwitchButton() setGradient( Q::Groove | Q::Checked | Q::Disabled, m_pal.onSurface12 ); setBoxBorderMetrics( Q::Groove, 2_dp ); setBoxBorderColors( Q::Groove, m_pal.outline ); + setBoxBorderColors( Q::Groove | Q::Disabled, m_pal.onSurface12 ); - setBoxBorderMetrics( Q::Groove | Q::Checked, 0 ); + setBoxBorderMetrics( Q::Groove | Q::Checked, 0, allStates ); setBoxShape( Q::Handle, 100, Qt::RelativeSize ); - setStrutSize( Q::Handle, 30_dp, 30_dp ); + setStrutSize( Q::Handle, { 30_dp, 30_dp } ); setMargin( Q::Handle, 7_dp ); setShadowMetrics( Q::Handle, { 17_dp, 0 } ); setShadowColor( Q::Handle, QskRgb::Transparent ); @@ -1022,6 +1024,15 @@ void Editor::setupSwitchButton() setGradient( Q::Handle, m_pal.outline ); setGradient( Q::Handle | Q::Checked, m_pal.onPrimary ); + setStrutSize( Q::Icon, { 16_dp, 16_dp } ); + setPadding( Q::Icon, 6_dp ); + setSymbol( Q::Icon, symbol( "switchbutton-unchecked" ) ); + setSymbol( Q::Icon | Q::Checked, symbol( "switchbutton-checked" ), allStates ); + setGraphicRole( Q::Icon, QskMaterial3Skin::GraphicRoleSurfaceContainerHighest ); + setGraphicRole( Q::Icon | Q::Checked, QskMaterial3Skin::GraphicRoleOnPrimaryContainer, allStates ); + setGraphicRole( Q::Icon | Q::Disabled, QskMaterial3Skin::GraphicRoleSurfaceContainerHighest38, allStates ); + setGraphicRole( Q::Icon | Q::Checked | Q::Disabled, QskMaterial3Skin::GraphicRoleOnSurface38, allStates ); + for ( auto state1 : { A::NoState, Q::Hovered, Q::Focused, Q::Pressed } ) { const qreal opacity = m_pal.stateOpacity( state1 ); @@ -1067,8 +1078,8 @@ void Editor::setupSwitchButton() { auto aspect = Q::Handle | state; - setPosition( aspect, 0.15 ); - setPosition( aspect | Q::Checked, 0.85 ); + setPosition( aspect, 0.10 ); + setPosition( aspect | Q::Checked, 0.9 ); } setAnimation( Q::Handle | A::Color, qskDuration ); @@ -1492,6 +1503,8 @@ QskMaterial3Theme::QskMaterial3Theme( QskSkin::ColorScheme colorScheme, surfaceVariant12 = QskRgb::toTransparentF( surfaceVariant, 0.12 ); + surfaceContainerHighest38 = QskRgb::toTransparentF( surfaceContainerHighest, 0.38 ); + elevation0 = QskShadowMetrics( 0, 0 ); elevation1 = QskShadowMetrics( -2, 9, { 0, 1 } ); elevation2 = QskShadowMetrics( -2, 8, { 0, 2 } ); @@ -1597,6 +1610,7 @@ void QskMaterial3Skin::setGraphicColor( GraphicRole role, QRgb rgb ) void QskMaterial3Skin::setupGraphicFilters( const QskMaterial3Theme& theme ) { setGraphicColor( GraphicRoleOnPrimary, theme.onPrimary ); + setGraphicColor( GraphicRoleOnPrimaryContainer, theme.onPrimaryContainer ); setGraphicColor( GraphicRoleOnSecondaryContainer, theme.onSecondaryContainer ); setGraphicColor( GraphicRoleOnError, theme.onError ); setGraphicColor( GraphicRoleOnSurface, theme.onSurface ); @@ -1604,6 +1618,8 @@ void QskMaterial3Skin::setupGraphicFilters( const QskMaterial3Theme& theme ) setGraphicColor( GraphicRoleOnSurfaceVariant, theme.onSurfaceVariant ); setGraphicColor( GraphicRolePrimary, theme.primary ); setGraphicColor( GraphicRoleSurface, theme.surface ); + setGraphicColor( GraphicRoleSurfaceContainerHighest, theme.surfaceContainerHighest ); + setGraphicColor( GraphicRoleSurfaceContainerHighest38, theme.surfaceContainerHighest38 ); } void QskMaterial3Skin::initHints() diff --git a/designsystems/material3/QskMaterial3Skin.h b/designsystems/material3/QskMaterial3Skin.h index b2790e02..bac26cba 100644 --- a/designsystems/material3/QskMaterial3Skin.h +++ b/designsystems/material3/QskMaterial3Skin.h @@ -80,6 +80,7 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Theme QRgb outlineVariant; QRgb surfaceContainerHighest; + QRgb surfaceContainerHighest38; QRgb shadow; @@ -110,12 +111,15 @@ class QSK_MATERIAL3_EXPORT QskMaterial3Skin : public QskSkin { GraphicRoleOnError, GraphicRoleOnPrimary, + GraphicRoleOnPrimaryContainer, GraphicRoleOnSecondaryContainer, GraphicRoleOnSurface, GraphicRoleOnSurface38, GraphicRoleOnSurfaceVariant, GraphicRolePrimary, GraphicRoleSurface, + GraphicRoleSurfaceContainerHighest, + GraphicRoleSurfaceContainerHighest38, }; QskMaterial3Skin( QObject* parent = nullptr ); diff --git a/designsystems/material3/icons/qvg/switchbutton-checked.qvg b/designsystems/material3/icons/qvg/switchbutton-checked.qvg new file mode 100644 index 0000000000000000000000000000000000000000..c927ddb380439cfb3fedf76ea642085480e47109 GIT binary patch literal 607 zcmWFx_I77L0}cid0T>PBF*7qUGXyZ$e}Kp^GXDP$(*zTP@zH5wPBF*7qUGXyZ$e}Kp^GXDP$(*zTP@zH5w1W)7+CFelPtk{Vd~UNgjn}5 zE=Dm=Gc3rdleu0ym7oM1r~$34_O_|AIRn@KD0I5xvc=jUYI(Be}P6aFoNPY PkpA(@K!bz?^bQjMwGnMI literal 0 HcmV?d00001 diff --git a/designsystems/material3/icons/switchbutton-checked.svg b/designsystems/material3/icons/switchbutton-checked.svg new file mode 100644 index 00000000..7f4d9275 --- /dev/null +++ b/designsystems/material3/icons/switchbutton-checked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/designsystems/material3/icons/switchbutton-unchecked.svg b/designsystems/material3/icons/switchbutton-unchecked.svg new file mode 100644 index 00000000..a9e2ee7e --- /dev/null +++ b/designsystems/material3/icons/switchbutton-unchecked.svg @@ -0,0 +1,4 @@ + + + + diff --git a/examples/gallery/button/ButtonPage.cpp b/examples/gallery/button/ButtonPage.cpp index 7ba35788..bc00b4d1 100644 --- a/examples/gallery/button/ButtonPage.cpp +++ b/examples/gallery/button/ButtonPage.cpp @@ -133,13 +133,24 @@ namespace SwitchButtonBox( QQuickItem* parent = nullptr ) : ButtonBox( Qt::Horizontal, parent ) { + setDimension( 6 ); + setSpacing( 20 ); + setDefaultAlignment( Qt::AlignCenter ); + for ( auto orientation : { Qt::Vertical, Qt::Horizontal } ) { - (void) new QskSwitchButton( orientation, this ); + using Q = QskSwitchButton; - auto button = new QskSwitchButton( orientation, this ); - button->setInverted( true ); - button->setChecked( true ); + for( auto iconMode : { Q::NoIcon, Q::ShowIconWhenSelected, Q::ShowIconAlways } ) + { + auto button = new QskSwitchButton( orientation, this ); + button->setIconMode( iconMode ); + + auto invertedButton = new QskSwitchButton( orientation, this ); + invertedButton->setInverted( true ); + invertedButton->setChecked( true ); + invertedButton->setIconMode( iconMode ); + } } } }; diff --git a/src/controls/QskSwitchButton.cpp b/src/controls/QskSwitchButton.cpp index c9988dd2..9d6f3b93 100644 --- a/src/controls/QskSwitchButton.cpp +++ b/src/controls/QskSwitchButton.cpp @@ -5,8 +5,9 @@ #include "QskSwitchButton.h" -QSK_SUBCONTROL( QskSwitchButton, Handle ) QSK_SUBCONTROL( QskSwitchButton, Groove ) +QSK_SUBCONTROL( QskSwitchButton, Handle ) +QSK_SUBCONTROL( QskSwitchButton, Icon ) struct QskSwitchButton::PrivateData { @@ -17,6 +18,7 @@ struct QskSwitchButton::PrivateData bool inverted = false; Qt::Orientation orientation; + IconMode iconMode = NoIcon; }; QskSwitchButton::QskSwitchButton( QQuickItem* parent ) @@ -76,6 +78,20 @@ void QskSwitchButton::setInverted( bool on ) } } +QskSwitchButton::IconMode QskSwitchButton::iconMode() const +{ + return m_data->iconMode; +} + +void QskSwitchButton::setIconMode( IconMode iconMode ) +{ + if( iconMode != m_data->iconMode ) + { + m_data->iconMode = iconMode; + Q_EMIT iconModeChanged( m_data->iconMode ); + } +} + QskAspect::Variation QskSwitchButton::effectiveVariation() const { return static_cast< QskAspect::Variation >( m_data->orientation ); diff --git a/src/controls/QskSwitchButton.h b/src/controls/QskSwitchButton.h index c63835e3..e12cc65f 100644 --- a/src/controls/QskSwitchButton.h +++ b/src/controls/QskSwitchButton.h @@ -21,8 +21,19 @@ class QSK_EXPORT QskSwitchButton : public QskAbstractButton Q_PROPERTY( bool inverted READ isInverted WRITE setInverted NOTIFY invertedChanged FINAL ) + Q_PROPERTY( IconMode iconMode READ iconMode + WRITE setIconMode NOTIFY iconModeChanged FINAL ) + public: - QSK_SUBCONTROLS( Groove, Handle ) + QSK_SUBCONTROLS( Groove, Handle, Icon ) + + enum IconMode + { + NoIcon, + ShowIconWhenSelected, + ShowIconAlways + }; + Q_ENUM( IconMode ) QskSwitchButton( Qt::Orientation, QQuickItem* parent = nullptr ); QskSwitchButton( QQuickItem* parent = nullptr ); @@ -32,16 +43,20 @@ class QSK_EXPORT QskSwitchButton : public QskAbstractButton bool isCheckable() const override final; Qt::Orientation orientation() const; - void setOrientation(Qt::Orientation); + void setOrientation( Qt::Orientation ); bool isInverted() const; void setInverted( bool ); + IconMode iconMode() const; + void setIconMode( IconMode ); + QskAspect::Variation effectiveVariation() const override; Q_SIGNALS: void orientationChanged( Qt::Orientation ); void invertedChanged( bool ); + void iconModeChanged( IconMode ); private: struct PrivateData; diff --git a/src/controls/QskSwitchButtonSkinlet.cpp b/src/controls/QskSwitchButtonSkinlet.cpp index 2a62ac08..471c39d0 100644 --- a/src/controls/QskSwitchButtonSkinlet.cpp +++ b/src/controls/QskSwitchButtonSkinlet.cpp @@ -6,6 +6,8 @@ #include "QskSwitchButtonSkinlet.h" #include "QskSwitchButton.h" +using Q = QskSwitchButton; + static inline qreal qskEffectivePosition( const QskSwitchButton* switchButton ) { auto pos = switchButton->positionHint( QskSwitchButton::Handle ); @@ -23,10 +25,23 @@ static inline qreal qskEffectivePosition( const QskSwitchButton* switchButton ) return pos; } +static QSizeF qskIconSize( const QskSwitchButton* button ) +{ + if( button->iconMode() == Q::NoIcon + || ( button->iconMode() == Q::ShowIconWhenSelected && !button->isChecked() ) ) + { + return {}; + } + else + { + return button->strutSizeHint( Q::Icon ); + } +} + QskSwitchButtonSkinlet::QskSwitchButtonSkinlet( QskSkin* skin ) : Inherited( skin ) { - setNodeRoles( { GrooveRole, HandleRole } ); + setNodeRoles( { GrooveRole, HandleRole, IconRole } ); } QskSwitchButtonSkinlet::~QskSwitchButtonSkinlet() @@ -36,16 +51,21 @@ QskSwitchButtonSkinlet::~QskSwitchButtonSkinlet() QRectF QskSwitchButtonSkinlet::subControlRect( const QskSkinnable* skinnable, const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const { - using Q = QskSwitchButton; - - if ( subControl == Q::Handle ) - { - return handleRect( skinnable, contentsRect ); - } + const auto button = static_cast< const Q* >( skinnable ); if ( subControl == Q::Groove ) { - return grooveRect( skinnable, contentsRect ); + return grooveRect( button, contentsRect ); + } + + if ( subControl == Q::Handle ) + { + return handleRect( button, contentsRect ); + } + + if ( subControl == Q::Icon ) + { + return iconRect( button, contentsRect ); } return Inherited::subControlRect( skinnable, contentsRect, subControl ); @@ -69,13 +89,14 @@ QSizeF QskSwitchButtonSkinlet::sizeHint( const QskSkinnable* skinnable, QSGNode* QskSwitchButtonSkinlet::updateSubNode( const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const { - using Q = QskSwitchButton; - switch ( nodeRole ) { case HandleRole: return updateBoxNode( skinnable, node, Q::Handle ); + case IconRole: + return updateSymbolNode( skinnable, node, Q::Icon ); + case GrooveRole: return updateBoxNode( skinnable, node, Q::Groove ); } @@ -84,19 +105,15 @@ QSGNode* QskSwitchButtonSkinlet::updateSubNode( const QskSkinnable* skinnable, } QRectF QskSwitchButtonSkinlet::grooveRect( - const QskSkinnable* skinnable, const QRectF& contentsRect ) const + const QskSwitchButton* button, const QRectF& contentsRect ) const { - using Q = QskSwitchButton; + auto size = button->strutSizeHint( Q::Groove ); - const auto switchButton = static_cast< const Q* >( skinnable ); - - auto size = skinnable->strutSizeHint( Q::Groove ); - - if ( switchButton->orientation() == Qt::Vertical ) + if ( button->orientation() == Qt::Vertical ) { if ( size.height() < 0.0 ) { - const auto handleSize = skinnable->strutSizeHint( Q::Handle ); + const auto handleSize = button->strutSizeHint( Q::Handle ); size.setHeight( 2 * handleSize.height() ); } } @@ -104,7 +121,7 @@ QRectF QskSwitchButtonSkinlet::grooveRect( { if ( size.width() < 0.0 ) { - const auto handleSize = skinnable->strutSizeHint( Q::Handle ); + const auto handleSize = button->strutSizeHint( Q::Handle ); size.setWidth( 2 * handleSize.width() ); } } @@ -119,19 +136,16 @@ QRectF QskSwitchButtonSkinlet::grooveRect( } QRectF QskSwitchButtonSkinlet::handleRect( - const QskSkinnable* skinnable, const QRectF& contentsRect ) const + const QskSwitchButton* button, const QRectF& contentsRect ) const { - using Q = QskSwitchButton; + const auto grooveRect = subControlRect( button, contentsRect, Q::Groove ); + const auto pos = qskEffectivePosition( button ); - const auto switchButton = static_cast< const Q* >( skinnable ); - - const auto grooveRect = subControlRect( skinnable, contentsRect, Q::Groove ); - const auto pos = qskEffectivePosition( switchButton ); - const auto size = skinnable->strutSizeHint( Q::Handle ); + auto size = button->strutSizeHint( Q::Handle ); qreal cx, cy; - if( switchButton->orientation() == Qt::Vertical ) + if( button->orientation() == Qt::Vertical ) { const qreal y0 = grooveRect.y() + 0.5 * size.height(); const qreal h = grooveRect.height() - size.height(); @@ -148,6 +162,20 @@ QRectF QskSwitchButtonSkinlet::handleRect( cy = grooveRect.y() + 0.5 * grooveRect.height(); } + auto iconSize = qskIconSize( button ); + + if( !iconSize.isNull() ) + { + auto padding = button->paddingHint( Q::Icon ); + + // need to compensate for the margins, + // which might differ between states: + auto margins = button->marginHint( Q::Handle ); + + iconSize = iconSize.grownBy( padding ).grownBy( margins ); + size = size.expandedTo( iconSize ); + } + QRectF r; r.setSize( size ); r.moveCenter( QPointF( cx, cy ) ); @@ -155,4 +183,14 @@ QRectF QskSwitchButtonSkinlet::handleRect( return r; } +QRectF QskSwitchButtonSkinlet::iconRect( const QskSwitchButton* button, const QRectF& contentsRect ) const +{ + QRectF rect; + rect.setSize( qskIconSize( button ) ); + const auto hr = handleRect( button, contentsRect ); + rect.moveCenter( hr.center() ); + return rect; +} + + #include "moc_QskSwitchButtonSkinlet.cpp" diff --git a/src/controls/QskSwitchButtonSkinlet.h b/src/controls/QskSwitchButtonSkinlet.h index f39fbf3c..1f7e614b 100644 --- a/src/controls/QskSwitchButtonSkinlet.h +++ b/src/controls/QskSwitchButtonSkinlet.h @@ -8,6 +8,8 @@ #include "QskSkinlet.h" +class QskSwitchButton; + class QSK_EXPORT QskSwitchButtonSkinlet : public QskSkinlet { Q_GADGET @@ -19,6 +21,7 @@ class QSK_EXPORT QskSwitchButtonSkinlet : public QskSkinlet { GrooveRole, HandleRole, + IconRole, RoleCount }; @@ -37,8 +40,9 @@ class QSK_EXPORT QskSwitchButtonSkinlet : public QskSkinlet quint8 nodeRole, QSGNode* ) const override; private: - QRectF grooveRect( const QskSkinnable*, const QRectF& ) const; - QRectF handleRect( const QskSkinnable*, const QRectF& ) const; + QRectF grooveRect( const QskSwitchButton*, const QRectF& ) const; + QRectF handleRect( const QskSwitchButton*, const QRectF& ) const; + QRectF iconRect( const QskSwitchButton*, const QRectF& ) const; }; #endif