4pragma ComponentBehavior: Bound
7import QtQuick.Templates as T
88 property bool expanded: false
91 property int variant: FabMenu.Variant.Primary
94 property int direction: FabMenu.Direction.Up
97 property int alignment: Qt.AlignRight
106 property bool scrim: true
109 property real margins: MD.Tokens.fabMenu.containerMargin
120 property string collapsedIconName: MD.SymbolNames.symbolAdd
128 property string expandedIconName: MD.SymbolNames.symbolClose
131 readonly property alias button: fabMenuButton
134 readonly property color itemContainerColor: {
136 case FabMenu.Variant.Primary:
137 return control.MD.Style.primaryContainerColor;
138 case FabMenu.Variant.Secondary:
139 return control.MD.Style.secondaryContainerColor;
141 return control.MD.Style.tertiaryContainerColor;
146 readonly
property color itemContentColor: {
149 return control.MD.Style.onPrimaryContainerColor;
150 case FabMenu.Variant.Secondary:
151 return control.MD.Style.onSecondaryContainerColor;
153 return control.MD.Style.onTertiaryContainerColor;
158 readonly
property bool _alignedRight: (alignment === Qt.AlignRight) !== mirrored
161 readonly property real _entranceDirection: direction ===
FabMenu.Direction.Up ? 1 : -1
164 readonly property real _buttonMargin: margins + MD.
Tokens.fabMenu.closeButtonContainerHeight + MD.
Tokens.fabMenu.closeButtonBetweenSpace
172 property Item _restoreFocusItem: null
176 control.expanded =
true;
181 control.expanded =
false;
186 control.expanded = !control.expanded;
195 function _focusSequence() {
197 for (let index = 0; index < control.contentChildren.length; ++index) {
198 const item = control.contentChildren[index];
199 if (item && item.menu === control && item.enabled)
202 if (control.direction ===
FabMenu.Direction.Down)
203 items.unshift(fabMenuButton);
205 items.push(fabMenuButton);
215 function _focusFirstAction() {
216 for (let index = 0; index < control.contentChildren.length; ++index) {
217 const item = control.contentChildren[index];
218 if (item && item.menu === control && item.enabled) {
219 item.forceActiveFocus(Qt.PopupFocusReason);
223 fabMenuButton.forceActiveFocus(Qt.PopupFocusReason);
232 function _moveFocus(direction) {
233 const sequence = control._focusSequence();
234 if (sequence.length === 0)
236 let index = sequence.indexOf(Window.window ? Window.window.activeFocusItem : null);
237 index = index < 0 ? (direction > 0 ? -1 : 0) : index;
238 sequence[(index + direction + sequence.length) % sequence.length]
239 .forceActiveFocus(Qt.TabFocusReason);
243 function _focusBoundary(last) {
244 const sequence = control._focusSequence();
245 if (sequence.length > 0)
246 sequence[last ? sequence.length - 1 : 0].forceActiveFocus(Qt.TabFocusReason);
257 function _bindItems() {
258 for (let index = 0; index < control.contentChildren.length; ++index) {
259 const item = control.contentChildren[index];
260 if (!item || item.menu === undefined || item.staggerIndex === undefined)
267 if (item.menu !== control) {
269 item.clicked.connect(control.close);
271 item.staggerIndex = index;
272 item.x = Qt.binding(() => {
273 return control._alignedRight ? Math.max(0, column.width - item.width) : 0;
278 implicitWidth: Math.max(fabMenuButton.implicitWidth + margins * 2, implicitContentWidth + leftPadding + rightPadding)
279 implicitHeight: Math.max(fabMenuButton.implicitHeight + margins * 2, implicitContentHeight + topPadding + bottomPadding)
286 rightPadding: margins
287 topPadding: direction ===
FabMenu.Direction.Up ? margins : _buttonMargin
288 bottomPadding: direction ===
FabMenu.Direction.Up ? _buttonMargin : margins
293 focusPolicy: Qt.NoFocus
294 Accessible.ignored:
true
296 onContentChildrenChanged: control._bindItems()
303 if (control.expanded) {
304 control._restoreFocusItem = Window.window ? Window.window.activeFocusItem : null;
305 Qt.callLater(control._focusFirstAction);
307 const restoreItem = control._restoreFocusItem || fabMenuButton;
309 restoreItem.forceActiveFocus(Qt.PopupFocusReason);
310 control._restoreFocusItem = null;
314 Component.onCompleted: control._bindItems()
319 Keys.onEscapePressed:
event => {
320 if (control.expanded)
323 event.accepted =
false;
325 Keys.onUpPressed: control._moveFocus(-1)
326 Keys.onDownPressed: control._moveFocus(1)
327 Keys.onPressed:
event => {
328 if (!control.expanded) {
329 event.accepted =
false;
330 }
else if (event.key === Qt.Key_Home) {
331 control._focusBoundary(
false);
332 event.accepted =
true;
333 }
else if (event.key === Qt.Key_End) {
334 control._focusBoundary(
true);
335 event.accepted =
true;
337 event.accepted =
false;
341 contentItem: Column {
349 transform: Translate {
350 y: control.direction ===
FabMenu.Direction.Up ? Math.max(0, column.height - column.implicitHeight) : 0
365 objectName:
"fabMenuScrim"
368 color: control.MD.Style.scrimColor
369 opacity: control.expanded && control.scrim ? MD.Tokens.fabMenu.scrimOpacity : 0
372 Behavior on opacity {
374 duration: MD.Tokens.motion.duration.short4
379 onTapped: control.close()
385 objectName:
"fabMenuButton"
390 x: control._alignedRight ? control.width - width - control.margins : control.margins
391 y: control.direction ===
FabMenu.Direction.Up ? control.height - height - control.margins : control.margins
394 expanded: control.expanded
395 containerColor: control.itemContainerColor
396 contentColor: control.itemContentColor
398 collapsedIconName: control.collapsedIconName
399 expandedIconName: control.expandedIconName
401 onClicked: control.toggle()
Material Design 3 design tokens.