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: MD.Tokens.typescale.labelLarge
49
53 readonly property bool hasIcon: icon.name.length > 0
54
55 implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
56 implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding)
57
58 leftInset: 0
59 rightInset: 0
60 topInset: UiMetrics.buttonInset(control)
61 bottomInset: UiMetrics.buttonInset(control)
62
63 leftPadding: UiMetrics.buttonPadding(control)
64 rightPadding: UiMetrics.buttonPadding(control)
65 topPadding: control.display === P.BaseButton.TextUnderIcon ? UiMetrics.buttonPadding(control) : 0
66 bottomPadding: control.display === P.BaseButton.TextUnderIcon ? UiMetrics.buttonPadding(control) : 0
67
68 flat: control.type === P.BaseButton.Type.Text || control.type === P.BaseButton.Type.Outlined
69
70 hoverEnabled: true
71 focusPolicy: Qt.StrongFocus
72
73 spacing: UiMetrics.buttonSpacing(control)
74
75 icon.width: UiMetrics.buttonIconSize(control)
76 icon.height: UiMetrics.buttonIconSize(control)
77
78 font.pixelSize: typescale.fontSize
79 font.weight: typescale.fontWeight
80 font.letterSpacing: typescale.tracking
81
82 QtObject {
83 id: state
84
85 property color containerColor: {
86 switch (control.type) {
87 case P.BaseButton.Type.Elevated:
88 return control.checked ? control.MD.Style.primaryColor : control.MD.Style.surfaceContainerLowColor;
89 case P.BaseButton.Type.Filled:
90 return !control.checked ? control.MD.Style.surfaceContainerColor : control.MD.Style.primaryColor;
91 case P.BaseButton.Type.Tonal:
92 return control.checked ? control.MD.Style.secondaryColor : control.MD.Style.secondaryContainerColor;
93 case P.BaseButton.Type.Outlined:
94 return control.checked ? control.MD.Style.inverseSurfaceColor : "transparent";
95 case P.BaseButton.Type.Text:
96 return "transparent";
97 }
98 }
99 property color labelColor: {
100 switch (control.type) {
101 case P.BaseButton.Type.Elevated:
102 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
103 case P.BaseButton.Type.Filled:
104 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
105 case P.BaseButton.Type.Tonal:
106 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
107 case P.BaseButton.Type.Outlined:
108 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
109 case P.BaseButton.Type.Text:
110 return control.MD.Style.primaryColor;
111 }
112 }
113 property color stateLayerColor: "transparent"
114
115 property real elevation: control.type === P.BaseButton.Type.Elevated ? MD.Tokens.button.elevatedContainerElevation : MD.Tokens.button.flatContainerElevation
116
117 property real contentOpacity: 1.0
118 property real containerOpacity: 1.0
119 property real stateLayerOpacity: 1.0
120 }
121
122 states: [
123 State {
124 name: "disabled"
125 when: !control.enabled
126
127 PropertyChanges {
128 state {
129 containerColor: {
130 switch (control.type) {
131 case P.BaseButton.Type.Elevated:
132 return control.MD.Style.onSurfaceColor;
133 case P.BaseButton.Type.Filled:
134 case P.BaseButton.Type.Tonal:
135 return control.MD.Style.onSurfaceColor;
136 case P.BaseButton.Type.Outlined:
137 return control.checked ? control.MD.Style.onSurfaceColor : control.MD.Style.outlineVariantColor;
138 case P.BaseButton.Type.Text:
139 return control.MD.Style.onSurfaceColor;
140 }
141 }
142 labelColor: {
143 switch (control.type) {
144 case P.BaseButton.Type.Elevated:
145 return control.MD.Style.onSurfaceColor;
146 case P.BaseButton.Type.Filled:
147 case P.BaseButton.Type.Tonal:
148 case P.BaseButton.Type.Outlined:
149 case P.BaseButton.Type.Text:
150 return control.MD.Style.onSurfaceColor;
151 }
152 }
153 elevation: MD.Tokens.button.flatContainerElevation
154 containerOpacity: MD.Tokens.button.disabledContainerOpacity
155 contentOpacity: MD.Tokens.button.disabledLabelTextOpacity
156 }
157 }
158 },
159 State {
160 name: "hovered"
161 when: control.hovered && control.enabled
162
163 PropertyChanges {
164 state {
165 labelColor: {
166 switch (control.type) {
167 case P.BaseButton.Type.Elevated:
168 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
169 case P.BaseButton.Type.Filled:
170 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
171 case P.BaseButton.Type.Tonal:
172 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
173 case P.BaseButton.Type.Outlined:
174 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
175 case P.BaseButton.Type.Text:
176 return control.MD.Style.primaryColor;
177 }
178 }
179 stateLayerColor: {
180 switch (control.type) {
181 case P.BaseButton.Type.Elevated:
182 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
183 case P.BaseButton.Type.Filled:
184 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
185 case P.BaseButton.Type.Tonal:
186 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
187 case P.BaseButton.Type.Outlined:
188 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
189 case P.BaseButton.Type.Text:
190 return control.MD.Style.primaryColor;
191 }
192 }
193 stateLayerOpacity: MD.Tokens.button.hoverStateLayerOpacity
194 }
195 }
196 },
197 State {
198 name: "focused"
199 when: control.visualFocus && control.enabled
200
201 PropertyChanges {
202 state {
203 labelColor: {
204 switch (control.type) {
205 case P.BaseButton.Type.Elevated:
206 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
207 case P.BaseButton.Type.Filled:
208 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
209 case P.BaseButton.Type.Tonal:
210 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
211 case P.BaseButton.Type.Outlined:
212 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
213 case P.BaseButton.Type.Text:
214 return control.MD.Style.primaryColor;
215 }
216 }
217 stateLayerColor: {
218 switch (control.type) {
219 case P.BaseButton.Type.Elevated:
220 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
221 case P.BaseButton.Type.Filled:
222 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
223 case P.BaseButton.Type.Tonal:
224 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
225 case P.BaseButton.Type.Outlined:
226 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
227 case P.BaseButton.Type.Text:
228 return control.MD.Style.primaryColor;
229 }
230 }
231 elevation: control.type === P.BaseButton.Type.Elevated ? MD.Tokens.button.elevatedContainerElevation : MD.Tokens.button.flatContainerElevation
232 stateLayerOpacity: MD.Tokens.button.focusStateLayerOpacity
233 }
234 }
235 },
236 State {
237 name: "pressed"
238 when: control.down && control.enabled
239
240 PropertyChanges {
241 state {
242 labelColor: {
243 switch (control.type) {
244 case P.BaseButton.Type.Elevated:
245 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
246 case P.BaseButton.Type.Filled:
247 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
248 case P.BaseButton.Type.Tonal:
249 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
250 case P.BaseButton.Type.Outlined:
251 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
252 case P.BaseButton.Type.Text:
253 return control.MD.Style.primaryColor;
254 }
255 }
256 stateLayerColor: {
257 switch (control.type) {
258 case P.BaseButton.Type.Elevated:
259 return control.checked ? control.MD.Style.onPrimaryColor : control.MD.Style.primaryColor;
260 case P.BaseButton.Type.Filled:
261 return !control.checked ? control.MD.Style.onSurfaceVariantColor : control.MD.Style.onPrimaryColor;
262 case P.BaseButton.Type.Tonal:
263 return control.checked ? control.MD.Style.onSecondaryColor : control.MD.Style.onSecondaryContainerColor;
264 case P.BaseButton.Type.Outlined:
265 return control.checked ? control.MD.Style.inverseOnSurfaceColor : control.MD.Style.onSurfaceVariantColor;
266 case P.BaseButton.Type.Text:
267 return control.MD.Style.primaryColor;
268 }
269 }
270 elevation: control.type === P.BaseButton.Type.Elevated ? MD.Tokens.button.elevatedContainerElevation : MD.Tokens.button.flatContainerElevation
271 stateLayerOpacity: MD.Tokens.button.pressedStateLayerOpacity
272 }
273 }
274 }
275 ]
276
277 contentItem: MD.IconLabel {
278 spacing: control.spacing
279 mirrored: control.mirrored
280 display: control.display
281
282 icon.name: control.icon.name
283 icon.width: control.icon.width
284 icon.height: control.icon.height
285 icon.color: color
286
287 typescale: control.typescale
288 text: control.text
289 opacity: state.contentOpacity
290 color: state.labelColor
291 }
292
293 background: MD.ElevationRectangle {
294 implicitWidth: 64
295 implicitHeight: UiMetrics.buttonHeight(control)
296
297 readonly property MD.shapeValue containerShape: UiMetrics.buttonShape(control)
298
299 topLeftRadius: UiMetrics.resolveShapeRadius(containerShape.topLeft, width, height)
300 topRightRadius: UiMetrics.resolveShapeRadius(containerShape.topRight, width, height)
301 bottomLeftRadius: UiMetrics.resolveShapeRadius(containerShape.bottomLeft, width, height)
302 bottomRightRadius: UiMetrics.resolveShapeRadius(containerShape.bottomRight, width, height)
303
304 Behavior on topLeftRadius {
305 NumberAnimation {
306 easing.type: Easing.BezierSpline
307 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
308 duration: MotionAnimation.expressiveFastSpatialDuration
309 }
310 }
311 Behavior on topRightRadius {
312 NumberAnimation {
313 easing.type: Easing.BezierSpline
314 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
315 duration: MotionAnimation.expressiveFastSpatialDuration
316 }
317 }
318 Behavior on bottomLeftRadius {
319 NumberAnimation {
320 easing.type: Easing.BezierSpline
321 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
322 duration: MotionAnimation.expressiveFastSpatialDuration
323 }
324 }
325 Behavior on bottomRightRadius {
326 NumberAnimation {
327 easing.type: Easing.BezierSpline
328 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
329 duration: MotionAnimation.expressiveFastSpatialDuration
330 }
331 }
332
333 border.width: control.type == P.BaseButton.Type.Outlined ? UiMetrics.buttonOutlineWidth(control) : 0
334 border.color: MD.Style.outlineVariantColor
335
336 elevation: state.elevation
337 elevationVisible: !MD.Utils.epsilonEqual(elevation, MD.Tokens.elevation.level0) && !control.flat && color.a > 0
338
339 opacity: state.containerOpacity
340 color: state.containerColor
341
342 MD.Ripple {
343 anchors.fill: parent
344
345 topLeftRadius: parent.topLeftRadius
346 topRightRadius: parent.topRightRadius
347 bottomLeftRadius: parent.bottomLeftRadius
348 bottomRightRadius: parent.bottomRightRadius
349
350 pressed: control.pressed
351 pressX: control.pressX
352 pressY: control.pressY
353
354 stateOpacity: state.stateLayerOpacity
355 color: state.stateLayerColor
356 }
357 }
358}