Fluid 1.3.0
Material Design for QtQuick and Qml
Loading...
Searching...
No Matches
Switch.qml
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
2// SPDX-License-Identifier: MPL-2.0
3
4import QtQuick
5import QtQuick.Templates as T
6import Fluid as MD
7import "../core/UiMetrics.js" as UiMetrics
8import "../internal/MotionAnimation.js" as MotionAnimation
9
20T.Switch {
21 id: control
22
26 enum IconConfiguration {
28 NoIcons,
30 SelectedIcon,
32 BothIcons
33 }
34
36 property MD.typescale typescale: MD.Tokens.typescale.labelLarge
37
43 property int iconConfiguration: Switch.IconConfiguration.NoIcons
44
45 implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
46 implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding, implicitIndicatorHeight + topPadding + bottomPadding)
47
48 padding: MD.Tokens.switch.contentPadding
49 spacing: MD.Tokens.switch.contentSpacing
50
51 hoverEnabled: true
52 focusPolicy: Qt.StrongFocus
53
54 indicator: Item {
55 id: switchIndicator
56
57 implicitWidth: MD.Tokens.switch.trackWidth
58 implicitHeight: MD.Tokens.switch.trackHeight
59
60 readonly property real handleCenterX: height / 2 + control.visualPosition * (width - height)
61
62 x: control.text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
63 y: control.topPadding + (control.availableHeight - height) / 2
64
65 QtObject {
66 id: indicatorState
67
68 // Track
69 property color trackColor: {
70 if (!control.enabled)
71 return control.checked ? control.MD.Style.onSurfaceColor : control.MD.Style.surfaceContainerHighestColor;
72 return control.checked ? control.MD.Style.primaryColor : control.MD.Style.surfaceContainerHighestColor;
73 }
74 property real trackOpacity: control.enabled ? 1.0 : MD.Tokens.switch.disabledTrackOpacity
75 property real borderWidth: control.checked ? 0 : MD.Tokens.switch.trackOutlineWidth
76 property color borderColor: control.enabled ? control.MD.Style.outlineColor : control.MD.Style.onSurfaceColor
77
78 // Handle
79 property color handleColor: {
80 if (!control.enabled)
81 return control.checked ? control.MD.Style.surfaceColor : control.MD.Style.onSurfaceColor;
82 if (control.checked)
83 return control.MD.Style.onPrimaryColor;
84 return control.iconConfiguration === Switch.IconConfiguration.BothIcons ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.outlineColor;
85 }
86 property real handleSize: control.pressed ? MD.Tokens.switch.pressedHandleSize : (control.checked ? MD.Tokens.switch.selectedHandleSize : (control.iconConfiguration === Switch.IconConfiguration.BothIcons ? MD.Tokens.switch.withIconHandleSize : MD.Tokens.switch.unselectedHandleSize))
87 property real handleOpacity: !control.enabled ? (control.checked ? MD.Tokens.switch.disabledSelectedHandleOpacity : MD.Tokens.switch.disabledUnselectedHandleOpacity) : 1.0
88
89 // Icon
90 property bool showIcon: control.iconConfiguration === Switch.IconConfiguration.BothIcons || (control.iconConfiguration === Switch.IconConfiguration.SelectedIcon && control.checked)
91 property string iconName: {
92 if (control.checked)
93 return control.icon.name.length > 0 ? control.icon.name : "check";
94 if (control.iconConfiguration === Switch.IconConfiguration.BothIcons)
95 return "close";
96 // SelectedIcon + unselected: keep the check name while fading out
97 return control.icon.name.length > 0 ? control.icon.name : "check";
98 }
99 property color iconColor: control.checked ? control.MD.Style.primaryColor : control.MD.Style.surfaceContainerHighestColor
100
101 // State layer
102 property color stateLayerColor: control.checked ? control.MD.Style.primaryColor : control.MD.Style.onSurfaceVariantColor
103 property real stateLayerOpacity: 0
104 }
105
106 states: [
107 State {
108 name: "hovered"
109 when: control.hovered && control.enabled && !control.pressed
110 PropertyChanges {
111 indicatorState.stateLayerOpacity: MD.Tokens.switch.hoverStateLayerOpacity
112 }
113 },
114 State {
115 name: "focused"
116 when: control.visualFocus && control.enabled && !control.pressed
117 PropertyChanges {
118 indicatorState.stateLayerOpacity: MD.Tokens.switch.focusStateLayerOpacity
119 }
120 },
121 State {
122 name: "pressed"
123 when: control.pressed && control.enabled
124 PropertyChanges {
125 indicatorState.stateLayerOpacity: MD.Tokens.switch.pressedStateLayerOpacity
126 }
127 }
128 ]
129
130 // Track
131 Rectangle {
132 objectName: "switchTrack"
133 anchors.fill: parent
134 topLeftRadius: UiMetrics.resolveShapeRadius(MD.Tokens.switch.trackShape.topLeft,
135 width, height)
136 topRightRadius: UiMetrics.resolveShapeRadius(MD.Tokens.switch.trackShape.topRight,
137 width, height)
138 bottomLeftRadius: UiMetrics.resolveShapeRadius(MD.Tokens.switch.trackShape.bottomLeft,
139 width, height)
140 bottomRightRadius: UiMetrics.resolveShapeRadius(
141 MD.Tokens.switch.trackShape.bottomRight, width, height)
142 color: indicatorState.trackColor
143 opacity: indicatorState.trackOpacity
144 border.width: indicatorState.borderWidth
145 border.color: indicatorState.borderColor
146
147 Behavior on color {
148 ColorAnimation {
149 duration: MD.Tokens.motion.duration.short2
150 }
151 }
152 Behavior on border.color {
153 ColorAnimation {
154 duration: MD.Tokens.motion.duration.short2
155 }
156 }
157 Behavior on border.width {
158 NumberAnimation {
159 duration: MD.Tokens.motion.duration.short2
160 }
161 }
162 }
163
164 // State layer + handle, in a 40dp container centered on the handle position
165 // Handle center: 16dp from left (unchecked) or 36dp from left (checked)
166 // Container x = handleCenter - 20
167 Item {
168 id: handleArea
169 width: MD.Tokens.switch.stateLayerSize
170 height: MD.Tokens.switch.stateLayerSize
171 y: (parent.height - height) / 2
172 x: switchIndicator.handleCenterX - width / 2
173
174 Behavior on x {
175 NumberAnimation {
176 easing.type: Easing.BezierSpline
177 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
178 duration: MotionAnimation.expressiveFastSpatialDuration
179 }
180 }
181
182 MD.Ripple {
183 objectName: "switchStateLayer"
184 anchors.fill: parent
185 topLeftRadius: UiMetrics.resolveShapeRadius(
186 MD.Tokens.switch.stateLayerShape.topLeft, width, height)
187 topRightRadius: UiMetrics.resolveShapeRadius(
188 MD.Tokens.switch.stateLayerShape.topRight, width, height)
189 bottomLeftRadius: UiMetrics.resolveShapeRadius(
190 MD.Tokens.switch.stateLayerShape.bottomLeft, width, height)
191 bottomRightRadius: UiMetrics.resolveShapeRadius(
192 MD.Tokens.switch.stateLayerShape.bottomRight, width,
193 height)
194 pressed: control.pressed
195 pressX: width / 2
196 pressY: height / 2
197 stateOpacity: indicatorState.stateLayerOpacity
198 color: indicatorState.stateLayerColor
199 }
200
201 Rectangle {
202 objectName: "switchHandle"
203 anchors.centerIn: parent
204 width: indicatorState.handleSize
205 height: width
206 topLeftRadius: UiMetrics.resolveShapeRadius(MD.Tokens.switch.handleShape.topLeft,
207 width, height)
208 topRightRadius: UiMetrics.resolveShapeRadius(MD.Tokens.switch.handleShape.topRight,
209 width, height)
210 bottomLeftRadius: UiMetrics.resolveShapeRadius(
211 MD.Tokens.switch.handleShape.bottomLeft, width, height)
212 bottomRightRadius: UiMetrics.resolveShapeRadius(
213 MD.Tokens.switch.handleShape.bottomRight, width, height)
214 color: indicatorState.handleColor
215 opacity: indicatorState.handleOpacity
216
217 Behavior on width {
218 NumberAnimation {
219 easing.type: Easing.BezierSpline
220 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
221 duration: MotionAnimation.expressiveFastSpatialDuration
222 }
223 }
224
225 Behavior on color {
226 ColorAnimation {
227 duration: MD.Tokens.motion.duration.short2
228 }
229 }
230
231 MD.Symbol {
232 anchors.centerIn: parent
233 iconWidth: control.checked ? MD.Tokens.switch.selectedIconSize : MD.Tokens.switch.unselectedIconSize
234 iconHeight: iconWidth
235 name: indicatorState.iconName
236 color: indicatorState.iconColor
237 opacity: indicatorState.showIcon ? 1.0 : 0.0
238
239 Behavior on opacity {
240 NumberAnimation {
241 duration: MD.Tokens.motion.duration.short2
242 }
243 }
244
245 Behavior on color {
246 ColorAnimation {
247 duration: MD.Tokens.motion.duration.short2
248 }
249 }
250 }
251 }
252 }
253 }
254
255 contentItem: MD.Label {
256 leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
257 rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
258
259 text: control.text
260 typescale: control.typescale
261 color: control.enabled ? control.MD.Style.onSurfaceColor : control.MD.Style.onSurfaceVariantColor
262 elide: Text.ElideRight
263 verticalAlignment: Text.AlignVCenter
264 }
265}
Material Design 3 design tokens.
Definition tokens.h:65
Displays text using a selectable Material 3 type scale.
Definition Label.qml:15
Material Design 3 switch tokens.
Definition switch.h:20
qreal stateLayerSize
Definition switch.h:28
qreal withIconHandleSize
Definition switch.h:34
qreal pressedHandleSize
Definition switch.h:35
qreal contentPadding
Definition switch.h:30
Material Symbols icon tokens.
Definition symbol.h:17