Fluid 1.3.0
Material Design for QtQuick and Qml
Loading...
Searching...
No Matches
ExposedDropdownMenu.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 Fluid as MD
7import Fluid.Private as P
8import "../core/UiMetrics.js" as UiMetrics
9import "../internal/MotionAnimation.js" as MotionAnimation
10import QtQuick
11import QtQuick.Templates as T
12import QtQuick.Window
13
34T.ComboBox {
35 id: control
36 objectName: "exposedDropdownMenu"
37
39 enum FieldStyle {
41 Filled = 0,
43 Outlined = 1
44 }
45
47 enum MenuColorStyle {
49 Standard = 0,
51 Vibrant = 1
52 }
53
55 property int fieldStyle: MD.ExposedDropdownMenu.Filled
57 property int menuColorStyle: MD.ExposedDropdownMenu.Standard
59 property string label: ""
61 property string placeholderText: ""
63 property string supportingText: ""
65 property string errorText: ""
67 property bool error: false
69 property string leadingIconName: ""
71 property url leadingIconSource
73 property string enabledRole: ""
75 property string leadingIconRole: ""
77 property string supportingTextRole: ""
79 property string trailingTextRole: ""
81 property string badgeRole: ""
82
84 readonly property bool _filledField: fieldStyle === MD.ExposedDropdownMenu.Filled
86 readonly property bool _outlinedField: fieldStyle === MD.ExposedDropdownMenu.Outlined
88 readonly property bool _vibrantMenu: menuColorStyle === MD.ExposedDropdownMenu.Vibrant
90 readonly property bool _hasLeadingIcon: leadingIconName.length > 0
91 || leadingIconSource.toString().length > 0
93 readonly property bool _fieldActive: activeFocus || popup.visible
95 readonly property color _inputColor: control.MD.Style.onSurfaceColor
97 readonly property color _labelColor: !enabled ? control.MD.Style.onSurfaceColor
98 : error && hovered
99 ? control.MD.Style.onErrorContainerColor
100 : error ? control.MD.Style.errorColor
101 : _fieldActive ? control.MD.Style.primaryColor
102 : control.MD.Style.onSurfaceVariantColor
104 readonly property color _leadingIconColor: !enabled ? control.MD.Style.onSurfaceColor
105 : control.MD.Style.onSurfaceVariantColor
107 readonly property color _trailingIconColor: !enabled ? control.MD.Style.onSurfaceColor
108 : error && hovered
109 ? control.MD.Style.onErrorContainerColor
110 : error ? control.MD.Style.errorColor
111 : control.MD.Style.onSurfaceVariantColor
113 readonly property color _indicatorColor: !enabled ? control.MD.Style.onSurfaceColor
114 : error && hovered
115 ? control.MD.Style.onErrorContainerColor
116 : error ? control.MD.Style.errorColor
117 : _fieldActive ? control.MD.Style.primaryColor
118 : hovered ? control.MD.Style.onSurfaceColor
119 : _outlinedField
120 ? control.MD.Style.outlineColor
121 : control.MD.Style.onSurfaceVariantColor
123 readonly property color _supportingColor: !enabled ? control.MD.Style.onSurfaceColor
124 : error ? control.MD.Style.errorColor
125 : control.MD.Style.onSurfaceVariantColor
127 readonly property real _leadingIconSize: _filledField
128 ? MD.Tokens.exposedDropdownMenu.filledLeadingIconSize
129 : MD.Tokens.exposedDropdownMenu.outlinedLeadingIconSize
131 readonly property real _leadingReservation: MD.Tokens.exposedDropdownMenu.horizontalPadding
132 + (_hasLeadingIcon
133 ? _leadingIconSize
134 + MD.Tokens.exposedDropdownMenu.leadingIconContentSpace
135 : 0)
137 readonly property real _trailingReservation: MD.Tokens.exposedDropdownMenu.horizontalPadding
138 + MD.Tokens.exposedDropdownMenu.trailingIconSize
139 + MD.Tokens.exposedDropdownMenu.trailingIconContentSpace
141 readonly property real _supportingAreaHeight: fieldDecoration.supportingAreaHeight
142
144 function _roleValue(row, roleName, rowModel) {
145 if (roleName.length === 0 || row < 0)
146 return undefined;
147 if (rowModel && rowModel[roleName] !== undefined)
148 return rowModel[roleName];
149 if (model && typeof model.get === "function") {
150 const data = model.get(row);
151 return data ? data[roleName] : undefined;
152 }
153 if (model && model[row] !== undefined && model[row] !== null)
154 return model[row][roleName];
155 return undefined;
156 }
157
159 function _roleText(row, roleName, rowModel) {
160 const value = _roleValue(row, roleName, rowModel);
161 return value === undefined || value === null ? "" : value.toString();
162 }
163
165 function _roleEnabled(row, rowModel) {
166 const value = _roleValue(row, enabledRole, rowModel);
167 return value === undefined || value === null ? true : Boolean(value);
168 }
169
171 function _isIconSource(value) {
172 if (value === undefined || value === null)
173 return false;
174 const stringValue = value.toString();
175 return stringValue.indexOf(":") >= 0 || stringValue.indexOf("/") >= 0
176 || stringValue.indexOf(".") >= 0;
177 }
178
179 implicitWidth: Math.max(MD.Tokens.exposedDropdownMenu.minimumWidth,
180 MD.Tokens.exposedDropdownMenu.preferredWidth)
181 implicitHeight: MD.Tokens.exposedDropdownMenu.fieldHeight
182 + _supportingAreaHeight
183 leftPadding: mirrored ? _trailingReservation : _leadingReservation
184 rightPadding: mirrored ? _leadingReservation : _trailingReservation
185 topPadding: 0
186 bottomPadding: _supportingAreaHeight
187 focusPolicy: Qt.StrongFocus
188 hoverEnabled: true
189
190 Accessible.name: label.length > 0 ? label : placeholderText
191 Accessible.description: error && errorText.length > 0 ? errorText : supportingText
192 Accessible.editable: editable
193
194 delegate: T.ItemDelegate {
195 id: optionDelegate
196 required property int index
197 required property var model
198
200 readonly property string supportingText: control._roleText(
201 index, control.supportingTextRole, model)
203 readonly property string trailingText: control._roleText(
204 index, control.trailingTextRole, model)
206 readonly property var badgeContent: control._roleValue(index, control.badgeRole, model)
208 readonly property var iconValue: control._roleValue(index, control.leadingIconRole, model)
210 readonly property bool selected: control.currentIndex === index
212 readonly property bool hasBadge: badgeContent !== undefined && badgeContent !== null
213 && badgeContent.toString().length > 0
215 readonly property bool hasIcon: iconValue !== undefined && iconValue !== null
216 && iconValue.toString().length > 0
218 readonly property var containerShape: {
219 if (selected)
220 return MD.Tokens.menu.verticalSelectedItemShape;
221 if (control.count <= 1)
222 return MD.Tokens.menu.verticalOnlyItemShape;
223 if (index === 0)
224 return MD.Tokens.menu.verticalFirstItemShape;
225 if (index === control.count - 1)
226 return MD.Tokens.menu.verticalLastItemShape;
227 return MD.Tokens.menu.verticalMiddleItemShape;
228 }
230 readonly property color contentColor: control._vibrantMenu
231 ? selected
232 ? control.MD.Style.onTertiaryColor
233 : control.MD.Style.onTertiaryContainerColor
234 : selected
235 ? control.MD.Style.onTertiaryContainerColor
236 : control.MD.Style.onSurfaceColor
238 readonly property color secondaryColor: selected ? contentColor
239 : control._vibrantMenu
240 ? control.MD.Style.onTertiaryContainerColor
241 : control.MD.Style.onSurfaceVariantColor
243 readonly property real contentOpacity: enabled ? 1
244 : MD.Tokens.menu.disabledContentOpacity
245
246 width: ListView.view ? ListView.view.width : implicitWidth
247 text: control.textAt(index)
248 enabled: control._roleEnabled(index, model)
249 highlighted: control.highlightedIndex === index
250 implicitHeight: Math.max(MD.Tokens.menu.verticalItemHeight,
251 optionLabels.implicitHeight
252 + MD.Tokens.menu.verticalItemTopPadding
253 + MD.Tokens.menu.verticalItemBottomPadding)
254 leftPadding: MD.Tokens.menu.verticalItemLeadingSpace
255 rightPadding: MD.Tokens.menu.verticalItemTrailingSpace
256 topPadding: MD.Tokens.menu.verticalItemTopPadding
257 bottomPadding: MD.Tokens.menu.verticalItemBottomPadding
258 spacing: MD.Tokens.menu.verticalItemBetweenSpace
259 hoverEnabled: control.hoverEnabled
260 focusPolicy: Qt.StrongFocus
261 LayoutMirroring.enabled: control.mirrored
262 LayoutMirroring.childrenInherit: true
263
264 contentItem: Row {
265 spacing: optionDelegate.spacing
266
267 Item {
268 id: optionIcon
269 objectName: "menuItemIcon"
270 width: MD.Tokens.menu.verticalIconSize
271 height: width
272 anchors.verticalCenter: parent.verticalCenter
273 // Keep the selection/icon slot stable so labels do not move
274 // when currentIndex changes.
275 visible: true
276
277 MD.Symbol {
278 objectName: "menuItemSelection"
279 anchors.fill: parent
280 name: MD.Symbols.check
281 iconWidth: parent.width
282 iconHeight: parent.height
283 color: optionDelegate.secondaryColor
284 opacity: optionDelegate.contentOpacity
285 visible: optionDelegate.selected
286 }
287 Image {
288 anchors.fill: parent
289 source: control._isIconSource(optionDelegate.iconValue)
290 ? optionDelegate.iconValue : ""
291 sourceSize: Qt.size(width, height)
292 fillMode: Image.PreserveAspectFit
293 opacity: optionDelegate.contentOpacity
294 visible: !optionDelegate.selected
295 && control._isIconSource(optionDelegate.iconValue)
296 }
297 MD.Symbol {
298 anchors.fill: parent
299 name: optionDelegate.hasIcon
300 && !control._isIconSource(optionDelegate.iconValue)
301 ? optionDelegate.iconValue.toString() : ""
302 iconWidth: parent.width
303 iconHeight: parent.height
304 color: optionDelegate.secondaryColor
305 opacity: optionDelegate.contentOpacity
306 visible: !optionDelegate.selected && optionDelegate.hasIcon
307 && !control._isIconSource(optionDelegate.iconValue)
308 }
309 }
310
311 Column {
312 id: optionLabels
313 width: Math.max(0, parent.width
314 - (optionIcon.visible ? optionIcon.width
315 + optionDelegate.spacing : 0)
316 - (optionTrailing.visible ? optionTrailing.implicitWidth
317 + optionDelegate.spacing : 0)
318 - (optionBadge.visible ? optionBadge.implicitWidth
319 + optionDelegate.spacing : 0))
320 anchors.verticalCenter: parent.verticalCenter
321 spacing: 0
322 MD.Label {
323 objectName: "menuItemLabel"
324 width: parent.width
325 text: optionDelegate.text
326 typescale: MD.Tokens.typescale.labelLarge
327 color: optionDelegate.contentColor
328 opacity: optionDelegate.contentOpacity
329 horizontalAlignment: optionDelegate.mirrored ? Text.AlignRight : Text.AlignLeft
330 wrapMode: Text.NoWrap
331 elide: Text.ElideRight
332 }
333 MD.Label {
334 objectName: "menuItemSupportingText"
335 width: parent.width
336 text: optionDelegate.supportingText
337 typescale: MD.Tokens.typescale.bodySmall
338 color: optionDelegate.secondaryColor
339 opacity: optionDelegate.contentOpacity
340 horizontalAlignment: optionDelegate.mirrored ? Text.AlignRight : Text.AlignLeft
341 wrapMode: Text.NoWrap
342 elide: Text.ElideRight
343 visible: text.length > 0
344 }
345 }
346
347 MD.Label {
348 id: optionTrailing
349 objectName: "menuItemTrailingText"
350 anchors.verticalCenter: parent.verticalCenter
351 text: optionDelegate.trailingText
352 typescale: MD.Tokens.typescale.labelLarge
353 color: optionDelegate.secondaryColor
354 opacity: optionDelegate.contentOpacity
355 visible: text.length > 0
356 }
357 MD.Label {
358 id: optionBadge
359 objectName: "menuItemBadge"
360 anchors.verticalCenter: parent.verticalCenter
361 text: optionDelegate.hasBadge ? optionDelegate.badgeContent.toString() : ""
362 typescale: MD.Tokens.typescale.labelSmall
363 color: optionDelegate.secondaryColor
364 opacity: optionDelegate.contentOpacity
365 visible: optionDelegate.hasBadge
366 }
367 }
368
369 background: Rectangle {
370 objectName: "menuItemBackground"
371 color: optionDelegate.selected
372 ? control._vibrantMenu
373 ? control.MD.Style.tertiaryColor
374 : control.MD.Style.tertiaryContainerColor
375 : "transparent"
376 topLeftRadius: UiMetrics.resolveShapeRadius(
377 optionDelegate.containerShape.topLeft, width, height)
378 topRightRadius: UiMetrics.resolveShapeRadius(
379 optionDelegate.containerShape.topRight, width, height)
380 bottomLeftRadius: UiMetrics.resolveShapeRadius(
381 optionDelegate.containerShape.bottomLeft, width, height)
382 bottomRightRadius: UiMetrics.resolveShapeRadius(
383 optionDelegate.containerShape.bottomRight, width, height)
384
385 MD.Ripple {
386 objectName: "menuItemStateLayer"
387 anchors.fill: parent
388 pressed: optionDelegate.pressed
389 pressX: optionDelegate.pressX
390 pressY: optionDelegate.pressY
391 color: optionDelegate.contentColor
392 topLeftRadius: parent.topLeftRadius
393 topRightRadius: parent.topRightRadius
394 bottomLeftRadius: parent.bottomLeftRadius
395 bottomRightRadius: parent.bottomRightRadius
396 stateOpacity: {
397 if (!optionDelegate.enabled)
398 return 0;
399 if (optionDelegate.pressed)
400 return MD.Tokens.menu.pressedStateLayerOpacity;
401 if (optionDelegate.visualFocus || optionDelegate.highlighted)
402 return MD.Tokens.menu.focusStateLayerOpacity;
403 if (optionDelegate.hovered)
404 return MD.Tokens.menu.hoverStateLayerOpacity;
405 return 0;
406 }
407 }
408 }
409 }
410
411 contentItem: T.TextField {
412 id: field
413 objectName: "exposedDropdownField"
414 x: control.leftPadding
415 y: 0
416 width: Math.max(0, control.width - control.leftPadding - control.rightPadding)
417 height: MD.Tokens.exposedDropdownMenu.fieldHeight
418 text: control.editable ? control.editText : control.displayText
419 enabled: control.editable && control.enabled
420 readOnly: !control.editable
421 validator: control.validator
422 inputMethodHints: control.inputMethodHints
423 selectByMouse: control.selectTextByMouse
424 color: control.enabled
425 ? control._inputColor
426 : MD.Color.transparent(control.MD.Style.onSurfaceColor,
427 MD.Tokens.exposedDropdownMenu.disabledContentOpacity)
428 selectionColor: control.MD.Style.primaryColor
429 selectedTextColor: control.MD.Style.onPrimaryColor
430 font.family: control.MD.Style.plainFontFamily
431 font.pixelSize: MD.Tokens.typescale.bodyLarge.fontSize
432 font.weight: MD.Tokens.typescale.bodyLarge.fontWeight
433 font.letterSpacing: MD.Tokens.typescale.bodyLarge.tracking
434 horizontalAlignment: control.mirrored ? Text.AlignRight : Text.AlignLeft
435 verticalAlignment: Text.AlignVCenter
436 leftPadding: 0
437 rightPadding: 0
438 topPadding: control._filledField
439 ? (fieldDecoration.labelImplicitHeight
440 + MD.Tokens.exposedDropdownMenu.labelInputTextSpace)
441 * fieldDecoration.labelProgress
442 : 0
443 bottomPadding: 0
444 background: Item {}
445 Accessible.ignored: true
446 }
447
448 indicator: Item {
449 objectName: "exposedDropdownIndicator"
450 x: control.mirrored ? MD.Tokens.exposedDropdownMenu.horizontalPadding
451 : control.width - width
452 - MD.Tokens.exposedDropdownMenu.horizontalPadding
453 y: (MD.Tokens.exposedDropdownMenu.fieldHeight - height) / 2
454 width: MD.Tokens.exposedDropdownMenu.trailingIconSize
455 height: width
456 opacity: control.enabled ? 1 : MD.Tokens.exposedDropdownMenu.disabledContentOpacity
457
458 MD.Symbol {
459 objectName: "exposedDropdownTrailingIcon"
460 anchors.fill: parent
461 name: MD.Symbols.arrowDropDown
462 iconWidth: parent.width
463 iconHeight: parent.height
464 color: control._trailingIconColor
465 rotation: control.popup.visible ? 180 : 0
466 }
467 }
468
469 background: Loader {
470 sourceComponent: fieldDecoration.backgroundComponent
471 }
472
473 Component {
474 id: exposedDropdownLeadingIcon
475
476 Item {
477 objectName: "exposedDropdownLeadingIcon"
478 width: control._leadingIconSize
479 height: width
480 opacity: control.enabled
481 ? 1 : MD.Tokens.exposedDropdownMenu.disabledContentOpacity
482
483 Image {
484 anchors.fill: parent
485 source: control.leadingIconSource
486 sourceSize: Qt.size(width, height)
487 fillMode: Image.PreserveAspectFit
488 visible: source.toString().length > 0
489 }
490
491 MD.Symbol {
492 anchors.fill: parent
493 name: control.leadingIconName
494 iconWidth: parent.width
495 iconHeight: parent.height
496 color: control._leadingIconColor
497 visible: control.leadingIconSource.toString().length === 0
498 && control.leadingIconName.length > 0
499 }
500 }
501 }
502
503 P.FieldDecoration {
504 id: fieldDecoration
505 objectName: "exposedDropdownFieldDecoration"
506 anchors.fill: parent
507
508 containerObjectName: "exposedDropdownBackground"
509 activeIndicatorObjectName: "exposedDropdownActiveIndicator"
510 labelNotchObjectName: "exposedDropdownLabelNotch"
511 labelObjectName: "exposedDropdownLabel"
512 placeholderObjectName: "exposedDropdownPlaceholder"
513 prefixObjectName: "exposedDropdownPrefix"
514 suffixObjectName: "exposedDropdownSuffix"
515 leadingSlotObjectName: "exposedDropdownLeadingSlot"
516 trailingSlotObjectName: "exposedDropdownTrailingSlot"
517 supportingObjectName: "exposedDropdownSupportingText"
518 errorObjectName: "exposedDropdownErrorText"
519 mirrored: control.mirrored
520 outlined: control._outlinedField
521 fieldFocused: control._fieldActive
522 fieldEnabled: control.enabled
523 error: control.error
524 containerTransitionsEnabled: true
525 labelTransitionsEnabled: true
526 indicatorTransitionsEnabled: control.enabled
527 applySlotDisabledOpacity: false
528 backgroundImplicitWidth: Math.max(
529 MD.Tokens.exposedDropdownMenu.minimumWidth,
530 MD.Tokens.exposedDropdownMenu.preferredWidth)
531 containerHeight: MD.Tokens.exposedDropdownMenu.fieldHeight
532 horizontalPadding: MD.Tokens.exposedDropdownMenu.horizontalPadding
533 contentVerticalPadding: MD.Tokens.exposedDropdownMenu.contentVerticalPadding
534 supportingTextTopSpace: MD.Tokens.exposedDropdownMenu.supportingTextTopSpace
535 supportingTextMinimumHeight: 0
536 outlinedLabelHorizontalPadding: MD.Tokens.exposedDropdownMenu.outlinedLabelHorizontalPadding
537 disabledContentOpacity: MD.Tokens.exposedDropdownMenu.disabledContentOpacity
538 leadingSlotMinimumSize: control._leadingIconSize
539 trailingSlotMinimumSize: 0
540 inputCenterY: MD.Tokens.exposedDropdownMenu.fieldHeight / 2
541 inputBaselineY: field.baselineOffset
542 alignInputDecorationsToBaseline: true
543 leadingReservation: control._leadingReservation
544 trailingReservation: control._trailingReservation
545 inputLeftPadding: control.leftPadding
546 inputRightPadding: control.rightPadding
547 labelLeadingPosition: control._leadingReservation
548 + (control._outlinedField
549 ? MD.Tokens.exposedDropdownMenu.outlinedLabelHorizontalPadding
550 * labelProgress
551 : 0)
552 labelTrailingPosition: control._trailingReservation
553 + (control._outlinedField
554 ? MD.Tokens.exposedDropdownMenu.outlinedLabelHorizontalPadding
555 * labelProgress
556 : 0)
557 fullShapeValue: MD.Tokens.shape.cornerValueFull
558 labelText: control.label
559 placeholderText: control.placeholderText
560 inputText: field.text
561 prefixText: ""
562 suffixText: ""
563 supportingText: control.supportingText
564 errorText: control.errorText
565 containerColor: control.flat || control._outlinedField
566 ? "transparent"
567 : !control.enabled
568 ? MD.Color.transparent(
569 control.MD.Style.onSurfaceColor,
570 MD.Tokens.exposedDropdownMenu.filledDisabledContainerOpacity)
571 : control.MD.Style.surfaceContainerHighestColor
572 outlineColor: !control.enabled
573 ? MD.Color.transparent(
574 control.MD.Style.onSurfaceColor,
575 MD.Tokens.exposedDropdownMenu.outlinedDisabledOutlineOpacity)
576 : control._indicatorColor
577 indicatorColor: control._indicatorColor
578 labelColor: control._labelColor
579 inputDecorationColor: control.enabled
580 ? control.MD.Style.onSurfaceVariantColor
581 : control.MD.Style.onSurfaceColor
582 supportingColor: control._supportingColor
583 notchColor: control.MD.Style.surfaceColor
584 containerShape: control._filledField
585 ? MD.Tokens.exposedDropdownMenu.filledContainerShape
586 : MD.Tokens.exposedDropdownMenu.outlinedContainerShape
587 outlineWidth: control._outlinedField && !control.flat
588 ? control._fieldActive
589 ? MD.Tokens.exposedDropdownMenu.outlinedFocusOutlineWidth
590 : control.hovered
591 ? MD.Tokens.exposedDropdownMenu.outlinedHoverOutlineWidth
592 : MD.Tokens.exposedDropdownMenu.outlinedOutlineWidth
593 : 0
594 activeIndicatorVisible: control._filledField && !control.flat
595 activeIndicatorHeight: control._fieldActive
596 ? MD.Tokens.exposedDropdownMenu.filledFocusActiveIndicatorHeight
597 : control.hovered
598 ? MD.Tokens.exposedDropdownMenu.filledHoverActiveIndicatorHeight
599 : MD.Tokens.exposedDropdownMenu.filledActiveIndicatorHeight
600 activeIndicatorOpacity: control.enabled
601 ? 1
602 : MD.Tokens.exposedDropdownMenu.disabledContentOpacity
603 fontFamily: control.MD.Style.plainFontFamily
604 bodyLargeTypeScale: MD.Tokens.typescale.bodyLarge
605 bodySmallTypeScale: MD.Tokens.typescale.bodySmall
606 supportingWrapMode: Text.Wrap
607 supportingElide: Text.ElideRight
608 effectsDuration: MotionAnimation.expressiveFastEffectsDuration
609 slowEffectsDuration: MotionAnimation.expressiveSlowEffectsDuration
610 spatialDuration: MotionAnimation.expressiveFastSpatialDuration
611 effectsCurve: MotionAnimation.expressiveFastEffectsCurve
612 slowEffectsCurve: MotionAnimation.expressiveSlowEffectsCurve
613 spatialCurve: MotionAnimation.expressiveFastSpatialCurve
614 leading: control._hasLeadingIcon ? exposedDropdownLeadingIcon : null
615 }
616
617 popup: T.Popup {
618 id: dropdownPopup
619 objectName: "exposedDropdownPopup"
620
622 property alias currentIndex: menuList.currentIndex
624 readonly property real _fieldWindowY: control.mapToItem(null, 0, 0).y
626 readonly property var _window: control.parent ? control.parent.Window.window : null
628 readonly property real _availableBelow: _window
629 ? _window.height - _fieldWindowY
630 - MD.Tokens.exposedDropdownMenu.fieldHeight
631 - MD.Tokens.menu.verticalViewportMargin
632 - MD.Tokens.exposedDropdownMenu.popupAnchorGap
633 : Number.POSITIVE_INFINITY
635 readonly property real _availableAbove: _window
636 ? _fieldWindowY
637 - MD.Tokens.menu.verticalViewportMargin
638 - MD.Tokens.exposedDropdownMenu.popupAnchorGap
639 : Number.POSITIVE_INFINITY
641 readonly property real _naturalHeight: menuList.contentHeight + topPadding + bottomPadding
643 readonly property bool _opensBelow: _availableBelow >= _naturalHeight
644 || _availableBelow >= _availableAbove
646 readonly property real _availableHeight: Math.max(0, _opensBelow
647 ? _availableBelow
648 : _availableAbove)
649
651 function itemAt(row) {
652 return menuList.itemAtIndex(row);
653 }
654
655 x: control.mirrored ? control.width - width : 0
656 y: _opensBelow
657 ? MD.Tokens.exposedDropdownMenu.fieldHeight
658 + MD.Tokens.exposedDropdownMenu.popupAnchorGap
659 : -implicitHeight - MD.Tokens.exposedDropdownMenu.popupAnchorGap
660 width: control.width
661 implicitWidth: control.width
662 implicitHeight: Math.min(_naturalHeight, _availableHeight)
663 margins: 0
664 leftMargin: MD.Tokens.menu.horizontalViewportMargin
665 rightMargin: MD.Tokens.menu.horizontalViewportMargin
666 topMargin: MD.Tokens.menu.verticalViewportMargin
667 bottomMargin: MD.Tokens.menu.verticalViewportMargin
668 topPadding: MD.Tokens.menu.verticalGroupPadding
669 bottomPadding: MD.Tokens.menu.verticalGroupPadding
670 leftPadding: MD.Tokens.menu.verticalGroupPadding
671 rightPadding: MD.Tokens.menu.verticalGroupPadding
672 focus: true
673 modal: false
674 closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutside
675 transformOrigin: _opensBelow ? Item.Top : Item.Bottom
676
677 contentItem: ListView {
678 id: menuList
679 objectName: "menuListView"
680 implicitHeight: contentHeight
681 model: control.delegateModel
682 currentIndex: control.highlightedIndex
683 interactive: contentHeight + dropdownPopup.topPadding + dropdownPopup.bottomPadding
684 > dropdownPopup.height
685 boundsBehavior: Flickable.StopAtBounds
686 spacing: MD.Tokens.menu.verticalSegmentedGap
687 clip: true
688 ScrollIndicator.vertical: MD.ScrollIndicator {}
689 }
690
691 background: MD.ElevationRectangle {
692 objectName: "exposedDropdownPopupBackground"
693 color: control._vibrantMenu
694 ? control.MD.Style.tertiaryContainerColor
695 : control.MD.Style.surfaceContainerLowColor
696 topLeftRadius: UiMetrics.resolveShapeRadius(
697 MD.Tokens.exposedDropdownMenu.popupContainerShape.topLeft,
698 width, height)
699 topRightRadius: UiMetrics.resolveShapeRadius(
700 MD.Tokens.exposedDropdownMenu.popupContainerShape.topRight,
701 width, height)
702 bottomLeftRadius: UiMetrics.resolveShapeRadius(
703 MD.Tokens.exposedDropdownMenu.popupContainerShape.bottomLeft,
704 width, height)
705 bottomRightRadius: UiMetrics.resolveShapeRadius(
706 MD.Tokens.exposedDropdownMenu.popupContainerShape.bottomRight,
707 width, height)
708 elevation: MD.Tokens.exposedDropdownMenu.popupContainerElevation
709 }
710
711 enter: Transition {
712 NumberAnimation {
713 property: "scale"
714 from: MD.Tokens.menu.closedScale
715 to: 1
716 duration: MotionAnimation.expressiveFastSpatialDuration
717 easing.type: Easing.BezierSpline
718 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
719 }
720 NumberAnimation {
721 property: "opacity"
722 from: 0
723 to: 1
724 duration: MotionAnimation.expressiveFastEffectsDuration
725 easing.type: Easing.BezierSpline
726 easing.bezierCurve: MotionAnimation.expressiveFastEffectsCurve
727 }
728 }
729
730 exit: Transition {
731 NumberAnimation {
732 property: "scale"
733 from: 1
734 to: MD.Tokens.menu.closedScale
735 duration: MotionAnimation.expressiveFastSpatialDuration
736 easing.type: Easing.BezierSpline
737 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
738 }
739 NumberAnimation {
740 property: "opacity"
741 from: 1
742 to: 0
743 duration: MotionAnimation.expressiveFastEffectsDuration
744 easing.type: Easing.BezierSpline
745 easing.bezierCurve: MotionAnimation.expressiveFastEffectsCurve
746 }
747 }
748 }
749}
Utility functions for colors.
Definition color.h:16
Material Design 3 design tokens.
Definition tokens.h:68
Tokens(QObject *parent=nullptr)
Definition tokens.cpp:8
Displays text using a selectable Material 3 type scale.
Definition Label.qml:15
A non-interactive indicator of the visible portion of scrolling content.
Material Symbols icon tokens.
Definition symbol.h:17