Fluid 1.3.0
Material Design for QtQuick and Qml
Loading...
Searching...
No Matches
FabMenuButton.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
4pragma ComponentBehavior: Bound
5
6import QtQuick
7import QtQuick.Templates as T
8import Fluid as MD
9import "../core/UiMetrics.js" as UiMetrics
10import "../internal/MotionAnimation.js" as MotionAnimation
11
32T.ToolButton {
33 id: control
34
36 enum Size {
37 Default,
38 Medium,
39 Large
40 }
41
43 property int size: FabMenuButton.Size.Default
44
53 property bool expanded: false
54
56 property color containerColor: control.MD.Style.primaryContainerColor
57
59 property color contentColor: control.MD.Style.onPrimaryContainerColor
60
62 property string collapsedIconName: MD.SymbolNames.symbolAdd
63
65 property string expandedIconName: MD.SymbolNames.symbolClose
66
68 readonly property color effectiveContainerColor: state.containerColor
69
71 readonly property color effectiveContentColor: state.contentColor
72
74 readonly property real effectiveElevation: state.elevation
75
76 icon.width: state.iconSize
77 icon.height: state.iconSize
78
79 implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding)
80 implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding)
81
82 leftInset: 0
83 rightInset: 0
84 topInset: 0
85 bottomInset: 0
86 padding: 0
87 spacing: 0
88
89 hoverEnabled: true
90 focusPolicy: Qt.StrongFocus
91
92 Accessible.name: text
93 Accessible.role: Accessible.Button
94 Accessible.checkable: true
95 Accessible.checked: expanded
96
97 QtObject {
98 id: state
99
100 readonly property real collapsedContainerWidth: {
101 switch (control.size) {
102 case FabMenuButton.Size.Default:
103 return MD.Tokens.fab.containerWidth;
104 case FabMenuButton.Size.Medium:
105 return MD.Tokens.fab.mediumContainerWidth;
106 case FabMenuButton.Size.Large:
107 return MD.Tokens.fab.largeContainerWidth;
108 }
109 }
110 readonly property real collapsedContainerHeight: {
111 switch (control.size) {
112 case FabMenuButton.Size.Default:
113 return MD.Tokens.fab.containerHeight;
114 case FabMenuButton.Size.Medium:
115 return MD.Tokens.fab.mediumContainerHeight;
116 case FabMenuButton.Size.Large:
117 return MD.Tokens.fab.largeContainerHeight;
118 }
119 }
120 readonly property MD.shapeValue collapsedContainerShape: {
121 switch (control.size) {
122 case FabMenuButton.Size.Default:
123 return MD.Tokens.fab.containerShape;
124 case FabMenuButton.Size.Medium:
125 return MD.Tokens.fab.mediumContainerShape;
126 case FabMenuButton.Size.Large:
127 return MD.Tokens.fab.largeContainerShape;
128 }
129 }
130 readonly property real collapsedIconSize: {
131 switch (control.size) {
132 case FabMenuButton.Size.Default:
133 return MD.Tokens.fab.iconSize;
134 case FabMenuButton.Size.Medium:
135 return MD.Tokens.fab.mediumIconSize;
136 case FabMenuButton.Size.Large:
137 return MD.Tokens.fab.largeIconSize;
138 }
139 }
140
141 property real containerWidth: control.expanded ? MD.Tokens.fabMenu.closeButtonContainerWidth : state.collapsedContainerWidth
142 property real containerHeight: control.expanded ? MD.Tokens.fabMenu.closeButtonContainerHeight : state.collapsedContainerHeight
143 readonly property MD.shapeValue containerShape: control.expanded
144 ? MD.Tokens.fabMenu.closeButtonContainerShape
145 : state.collapsedContainerShape
146 property real topLeftRadius: UiMetrics.resolveShapeRadius(
147 containerShape.topLeft, containerWidth, containerHeight)
148 property real topRightRadius: UiMetrics.resolveShapeRadius(
149 containerShape.topRight, containerWidth, containerHeight)
150 property real bottomLeftRadius: UiMetrics.resolveShapeRadius(
151 containerShape.bottomLeft, containerWidth,
152 containerHeight)
153 property real bottomRightRadius: UiMetrics.resolveShapeRadius(
154 containerShape.bottomRight, containerWidth,
155 containerHeight)
156 property real iconSize: control.expanded ? MD.Tokens.fabMenu.closeButtonIconSize : state.collapsedIconSize
157
158 property color containerColor: control.containerColor
159 property color contentColor: control.contentColor
160 property color stateLayerColor: "transparent"
161
162 property real elevation: control.expanded ? MD.Tokens.fabMenu.closeButtonContainerElevation : MD.Tokens.fab.containerElevation
163 property real stateLayerOpacity: 0
164
165 Behavior on containerWidth {
166 NumberAnimation {
167 // qmllint disable unresolved-type
168 easing.type: Easing.BezierSpline
169 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
170 duration: MotionAnimation.expressiveFastSpatialDuration
171 // qmllint enable unresolved-type
172 }
173 }
174
175 Behavior on containerHeight {
176 NumberAnimation {
177 // qmllint disable unresolved-type
178 easing.type: Easing.BezierSpline
179 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
180 duration: MotionAnimation.expressiveFastSpatialDuration
181 // qmllint enable unresolved-type
182 }
183 }
184
185 Behavior on topLeftRadius {
186 NumberAnimation {
187 // qmllint disable unresolved-type
188 easing.type: Easing.BezierSpline
189 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
190 duration: MotionAnimation.expressiveFastSpatialDuration
191 // qmllint enable unresolved-type
192 }
193 }
194
195 Behavior on topRightRadius {
196 NumberAnimation {
197 // qmllint disable unresolved-type
198 easing.type: Easing.BezierSpline
199 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
200 duration: MotionAnimation.expressiveFastSpatialDuration
201 // qmllint enable unresolved-type
202 }
203 }
204
205 Behavior on bottomLeftRadius {
206 NumberAnimation {
207 // qmllint disable unresolved-type
208 easing.type: Easing.BezierSpline
209 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
210 duration: MotionAnimation.expressiveFastSpatialDuration
211 // qmllint enable unresolved-type
212 }
213 }
214
215 Behavior on bottomRightRadius {
216 NumberAnimation {
217 // qmllint disable unresolved-type
218 easing.type: Easing.BezierSpline
219 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
220 duration: MotionAnimation.expressiveFastSpatialDuration
221 // qmllint enable unresolved-type
222 }
223 }
224
225 Behavior on iconSize {
226 NumberAnimation {
227 // qmllint disable unresolved-type
228 easing.type: Easing.BezierSpline
229 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
230 duration: MotionAnimation.expressiveFastSpatialDuration
231 // qmllint enable unresolved-type
232 }
233 }
234 }
235
236 states: [
237 State {
238 name: "pressed"
239 when: control.down && control.enabled
240
241 PropertyChanges {
242 state.elevation: control.expanded ? MD.Tokens.fabMenu.closeButtonPressedContainerElevation : MD.Tokens.fab.pressedContainerElevation
243 state.stateLayerColor: control.effectiveContentColor
244 state.stateLayerOpacity: MD.Tokens.fabMenu.pressedStateLayerOpacity
245 }
246 },
247 State {
248 name: "focused"
249 when: control.visualFocus && control.enabled
250
251 PropertyChanges {
252 state.elevation: control.expanded ? MD.Tokens.fabMenu.closeButtonFocusContainerElevation : MD.Tokens.fab.focusContainerElevation
253 state.stateLayerColor: control.effectiveContentColor
254 state.stateLayerOpacity: MD.Tokens.fabMenu.focusStateLayerOpacity
255 }
256 },
257 State {
258 name: "hovered"
259 when: control.hovered && control.enabled
260
261 PropertyChanges {
262 state.elevation: control.expanded ? MD.Tokens.fabMenu.closeButtonHoverContainerElevation : MD.Tokens.fab.hoverContainerElevation
263 state.stateLayerColor: control.effectiveContentColor
264 state.stateLayerOpacity: MD.Tokens.fabMenu.hoverStateLayerOpacity
265 }
266 }
267 ]
268
269 contentItem: Item {
270 implicitWidth: state.iconSize
271 implicitHeight: state.iconSize
272
273 MD.Symbol {
274 objectName: "fabMenuButtonCollapsedSymbol"
275
276 anchors.centerIn: parent
277 name: control.collapsedIconName
278 iconWidth: state.iconSize
279 iconHeight: state.iconSize
280 color: control.effectiveContentColor
281 opacity: control.expanded ? 0 : 1
282 visible: opacity > 0
283
284 Behavior on opacity {
285 NumberAnimation {
286 duration: MD.Tokens.motion.duration.short4
287 }
288 }
289 }
290
291 MD.Symbol {
292 objectName: "fabMenuButtonExpandedSymbol"
293
294 anchors.centerIn: parent
295 name: control.expandedIconName
296 iconWidth: state.iconSize
297 iconHeight: state.iconSize
298 color: control.effectiveContentColor
299 opacity: control.expanded ? 1 : 0
300 visible: opacity > 0
301
302 Behavior on opacity {
303 NumberAnimation {
304 duration: MD.Tokens.motion.duration.short4
305 }
306 }
307 }
308 }
309
310 background: MD.ElevationRectangle {
311 implicitWidth: state.containerWidth
312 implicitHeight: state.containerHeight
313 topLeftRadius: state.topLeftRadius
314 topRightRadius: state.topRightRadius
315 bottomLeftRadius: state.bottomLeftRadius
316 bottomRightRadius: state.bottomRightRadius
317 color: state.containerColor
318 elevation: state.elevation
319
320 MD.Ripple {
321 objectName: "fabMenuButtonRipple"
322
323 anchors.fill: parent
324 topLeftRadius: parent.topLeftRadius
325 topRightRadius: parent.topRightRadius
326 bottomLeftRadius: parent.bottomLeftRadius
327 bottomRightRadius: parent.bottomRightRadius
328 pressed: control.pressed
329 pressX: control.pressX
330 pressY: control.pressY
331 stateOpacity: state.stateLayerOpacity
332 color: state.stateLayerColor
333 }
334 }
335}
The toggle button of a Material Design 3 Expressive FAB menu.