Fluid 1.3.0
Material Design for QtQuick and Qml
Loading...
Searching...
No Matches
IconButton.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 QtQuick.Controls.impl as TImpl
7import Fluid as MD
8import "../core/UiMetrics.js" as UiMetrics
9import "../internal/MotionAnimation.js" as MotionAnimation
10
24T.ToolButton {
25 id: control
26
27 component IconData: QtObject {
28 property string name
29 property url source
30 property color color
31 property bool fill: false
32 }
33
35 enum Type {
36 Filled,
37 Tonal,
38 Outlined,
39 Standard
40 }
41
46 enum Shape {
47 Round,
48 Square
49 }
50
52 enum Size {
53 ExtraSmall,
54 Small,
55 Medium,
56 Large,
57 ExtraLarge
58 }
59
61 enum Width {
62 Default,
63 Narrow,
64 Wide
65 }
66
72 property int type: IconButton.Type.Filled
73
79 property int shape: IconButton.Shape.Round
80
87 property int size: IconButton.Size.Small
88
94 property int widthVariant: IconButton.Width.Default
95
102 property IconData checkedIcon: IconData {}
103
105 property bool mirrorIconInRtl: false
106
111 property bool tintSourceIcon: true
112
117 property color contentColor: {
118 switch (type) {
119 case IconButton.Type.Filled:
120 return checked ? MD.Style.onPrimaryColor : MD.Style.onSurfaceVariantColor;
121 case IconButton.Type.Tonal:
122 return checked ? MD.Style.onSecondaryColor : MD.Style.onSecondaryContainerColor;
123 case IconButton.Type.Outlined:
124 return checked ? MD.Style.inverseOnSurfaceColor : MD.Style.onSurfaceVariantColor;
125 case IconButton.Type.Standard:
126 return checked ? MD.Style.primaryColor : MD.Style.onSurfaceVariantColor;
127 }
128 }
129
134 property color containerColor: {
135 switch (type) {
136 case IconButton.Type.Filled:
137 return checked ? MD.Style.primaryColor : MD.Style.surfaceContainerColor;
138 case IconButton.Type.Tonal:
139 return checked ? MD.Style.secondaryColor : MD.Style.secondaryContainerColor;
140 case IconButton.Type.Outlined:
141 return checked ? MD.Style.inverseSurfaceColor : "transparent";
142 case IconButton.Type.Standard:
143 return "transparent";
144 }
145 }
146
148 property color disabledContentColor: MD.Style.onSurfaceColor
149
151 property color disabledContainerColor: MD.Style.onSurfaceColor
152
154 property color outlineColor: MD.Style.outlineVariantColor
155
157 readonly property color effectiveContentColor: state.contentColor
158
160 readonly property real effectiveContentOpacity: state.contentOpacity
161
163 readonly property color effectiveContainerColor: state.containerColor
164
166 readonly property bool usingCheckedIcon: checked && (checkedIcon.name.length > 0 || checkedIcon.source.toString().length > 0)
167
169 readonly property string effectiveIconName: usingCheckedIcon ? checkedIcon.name : icon.name
170
172 readonly property url effectiveIconSource: usingCheckedIcon ? checkedIcon.source : icon.source
173
175 readonly property color effectiveIconColor: {
176 const explicitColor = usingCheckedIcon ? checkedIcon.color : icon.color;
177 return explicitColor.a > 0 ? explicitColor : effectiveContentColor;
178 }
179
181 readonly property bool effectiveIconFill: checked && (usingCheckedIcon ? checkedIcon.fill : true)
182
184 readonly property bool effectiveIconMirrored: mirrorIconInRtl && mirrored
185
186 icon.width: state.iconSize.width
187 icon.height: state.iconSize.height
188
189 implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
190 implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding)
191
192 leftInset: state.inset.width / 2
193 rightInset: state.inset.width / 2
194 topInset: state.inset.height / 2
195 bottomInset: state.inset.height / 2
196
197 padding: 0
198
199 spacing: 0
200
201 hoverEnabled: true
202 focusPolicy: Qt.StrongFocus
203
204 QtObject {
205 id: state
206
207 readonly property real currentContainerHeight: {
208 switch (control.size) {
209 case IconButton.Size.ExtraSmall: return MD.Tokens.iconButton.containerHeightExtraSmall;
210 case IconButton.Size.Small: return MD.Tokens.iconButton.containerHeightSmall;
211 case IconButton.Size.Medium: return MD.Tokens.iconButton.containerHeightMedium;
212 case IconButton.Size.Large: return MD.Tokens.iconButton.containerHeightLarge;
213 case IconButton.Size.ExtraLarge: return MD.Tokens.iconButton.containerHeightExtraLarge;
214 }
215 }
216 readonly property real currentIconSize: {
217 switch (control.size) {
218 case IconButton.Size.ExtraSmall: return MD.Tokens.iconButton.iconSizeExtraSmall;
219 case IconButton.Size.Small: return MD.Tokens.iconButton.iconSizeSmall;
220 case IconButton.Size.Medium: return MD.Tokens.iconButton.iconSizeMedium;
221 case IconButton.Size.Large: return MD.Tokens.iconButton.iconSizeLarge;
222 case IconButton.Size.ExtraLarge: return MD.Tokens.iconButton.iconSizeExtraLarge;
223 }
224 }
225 readonly property real currentLeadingSpace: {
226 if (control.widthVariant === IconButton.Width.Narrow) {
227 switch (control.size) {
228 case IconButton.Size.ExtraSmall: return MD.Tokens.iconButton.narrowLeadingSpaceExtraSmall;
229 case IconButton.Size.Small: return MD.Tokens.iconButton.narrowLeadingSpaceSmall;
230 case IconButton.Size.Medium: return MD.Tokens.iconButton.narrowLeadingSpaceMedium;
231 case IconButton.Size.Large: return MD.Tokens.iconButton.narrowLeadingSpaceLarge;
232 case IconButton.Size.ExtraLarge: return MD.Tokens.iconButton.narrowLeadingSpaceExtraLarge;
233 }
234 }
235 if (control.widthVariant === IconButton.Width.Wide) {
236 switch (control.size) {
237 case IconButton.Size.ExtraSmall: return MD.Tokens.iconButton.wideLeadingSpaceExtraSmall;
238 case IconButton.Size.Small: return MD.Tokens.iconButton.wideLeadingSpaceSmall;
239 case IconButton.Size.Medium: return MD.Tokens.iconButton.wideLeadingSpaceMedium;
240 case IconButton.Size.Large: return MD.Tokens.iconButton.wideLeadingSpaceLarge;
241 case IconButton.Size.ExtraLarge: return MD.Tokens.iconButton.wideLeadingSpaceExtraLarge;
242 }
243 }
244 switch (control.size) {
245 case IconButton.Size.ExtraSmall: return MD.Tokens.iconButton.defaultLeadingSpaceExtraSmall;
246 case IconButton.Size.Small: return MD.Tokens.iconButton.defaultLeadingSpaceSmall;
247 case IconButton.Size.Medium: return MD.Tokens.iconButton.defaultLeadingSpaceMedium;
248 case IconButton.Size.Large: return MD.Tokens.iconButton.defaultLeadingSpaceLarge;
249 case IconButton.Size.ExtraLarge: return MD.Tokens.iconButton.defaultLeadingSpaceExtraLarge;
250 }
251 }
252 readonly property real currentTrailingSpace: {
253 if (control.widthVariant === IconButton.Width.Narrow) {
254 switch (control.size) {
255 case IconButton.Size.ExtraSmall: return MD.Tokens.iconButton.narrowTrailingSpaceExtraSmall;
256 case IconButton.Size.Small: return MD.Tokens.iconButton.narrowTrailingSpaceSmall;
257 case IconButton.Size.Medium: return MD.Tokens.iconButton.narrowTrailingSpaceMedium;
258 case IconButton.Size.Large: return MD.Tokens.iconButton.narrowTrailingSpaceLarge;
259 case IconButton.Size.ExtraLarge: return MD.Tokens.iconButton.narrowTrailingSpaceExtraLarge;
260 }
261 }
262 if (control.widthVariant === IconButton.Width.Wide) {
263 switch (control.size) {
264 case IconButton.Size.ExtraSmall: return MD.Tokens.iconButton.wideTrailingSpaceExtraSmall;
265 case IconButton.Size.Small: return MD.Tokens.iconButton.wideTrailingSpaceSmall;
266 case IconButton.Size.Medium: return MD.Tokens.iconButton.wideTrailingSpaceMedium;
267 case IconButton.Size.Large: return MD.Tokens.iconButton.wideTrailingSpaceLarge;
268 case IconButton.Size.ExtraLarge: return MD.Tokens.iconButton.wideTrailingSpaceExtraLarge;
269 }
270 }
271 switch (control.size) {
272 case IconButton.Size.ExtraSmall: return MD.Tokens.iconButton.defaultTrailingSpaceExtraSmall;
273 case IconButton.Size.Small: return MD.Tokens.iconButton.defaultTrailingSpaceSmall;
274 case IconButton.Size.Medium: return MD.Tokens.iconButton.defaultTrailingSpaceMedium;
275 case IconButton.Size.Large: return MD.Tokens.iconButton.defaultTrailingSpaceLarge;
276 case IconButton.Size.ExtraLarge: return MD.Tokens.iconButton.defaultTrailingSpaceExtraLarge;
277 }
278 }
279 readonly property real outlineWidth: {
280 switch (control.size) {
281 case IconButton.Size.ExtraSmall: return MD.Tokens.iconButton.outlinedOutlineWidthExtraSmall;
282 case IconButton.Size.Small: return MD.Tokens.iconButton.outlinedOutlineWidthSmall;
283 case IconButton.Size.Medium: return MD.Tokens.iconButton.outlinedOutlineWidthMedium;
284 case IconButton.Size.Large: return MD.Tokens.iconButton.outlinedOutlineWidthLarge;
285 case IconButton.Size.ExtraLarge: return MD.Tokens.iconButton.outlinedOutlineWidthExtraLarge;
286 }
287 }
288 property size buttonSize: Qt.size(currentIconSize + currentLeadingSpace + currentTrailingSpace, currentContainerHeight)
289 property size iconSize: Qt.size(currentIconSize, currentIconSize)
290
291 property size inset: {
292 const minimum = MD.Tokens.iconButton.minimumInteractiveSize;
293 return Qt.size(Math.max(minimum, state.buttonSize.width) - state.buttonSize.width,
294 Math.max(minimum, state.buttonSize.height) - state.buttonSize.height);
295 }
296
297 property MD.shapeValue containerShape: {
298 const tokens = MD.Tokens.iconButton;
299 if (control.pressed) {
300 switch (control.size) {
301 case IconButton.Size.ExtraSmall: return tokens.pressedContainerShapeExtraSmall;
302 case IconButton.Size.Small: return tokens.pressedContainerShapeSmall;
303 case IconButton.Size.Medium: return tokens.pressedContainerShapeMedium;
304 case IconButton.Size.Large: return tokens.pressedContainerShapeLarge;
305 case IconButton.Size.ExtraLarge: return tokens.pressedContainerShapeExtraLarge;
306 }
307 }
308 if (control.checked) {
309 if (control.shape === IconButton.Shape.Square)
310 return tokens.selectedContainerShapeSquare;
311 switch (control.size) {
312 case IconButton.Size.ExtraSmall: return tokens.selectedContainerShapeRoundExtraSmall;
313 case IconButton.Size.Small: return tokens.selectedContainerShapeRoundSmall;
314 case IconButton.Size.Medium: return tokens.selectedContainerShapeRoundMedium;
315 case IconButton.Size.Large: return tokens.selectedContainerShapeRoundLarge;
316 case IconButton.Size.ExtraLarge: return tokens.selectedContainerShapeRoundExtraLarge;
317 }
318 }
319 if (control.shape === IconButton.Shape.Round)
320 return tokens.containerShapeRound;
321 switch (control.size) {
322 case IconButton.Size.ExtraSmall: return tokens.containerShapeSquareExtraSmall;
323 case IconButton.Size.Small: return tokens.containerShapeSquareSmall;
324 case IconButton.Size.Medium: return tokens.containerShapeSquareMedium;
325 case IconButton.Size.Large: return tokens.containerShapeSquareLarge;
326 case IconButton.Size.ExtraLarge: return tokens.containerShapeSquareExtraLarge;
327 }
328 }
329
330 property color containerColor: control.containerColor
331 property color contentColor: control.contentColor
332 property color stateLayerColor: "transparent"
333
334 property real contentOpacity: 1.0
335 property real containerOpacity: 1.0
336 property real stateLayerOpacity: 0.0
337 }
338
339 states: [
340 State {
341 name: "disabled"
342 when: !control.enabled
343
344 PropertyChanges {
345 state.containerColor: control.type === IconButton.Type.Standard ? "transparent" : control.disabledContainerColor
346 state.contentColor: control.disabledContentColor
347 state.containerOpacity: MD.Tokens.iconButton.disabledContainerOpacity
348 state.contentOpacity: MD.Tokens.iconButton.disabledIconOpacity
349 }
350 },
351 State {
352 name: "pressed"
353 when: control.down && control.enabled
354
355 PropertyChanges {
356 state.stateLayerColor: control.effectiveIconColor
357 state.stateLayerOpacity: MD.Tokens.iconButton.pressedStateLayerOpacity
358 }
359 },
360 State {
361 name: "focused"
362 when: control.visualFocus && control.enabled
363
364 PropertyChanges {
365 state.stateLayerColor: control.effectiveIconColor
366 state.stateLayerOpacity: MD.Tokens.iconButton.focusStateLayerOpacity
367 }
368 },
369 State {
370 name: "hovered"
371 when: control.hovered && control.enabled
372
373 PropertyChanges {
374 state.stateLayerColor: control.effectiveIconColor
375 state.stateLayerOpacity: MD.Tokens.iconButton.hoverStateLayerOpacity
376 }
377 }
378 ]
379
380 contentItem: Item {
381 id: iconContent
382
383 implicitWidth: control.icon.width
384 implicitHeight: control.icon.height
385
386 transform: Scale {
387 origin.x: iconContent.width / 2
388 origin.y: iconContent.height / 2
389 xScale: control.effectiveIconMirrored ? -1 : 1
390 }
391
392 TImpl.IconImage {
393 id: sourceIcon
394
395 objectName: "iconButtonSourceImage"
396 anchors.centerIn: parent
397 width: control.icon.width
398 height: control.icon.height
399 source: control.effectiveIconSource
400 sourceSize: Qt.size(control.icon.width, control.icon.height)
401 fillMode: Image.PreserveAspectFit
402 color: control.tintSourceIcon ? control.effectiveIconColor : "transparent"
403 opacity: state.contentOpacity
404 visible: control.effectiveIconSource.toString().length > 0
405 }
406
407 MD.Symbol {
408 objectName: "iconButtonSymbol"
409 anchors.centerIn: parent
410 name: control.effectiveIconName
411 iconWidth: control.icon.width
412 iconHeight: control.icon.height
413 color: control.effectiveIconColor
414 opacity: state.contentOpacity
415 fill: control.effectiveIconFill
416 visible: control.effectiveIconSource.toString().length === 0 && name.length > 0
417 }
418 }
419
420 background: Rectangle {
421 implicitWidth: state.buttonSize.width
422 implicitHeight: state.buttonSize.height
423
424 topLeftRadius: UiMetrics.resolveShapeRadius(state.containerShape.topLeft, width, height)
425 topRightRadius: UiMetrics.resolveShapeRadius(state.containerShape.topRight, width, height)
426 bottomLeftRadius: UiMetrics.resolveShapeRadius(state.containerShape.bottomLeft, width, height)
427 bottomRightRadius: UiMetrics.resolveShapeRadius(state.containerShape.bottomRight, width, height)
428
429 Behavior on topLeftRadius {
430 NumberAnimation {
431 easing.type: Easing.BezierSpline
432 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
433 duration: MotionAnimation.expressiveFastSpatialDuration
434 }
435 }
436 Behavior on topRightRadius {
437 NumberAnimation {
438 easing.type: Easing.BezierSpline
439 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
440 duration: MotionAnimation.expressiveFastSpatialDuration
441 }
442 }
443 Behavior on bottomLeftRadius {
444 NumberAnimation {
445 easing.type: Easing.BezierSpline
446 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
447 duration: MotionAnimation.expressiveFastSpatialDuration
448 }
449 }
450 Behavior on bottomRightRadius {
451 NumberAnimation {
452 easing.type: Easing.BezierSpline
453 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
454 duration: MotionAnimation.expressiveFastSpatialDuration
455 }
456 }
457
458 border.width: control.type === IconButton.Type.Outlined && !control.checked ? state.outlineWidth : 0
459 border.color: control.outlineColor
460
461 opacity: state.containerOpacity
462 color: state.containerColor
463
464 MD.Ripple {
465 anchors.fill: parent
466
467 topLeftRadius: parent.topLeftRadius
468 topRightRadius: parent.topRightRadius
469 bottomLeftRadius: parent.bottomLeftRadius
470 bottomRightRadius: parent.bottomRightRadius
471
472 pressed: control.pressed
473 pressX: control.pressX
474 pressY: control.pressY
475
476 stateOpacity: state.stateLayerOpacity
477 color: state.stateLayerColor
478 }
479 }
480}
Material Design 3 Expressive icon button tokens.
Definition iconbutton.h:20
qreal wideLeadingSpaceExtraSmall
Definition iconbutton.h:47
qreal wideTrailingSpaceMedium
Definition iconbutton.h:68
qreal narrowLeadingSpaceSmall
Definition iconbutton.h:43
Fluid::ShapeValue containerShapeSquareMedium
Definition iconbutton.h:83
Fluid::ShapeValue selectedContainerShapeRoundMedium
Definition iconbutton.h:100
qreal narrowLeadingSpaceLarge
Definition iconbutton.h:45
qreal wideLeadingSpaceExtraLarge
Definition iconbutton.h:51
qreal defaultTrailingSpaceExtraSmall
Definition iconbutton.h:53
Fluid::ShapeValue containerShapeSquareSmall
Definition iconbutton.h:82
qreal containerHeightExtraSmall
Definition iconbutton.h:25
qreal defaultTrailingSpaceMedium
Definition iconbutton.h:55
qreal outlinedOutlineWidthExtraLarge
Definition iconbutton.h:77
Fluid::ShapeValue pressedContainerShapeMedium
Definition iconbutton.h:91
qreal wideLeadingSpaceLarge
Definition iconbutton.h:50
qreal narrowLeadingSpaceExtraLarge
Definition iconbutton.h:46
Fluid::ShapeValue selectedContainerShapeRoundExtraLarge READ selectedContainerShapeRoundExtraLarge
Definition iconbutton.h:104
qreal outlinedOutlineWidthExtraSmall
Definition iconbutton.h:72
qreal wideLeadingSpaceMedium
Definition iconbutton.h:49
qreal disabledContainerOpacity
Definition iconbutton.h:106
qreal containerHeightExtraLarge
Definition iconbutton.h:29
qreal defaultTrailingSpaceLarge
Definition iconbutton.h:56
qreal outlinedOutlineWidthMedium
Definition iconbutton.h:74
Fluid::ShapeValue containerShapeSquareExtraSmall
Definition iconbutton.h:81
Fluid::ShapeValue pressedContainerShapeExtraSmall
Definition iconbutton.h:89
qreal wideTrailingSpaceSmall
Definition iconbutton.h:67
Fluid::ShapeValue containerShapeSquareExtraLarge
Definition iconbutton.h:87
Fluid::ShapeValue selectedContainerShapeRoundExtraSmall READ selectedContainerShapeRoundExtraSmall
Definition iconbutton.h:96
Fluid::ShapeValue pressedContainerShapeExtraLarge
Definition iconbutton.h:94
qreal narrowLeadingSpaceMedium
Definition iconbutton.h:44
qreal outlinedOutlineWidthLarge
Definition iconbutton.h:75
qreal narrowTrailingSpaceSmall
Definition iconbutton.h:61
qreal containerHeightLarge
Definition iconbutton.h:28
qreal outlinedOutlineWidthSmall
Definition iconbutton.h:73
Fluid::ShapeValue selectedContainerShapeRoundSmall
Definition iconbutton.h:98
Fluid::ShapeValue pressedContainerShapeLarge
Definition iconbutton.h:92
qreal defaultLeadingSpaceSmall
Definition iconbutton.h:37
qreal defaultLeadingSpaceExtraLarge
Definition iconbutton.h:41
qreal wideTrailingSpaceExtraLarge
Definition iconbutton.h:70
qreal iconSizeExtraSmall
Definition iconbutton.h:30
Fluid::ShapeValue selectedContainerShapeRoundLarge
Definition iconbutton.h:102
Fluid::ShapeValue pressedContainerShapeSmall
Definition iconbutton.h:90
qreal defaultLeadingSpaceLarge
Definition iconbutton.h:39
qreal defaultTrailingSpaceExtraLarge
Definition iconbutton.h:58
Fluid::ShapeValue containerShapeSquareLarge
Definition iconbutton.h:84
qreal iconSizeExtraLarge
Definition iconbutton.h:34
qreal defaultLeadingSpaceExtraSmall
Definition iconbutton.h:36
qreal narrowTrailingSpaceExtraSmall
Definition iconbutton.h:60
qreal narrowLeadingSpaceExtraSmall
Definition iconbutton.h:42
qreal wideTrailingSpaceExtraSmall
Definition iconbutton.h:66
qreal containerHeightMedium
Definition iconbutton.h:27
qreal narrowTrailingSpaceExtraLarge
Definition iconbutton.h:65
qreal defaultTrailingSpaceSmall
Definition iconbutton.h:54
qreal wideTrailingSpaceLarge
Definition iconbutton.h:69
qreal defaultLeadingSpaceMedium
Definition iconbutton.h:38
qreal wideLeadingSpaceSmall
Definition iconbutton.h:48
qreal narrowTrailingSpaceMedium
Definition iconbutton.h:62
qreal containerHeightSmall
Definition iconbutton.h:26
qreal narrowTrailingSpaceLarge
Definition iconbutton.h:63