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 00000000..c927ddb3
Binary files /dev/null and b/designsystems/material3/icons/qvg/switchbutton-checked.qvg differ
diff --git a/designsystems/material3/icons/qvg/switchbutton-unchecked.qvg b/designsystems/material3/icons/qvg/switchbutton-unchecked.qvg
new file mode 100644
index 00000000..b33f61e4
Binary files /dev/null and b/designsystems/material3/icons/qvg/switchbutton-unchecked.qvg differ
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