Fluid 1.3.0
Material Design for QtQuick and Qml
Loading...
Searching...
No Matches
Button.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 Fluid as MD
6import "../internal/MotionAnimation.js" as MotionAnimation
7import Fluid.Private as P
8import "../core/UiMetrics.js" as UiMetrics
9
21P.BaseButton {
22 id: control
23
27 property int type: P.BaseButton.Type.Elevated
28
34 property int shape: P.BaseButton.Shape.Round
35
42 property int size: P.BaseButton.Size.Small
43
48 property MD.typescale typescale: UiMetrics.buttonTypescale(control)
49
50 // Set by ButtonGroup while this button is one of its direct children.
51 property var __buttonGroup: null
52 property real __groupTopLeftRadius: -1
53 property real __groupTopRightRadius: -1
54 property real __groupBottomLeftRadius: -1
55 property real __groupBottomRightRadius: -1
56
60 readonly property bool hasIcon: icon.name.length > 0
61
62 implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
63 implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding)
64
65 leftInset: 0
66 rightInset: 0
67 topInset: UiMetrics.buttonInset(control)
68 bottomInset: UiMetrics.buttonInset(control)
69
70 leftPadding: UiMetrics.buttonPadding(control)
71 rightPadding: UiMetrics.buttonPadding(control)
72 topPadding: control.display === P.BaseButton.TextUnderIcon ? UiMetrics.buttonPadding(control) : 0
73 bottomPadding: control.display === P.BaseButton.TextUnderIcon ? UiMetrics.buttonPadding(control) : 0
74
75 flat: control.type === P.BaseButton.Type.Text || control.type === P.BaseButton.Type.Outlined
76
77 hoverEnabled: true
78 focusPolicy: Qt.StrongFocus
79
80 onCheckedChanged: {
81 if (__buttonGroup)
82 __buttonGroup.__childCheckedChanged(control);
83 }
84 onActiveFocusChanged: {
85 if (activeFocus && __buttonGroup)
86 __buttonGroup.__childFocused(control);
87 }
88 onPressedChanged: {
89 if (__buttonGroup)
90 __buttonGroup.__childGeometryChanged();
91 }
92 onImplicitWidthChanged: {
93 if (__buttonGroup)
94 __buttonGroup.__childGeometryChanged();
95 }
96 onVisibleChanged: {
97 if (__buttonGroup)
98 __buttonGroup.__childrenChanged();
99 }
100 onEnabledChanged: {
101 if (__buttonGroup)
102 __buttonGroup.__childrenChanged();
103 }
104 Keys.onPressed: event => {
105 if (__buttonGroup && __buttonGroup.__handleKey(control, event))
106 event.accepted = true;
107 }
108
109 Behavior on width {
110 enabled: control.__buttonGroup && control.__buttonGroup.__geometryInitialized
111 && control.__buttonGroup.variant === control.__buttonGroup.__standardVariant
112 NumberAnimation {
113 easing.type: Easing.BezierSpline
114 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
115 duration: MotionAnimation.expressiveFastSpatialDuration
116 }
117 }
118 Behavior on x {
119 enabled: control.__buttonGroup && control.__buttonGroup.__geometryInitialized
120 && control.__buttonGroup.variant === control.__buttonGroup.__standardVariant
121 NumberAnimation {
122 easing.type: Easing.BezierSpline
123 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
124 duration: MotionAnimation.expressiveFastSpatialDuration
125 }
126 }
127
128 Accessible.role: __buttonGroup && __buttonGroup.selectionMode === __buttonGroup.__singleSelectionMode
129 ? Accessible.RadioButton
130 : (__buttonGroup && __buttonGroup.selectionMode === __buttonGroup.__multiSelectionMode
131 ? Accessible.CheckBox : Accessible.Button)
132 Accessible.checkable: checkable
133 Accessible.checked: checked
134
135 spacing: UiMetrics.buttonSpacing(control)
136
137 icon.width: UiMetrics.buttonIconSize(control)
138 icon.height: UiMetrics.buttonIconSize(control)
139
140 font.pixelSize: typescale.fontSize
141 font.weight: typescale.fontWeight
142 font.letterSpacing: typescale.tracking
143
144 QtObject {
145 id: state
146
147 property color containerColor: {
148 switch (control.type) {
149 case P.BaseButton.Type.Elevated:
150 return control.checked ? control.MD.Style.primaryColor : control.MD.Style.surfaceContainerLowColor;
151 case P.BaseButton.Type.Filled:
152 return !control.checked ? control.MD.Style.surfaceContainerColor : control.MD.Style.primaryColor;
153 case P.BaseButton.Type.Tonal:
154 return control.checked ? control.MD.Style.secondaryColor : control.MD.Style.secondaryContainerColor;
155 case P.BaseButton.Type.Outlined:
156 return control.checked ? control.MD.Style.inverseSurfaceColor : "transparent";
157 case P.BaseButton.Type.Text:
158 return "transparent";
159 }
160 }
161 property color labelColor: {
162 switch (control.type) {
163 case P.BaseButton.Type.Elevated:
164 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
165 case P.BaseButton.Type.Filled:
166 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
167 case P.BaseButton.Type.Tonal:
168 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
169 case P.BaseButton.Type.Outlined:
170 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
171 case P.BaseButton.Type.Text:
172 return control.MD.Style.primaryColor;
173 }
174 }
175 property color stateLayerColor: "transparent"
176
177 property real elevation: control.type === P.BaseButton.Type.Elevated ? MD.Tokens.button.elevatedContainerElevation : MD.Tokens.button.flatContainerElevation
178
179 property real contentOpacity: 1.0
180 property real containerOpacity: 1.0
181 property real stateLayerOpacity: 1.0
182 }
183
184 states: [
185 State {
186 name: "disabled"
187 when: !control.enabled
188
189 PropertyChanges {
190 state {
191 containerColor: {
192 switch (control.type) {
193 case P.BaseButton.Type.Elevated:
194 return control.MD.Style.onSurfaceColor;
195 case P.BaseButton.Type.Filled:
196 case P.BaseButton.Type.Tonal:
197 return control.MD.Style.onSurfaceColor;
198 case P.BaseButton.Type.Outlined:
199 return control.checked ? control.MD.Style.onSurfaceColor : control.MD.Style.outlineVariantColor;
200 case P.BaseButton.Type.Text:
201 return control.MD.Style.onSurfaceColor;
202 }
203 }
204 labelColor: {
205 switch (control.type) {
206 case P.BaseButton.Type.Elevated:
207 return control.MD.Style.onSurfaceColor;
208 case P.BaseButton.Type.Filled:
209 case P.BaseButton.Type.Tonal:
210 case P.BaseButton.Type.Outlined:
211 case P.BaseButton.Type.Text:
212 return control.MD.Style.onSurfaceColor;
213 }
214 }
215 elevation: MD.Tokens.button.flatContainerElevation
216 containerOpacity: MD.Tokens.button.disabledContainerOpacity
217 contentOpacity: MD.Tokens.button.disabledLabelTextOpacity
218 }
219 }
220 },
221 State {
222 name: "hovered"
223 when: control.hovered && control.enabled
224
225 PropertyChanges {
226 state {
227 labelColor: {
228 switch (control.type) {
229 case P.BaseButton.Type.Elevated:
230 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
231 case P.BaseButton.Type.Filled:
232 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
233 case P.BaseButton.Type.Tonal:
234 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
235 case P.BaseButton.Type.Outlined:
236 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
237 case P.BaseButton.Type.Text:
238 return control.MD.Style.primaryColor;
239 }
240 }
241 stateLayerColor: {
242 switch (control.type) {
243 case P.BaseButton.Type.Elevated:
244 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
245 case P.BaseButton.Type.Filled:
246 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
247 case P.BaseButton.Type.Tonal:
248 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
249 case P.BaseButton.Type.Outlined:
250 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
251 case P.BaseButton.Type.Text:
252 return control.MD.Style.primaryColor;
253 }
254 }
255 stateLayerOpacity: MD.Tokens.button.hoverStateLayerOpacity
256 }
257 }
258 },
259 State {
260 name: "focused"
261 when: control.visualFocus && control.enabled
262
263 PropertyChanges {
264 state {
265 labelColor: {
266 switch (control.type) {
267 case P.BaseButton.Type.Elevated:
268 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
269 case P.BaseButton.Type.Filled:
270 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
271 case P.BaseButton.Type.Tonal:
272 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
273 case P.BaseButton.Type.Outlined:
274 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
275 case P.BaseButton.Type.Text:
276 return control.MD.Style.primaryColor;
277 }
278 }
279 stateLayerColor: {
280 switch (control.type) {
281 case P.BaseButton.Type.Elevated:
282 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
283 case P.BaseButton.Type.Filled:
284 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
285 case P.BaseButton.Type.Tonal:
286 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
287 case P.BaseButton.Type.Outlined:
288 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
289 case P.BaseButton.Type.Text:
290 return control.MD.Style.primaryColor;
291 }
292 }
293 elevation: control.type === P.BaseButton.Type.Elevated ? MD.Tokens.button.elevatedContainerElevation : MD.Tokens.button.flatContainerElevation
294 stateLayerOpacity: MD.Tokens.button.focusStateLayerOpacity
295 }
296 }
297 },
298 State {
299 name: "pressed"
300 when: control.down && control.enabled
301
302 PropertyChanges {
303 state {
304 labelColor: {
305 switch (control.type) {
306 case P.BaseButton.Type.Elevated:
307 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
308 case P.BaseButton.Type.Filled:
309 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
310 case P.BaseButton.Type.Tonal:
311 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
312 case P.BaseButton.Type.Outlined:
313 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
314 case P.BaseButton.Type.Text:
315 return control.MD.Style.primaryColor;
316 }
317 }
318 stateLayerColor: {
319 switch (control.type) {
320 case P.BaseButton.Type.Elevated:
321 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
322 case P.BaseButton.Type.Filled:
323 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
324 case P.BaseButton.Type.Tonal:
325 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
326 case P.BaseButton.Type.Outlined:
327 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
328 case P.BaseButton.Type.Text:
329 return control.MD.Style.primaryColor;
330 }
331 }
332 elevation: control.type === P.BaseButton.Type.Elevated ? MD.Tokens.button.elevatedContainerElevation : MD.Tokens.button.flatContainerElevation
333 stateLayerOpacity: MD.Tokens.button.pressedStateLayerOpacity
334 }
335 }
336 }
337 ]
338
339 contentItem: MD.IconLabel {
340 spacing: control.spacing
341 mirrored: control.mirrored
342 display: control.display
343
344 icon.name: control.icon.name
345 icon.width: control.icon.width
346 icon.height: control.icon.height
347 icon.color: color
348
349 typescale: control.typescale
350 text: control.text
351 opacity: state.contentOpacity
352 color: state.labelColor
353 }
354
355 background: MD.ElevationRectangle {
356 implicitWidth: 64
357 implicitHeight: UiMetrics.buttonHeight(control)
358
359 readonly property MD.shapeValue containerShape: UiMetrics.buttonShape(control)
360
361 topLeftRadius: control.__groupTopLeftRadius >= 0 ? control.__groupTopLeftRadius : UiMetrics.resolveShapeRadius(containerShape.topLeft, width, height)
362 topRightRadius: control.__groupTopRightRadius >= 0 ? control.__groupTopRightRadius : UiMetrics.resolveShapeRadius(containerShape.topRight, width, height)
363 bottomLeftRadius: control.__groupBottomLeftRadius >= 0 ? control.__groupBottomLeftRadius : UiMetrics.resolveShapeRadius(containerShape.bottomLeft, width, height)
364 bottomRightRadius: control.__groupBottomRightRadius >= 0 ? control.__groupBottomRightRadius : UiMetrics.resolveShapeRadius(containerShape.bottomRight, width, height)
365
366 Behavior on topLeftRadius {
367 enabled: !control.__buttonGroup || control.__buttonGroup.__geometryInitialized
368 NumberAnimation {
369 easing.type: Easing.BezierSpline
370 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
371 duration: MotionAnimation.expressiveFastSpatialDuration
372 }
373 }
374 Behavior on topRightRadius {
375 enabled: !control.__buttonGroup || control.__buttonGroup.__geometryInitialized
376 NumberAnimation {
377 easing.type: Easing.BezierSpline
378 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
379 duration: MotionAnimation.expressiveFastSpatialDuration
380 }
381 }
382 Behavior on bottomLeftRadius {
383 enabled: !control.__buttonGroup || control.__buttonGroup.__geometryInitialized
384 NumberAnimation {
385 easing.type: Easing.BezierSpline
386 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
387 duration: MotionAnimation.expressiveFastSpatialDuration
388 }
389 }
390 Behavior on bottomRightRadius {
391 enabled: !control.__buttonGroup || control.__buttonGroup.__geometryInitialized
392 NumberAnimation {
393 easing.type: Easing.BezierSpline
394 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
395 duration: MotionAnimation.expressiveFastSpatialDuration
396 }
397 }
398
399 border.width: control.type == P.BaseButton.Type.Outlined ? UiMetrics.buttonOutlineWidth(control) : 0
400 border.color: MD.Style.outlineVariantColor
401
402 elevation: state.elevation
403 elevationVisible: !MD.Utils.epsilonEqual(elevation, MD.Tokens.elevation.level0) && !control.flat && color.a > 0
404
405 opacity: state.containerOpacity
406 color: state.containerColor
407
408 MD.Ripple {
409 anchors.fill: parent
410
411 topLeftRadius: parent.topLeftRadius
412 topRightRadius: parent.topRightRadius
413 bottomLeftRadius: parent.bottomLeftRadius
414 bottomRightRadius: parent.bottomRightRadius
415
416 pressed: control.pressed
417 pressX: control.pressX
418 pressY: control.pressY
419
420 stateOpacity: state.stateLayerOpacity
421 color: state.stateLayerColor
422 }
423 }
424}