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 "../core/UiMetrics.js" as UiMetrics
8import "../internal/MotionAnimation.js" as MotionAnimation
9import QtQuick
10import QtQuick.Templates as T
11import QtQuick.Window
12
33T.ComboBox {
34 id: control
35 objectName: "exposedDropdownMenu"
36
38 enum FieldStyle {
40 Filled = 0,
42 Outlined = 1
43 }
44
46 enum MenuColorStyle {
48 Standard = 0,
50 Vibrant = 1
51 }
52
54 property int fieldStyle: MD.ExposedDropdownMenu.Filled
56 property int menuColorStyle: MD.ExposedDropdownMenu.Standard
58 property string label: ""
60 property string placeholderText: ""
62 property string supportingText: ""
64 property string errorText: ""
66 property bool error: false
68 property string leadingIconName: ""
70 property url leadingIconSource
72 property string enabledRole: ""
74 property string leadingIconRole: ""
76 property string supportingTextRole: ""
78 property string trailingTextRole: ""
80 property string badgeRole: ""
81
83 readonly property bool _filledField: fieldStyle === MD.ExposedDropdownMenu.Filled
85 readonly property bool _outlinedField: fieldStyle === MD.ExposedDropdownMenu.Outlined
87 readonly property bool _vibrantMenu: menuColorStyle === MD.ExposedDropdownMenu.Vibrant
89 readonly property bool _hasLeadingIcon: leadingIconName.length > 0
90 || leadingIconSource.toString().length > 0
92 readonly property bool _labelFloated: label.length > 0
93 && (displayText.length > 0 || editText.length > 0
94 || activeFocus || popup.visible)
96 readonly property bool _fieldActive: activeFocus || popup.visible
98 readonly property color _inputColor: control.MD.Style.onSurfaceColor
100 readonly property color _labelColor: !enabled ? control.MD.Style.onSurfaceColor
101 : error && hovered
102 ? control.MD.Style.onErrorContainerColor
103 : error ? control.MD.Style.errorColor
104 : _fieldActive ? control.MD.Style.primaryColor
105 : control.MD.Style.onSurfaceVariantColor
107 readonly property color _leadingIconColor: !enabled ? control.MD.Style.onSurfaceColor
108 : control.MD.Style.onSurfaceVariantColor
110 readonly property color _trailingIconColor: !enabled ? control.MD.Style.onSurfaceColor
111 : error && hovered
112 ? control.MD.Style.onErrorContainerColor
113 : error ? control.MD.Style.errorColor
114 : control.MD.Style.onSurfaceVariantColor
116 readonly property color _indicatorColor: !enabled ? control.MD.Style.onSurfaceColor
117 : error && hovered
118 ? control.MD.Style.onErrorContainerColor
119 : error ? control.MD.Style.errorColor
120 : _fieldActive ? control.MD.Style.primaryColor
121 : hovered ? control.MD.Style.onSurfaceColor
122 : _outlinedField
123 ? control.MD.Style.outlineColor
124 : control.MD.Style.onSurfaceVariantColor
126 readonly property color _supportingColor: !enabled ? control.MD.Style.onSurfaceColor
127 : error ? control.MD.Style.errorColor
128 : control.MD.Style.onSurfaceVariantColor
130 readonly property real _leadingIconSize: _filledField
131 ? MD.Tokens.exposedDropdownMenu.filledLeadingIconSize
132 : MD.Tokens.exposedDropdownMenu.outlinedLeadingIconSize
134 readonly property real _leadingReservation: MD.Tokens.exposedDropdownMenu.horizontalPadding
135 + (_hasLeadingIcon
136 ? _leadingIconSize
137 + MD.Tokens.exposedDropdownMenu.leadingIconContentSpace
138 : 0)
140 readonly property real _trailingReservation: MD.Tokens.exposedDropdownMenu.horizontalPadding
141 + MD.Tokens.exposedDropdownMenu.trailingIconSize
142 + MD.Tokens.exposedDropdownMenu.trailingIconContentSpace
144 readonly property real _supportingAreaHeight: (error && errorText.length > 0)
145 || (!error && supportingText.length > 0)
146 ? MD.Tokens.exposedDropdownMenu.supportingTextTopSpace
147 + supportingLabel.implicitHeight
148 : 0
149
151 function _roleValue(row, roleName, rowModel) {
152 if (roleName.length === 0 || row < 0)
153 return undefined;
154 if (rowModel && rowModel[roleName] !== undefined)
155 return rowModel[roleName];
156 if (model && typeof model.get === "function") {
157 const data = model.get(row);
158 return data ? data[roleName] : undefined;
159 }
160 if (model && model[row] !== undefined && model[row] !== null)
161 return model[row][roleName];
162 return undefined;
163 }
164
166 function _roleText(row, roleName, rowModel) {
167 const value = _roleValue(row, roleName, rowModel);
168 return value === undefined || value === null ? "" : value.toString();
169 }
170
172 function _roleEnabled(row, rowModel) {
173 const value = _roleValue(row, enabledRole, rowModel);
174 return value === undefined || value === null ? true : Boolean(value);
175 }
176
178 function _isIconSource(value) {
179 if (value === undefined || value === null)
180 return false;
181 const stringValue = value.toString();
182 return stringValue.indexOf(":") >= 0 || stringValue.indexOf("/") >= 0
183 || stringValue.indexOf(".") >= 0;
184 }
185
186 implicitWidth: Math.max(MD.Tokens.exposedDropdownMenu.minimumWidth,
187 MD.Tokens.exposedDropdownMenu.preferredWidth)
188 implicitHeight: MD.Tokens.exposedDropdownMenu.fieldHeight
189 + _supportingAreaHeight
190 leftPadding: mirrored ? _trailingReservation : _leadingReservation
191 rightPadding: mirrored ? _leadingReservation : _trailingReservation
192 topPadding: 0
193 bottomPadding: _supportingAreaHeight
194 focusPolicy: Qt.StrongFocus
195 hoverEnabled: true
196
197 Accessible.name: label.length > 0 ? label : placeholderText
198 Accessible.description: error && errorText.length > 0 ? errorText : supportingText
199 Accessible.editable: editable
200
201 delegate: T.ItemDelegate {
202 id: optionDelegate
203 required property int index
204 required property var model
205
207 readonly property string supportingText: control._roleText(
208 index, control.supportingTextRole, model)
210 readonly property string trailingText: control._roleText(
211 index, control.trailingTextRole, model)
213 readonly property var badgeContent: control._roleValue(index, control.badgeRole, model)
215 readonly property var iconValue: control._roleValue(index, control.leadingIconRole, model)
217 readonly property bool selected: control.currentIndex === index
219 readonly property bool hasBadge: badgeContent !== undefined && badgeContent !== null
220 && badgeContent.toString().length > 0
222 readonly property bool hasIcon: iconValue !== undefined && iconValue !== null
223 && iconValue.toString().length > 0
225 readonly property var containerShape: {
226 if (selected)
227 return MD.Tokens.menu.verticalSelectedItemShape;
228 if (control.count <= 1)
229 return MD.Tokens.menu.verticalOnlyItemShape;
230 if (index === 0)
231 return MD.Tokens.menu.verticalFirstItemShape;
232 if (index === control.count - 1)
233 return MD.Tokens.menu.verticalLastItemShape;
234 return MD.Tokens.menu.verticalMiddleItemShape;
235 }
237 readonly property color contentColor: control._vibrantMenu
238 ? selected
239 ? control.MD.Style.onTertiaryColor
240 : control.MD.Style.onTertiaryContainerColor
241 : selected
242 ? control.MD.Style.onTertiaryContainerColor
243 : control.MD.Style.onSurfaceColor
245 readonly property color secondaryColor: selected ? contentColor
246 : control._vibrantMenu
247 ? control.MD.Style.onTertiaryContainerColor
248 : control.MD.Style.onSurfaceVariantColor
250 readonly property real contentOpacity: enabled ? 1
251 : MD.Tokens.menu.disabledContentOpacity
252
253 width: ListView.view ? ListView.view.width : implicitWidth
254 text: control.textAt(index)
255 enabled: control._roleEnabled(index, model)
256 highlighted: control.highlightedIndex === index
257 implicitHeight: Math.max(MD.Tokens.menu.verticalItemHeight,
258 optionLabels.implicitHeight
259 + MD.Tokens.menu.verticalItemTopPadding
260 + MD.Tokens.menu.verticalItemBottomPadding)
261 leftPadding: MD.Tokens.menu.verticalItemLeadingSpace
262 rightPadding: MD.Tokens.menu.verticalItemTrailingSpace
263 topPadding: MD.Tokens.menu.verticalItemTopPadding
264 bottomPadding: MD.Tokens.menu.verticalItemBottomPadding
265 spacing: MD.Tokens.menu.verticalItemBetweenSpace
266 hoverEnabled: control.hoverEnabled
267 focusPolicy: Qt.StrongFocus
268 LayoutMirroring.enabled: control.mirrored
269 LayoutMirroring.childrenInherit: true
270
271 contentItem: Row {
272 spacing: optionDelegate.spacing
273
274 Item {
275 id: optionIcon
276 objectName: "menuItemIcon"
277 width: MD.Tokens.menu.verticalIconSize
278 height: width
279 anchors.verticalCenter: parent.verticalCenter
280 // Keep the selection/icon slot stable so labels do not move
281 // when currentIndex changes.
282 visible: true
283
284 MD.Symbol {
285 objectName: "menuItemSelection"
286 anchors.fill: parent
287 name: MD.SymbolNames.symbolCheck
288 iconWidth: parent.width
289 iconHeight: parent.height
290 color: optionDelegate.secondaryColor
291 opacity: optionDelegate.contentOpacity
292 visible: optionDelegate.selected
293 }
294 Image {
295 anchors.fill: parent
296 source: control._isIconSource(optionDelegate.iconValue)
297 ? optionDelegate.iconValue : ""
298 sourceSize: Qt.size(width, height)
299 fillMode: Image.PreserveAspectFit
300 opacity: optionDelegate.contentOpacity
301 visible: !optionDelegate.selected
302 && control._isIconSource(optionDelegate.iconValue)
303 }
304 MD.Symbol {
305 anchors.fill: parent
306 name: optionDelegate.hasIcon
307 && !control._isIconSource(optionDelegate.iconValue)
308 ? optionDelegate.iconValue.toString() : ""
309 iconWidth: parent.width
310 iconHeight: parent.height
311 color: optionDelegate.secondaryColor
312 opacity: optionDelegate.contentOpacity
313 visible: !optionDelegate.selected && optionDelegate.hasIcon
314 && !control._isIconSource(optionDelegate.iconValue)
315 }
316 }
317
318 Column {
319 id: optionLabels
320 width: Math.max(0, parent.width
321 - (optionIcon.visible ? optionIcon.width
322 + optionDelegate.spacing : 0)
323 - (optionTrailing.visible ? optionTrailing.implicitWidth
324 + optionDelegate.spacing : 0)
325 - (optionBadge.visible ? optionBadge.implicitWidth
326 + optionDelegate.spacing : 0))
327 anchors.verticalCenter: parent.verticalCenter
328 spacing: 0
329 MD.Label {
330 objectName: "menuItemLabel"
331 width: parent.width
332 text: optionDelegate.text
333 typescale: MD.Tokens.typescale.labelLarge
334 color: optionDelegate.contentColor
335 opacity: optionDelegate.contentOpacity
336 horizontalAlignment: optionDelegate.mirrored ? Text.AlignRight : Text.AlignLeft
337 wrapMode: Text.NoWrap
338 elide: Text.ElideRight
339 }
340 MD.Label {
341 objectName: "menuItemSupportingText"
342 width: parent.width
343 text: optionDelegate.supportingText
344 typescale: MD.Tokens.typescale.bodySmall
345 color: optionDelegate.secondaryColor
346 opacity: optionDelegate.contentOpacity
347 horizontalAlignment: optionDelegate.mirrored ? Text.AlignRight : Text.AlignLeft
348 wrapMode: Text.NoWrap
349 elide: Text.ElideRight
350 visible: text.length > 0
351 }
352 }
353
354 MD.Label {
355 id: optionTrailing
356 objectName: "menuItemTrailingText"
357 anchors.verticalCenter: parent.verticalCenter
358 text: optionDelegate.trailingText
359 typescale: MD.Tokens.typescale.labelLarge
360 color: optionDelegate.secondaryColor
361 opacity: optionDelegate.contentOpacity
362 visible: text.length > 0
363 }
364 MD.Label {
365 id: optionBadge
366 objectName: "menuItemBadge"
367 anchors.verticalCenter: parent.verticalCenter
368 text: optionDelegate.hasBadge ? optionDelegate.badgeContent.toString() : ""
369 typescale: MD.Tokens.typescale.labelSmall
370 color: optionDelegate.secondaryColor
371 opacity: optionDelegate.contentOpacity
372 visible: optionDelegate.hasBadge
373 }
374 }
375
376 background: Rectangle {
377 objectName: "menuItemBackground"
378 color: optionDelegate.selected
379 ? control._vibrantMenu
380 ? control.MD.Style.tertiaryColor
381 : control.MD.Style.tertiaryContainerColor
382 : "transparent"
383 topLeftRadius: UiMetrics.resolveShapeRadius(
384 optionDelegate.containerShape.topLeft, width, height)
385 topRightRadius: UiMetrics.resolveShapeRadius(
386 optionDelegate.containerShape.topRight, width, height)
387 bottomLeftRadius: UiMetrics.resolveShapeRadius(
388 optionDelegate.containerShape.bottomLeft, width, height)
389 bottomRightRadius: UiMetrics.resolveShapeRadius(
390 optionDelegate.containerShape.bottomRight, width, height)
391
392 MD.Ripple {
393 objectName: "menuItemStateLayer"
394 anchors.fill: parent
395 pressed: optionDelegate.pressed
396 pressX: optionDelegate.pressX
397 pressY: optionDelegate.pressY
398 color: optionDelegate.contentColor
399 topLeftRadius: parent.topLeftRadius
400 topRightRadius: parent.topRightRadius
401 bottomLeftRadius: parent.bottomLeftRadius
402 bottomRightRadius: parent.bottomRightRadius
403 stateOpacity: {
404 if (!optionDelegate.enabled)
405 return 0;
406 if (optionDelegate.pressed)
407 return MD.Tokens.menu.pressedStateLayerOpacity;
408 if (optionDelegate.visualFocus || optionDelegate.highlighted)
409 return MD.Tokens.menu.focusStateLayerOpacity;
410 if (optionDelegate.hovered)
411 return MD.Tokens.menu.hoverStateLayerOpacity;
412 return 0;
413 }
414 }
415 }
416 }
417
418 contentItem: T.TextField {
419 id: field
420 objectName: "exposedDropdownField"
421 x: control.leftPadding
422 y: 0
423 width: Math.max(0, control.width - control.leftPadding - control.rightPadding)
424 height: MD.Tokens.exposedDropdownMenu.fieldHeight
425 text: control.editable ? control.editText : control.displayText
426 enabled: control.editable && control.enabled
427 readOnly: !control.editable
428 validator: control.validator
429 inputMethodHints: control.inputMethodHints
430 selectByMouse: control.selectTextByMouse
431 color: control.enabled
432 ? control._inputColor
433 : MD.Color.transparent(control.MD.Style.onSurfaceColor,
434 MD.Tokens.exposedDropdownMenu.disabledContentOpacity)
435 selectionColor: control.MD.Style.primaryColor
436 selectedTextColor: control.MD.Style.onPrimaryColor
437 font.family: control.MD.Style.plainFontFamily
438 font.pixelSize: MD.Tokens.typescale.bodyLarge.fontSize
439 font.weight: MD.Tokens.typescale.bodyLarge.fontWeight
440 font.letterSpacing: MD.Tokens.typescale.bodyLarge.tracking
441 horizontalAlignment: control.mirrored ? Text.AlignRight : Text.AlignLeft
442 verticalAlignment: Text.AlignVCenter
443 leftPadding: 0
444 rightPadding: 0
445 topPadding: control._labelFloated
446 && control._filledField
447 ? fieldLabel.implicitHeight + MD.Tokens.exposedDropdownMenu.labelInputTextSpace
448 : 0
449 bottomPadding: 0
450 background: Item {}
451 Accessible.ignored: true
452
453 Rectangle {
454 objectName: "exposedDropdownLabelNotch"
455 x: fieldLabel.x - MD.Tokens.exposedDropdownMenu.outlinedLabelHorizontalPadding
456 y: fieldLabel.y
457 width: fieldLabel.width
458 + MD.Tokens.exposedDropdownMenu.outlinedLabelHorizontalPadding * 2
459 height: fieldLabel.height
460 color: control.MD.Style.surfaceColor
461 visible: control._labelFloated
462 && control._outlinedField
463 }
464
465 MD.Label {
466 id: fieldLabel
467 objectName: "exposedDropdownLabel"
468 readonly property bool outlinedFloating: control._labelFloated
469 && control._outlinedField
470 x: outlinedFloating
471 ? control.mirrored
472 ? parent.width - width
473 - MD.Tokens.exposedDropdownMenu.outlinedLabelHorizontalPadding
474 : MD.Tokens.exposedDropdownMenu.outlinedLabelHorizontalPadding
475 : 0
476 y: outlinedFloating ? -height / 2
477 : control._labelFloated ? MD.Tokens.exposedDropdownMenu.contentVerticalPadding
478 : (parent.height - height) / 2
479 width: outlinedFloating
480 ? Math.min(fieldLabelFontMetrics.advanceWidth(text),
481 parent.width
482 - MD.Tokens.exposedDropdownMenu.outlinedLabelHorizontalPadding * 2)
483 : parent.width
484 text: control.label
485 typescale: control._labelFloated ? MD.Tokens.typescale.bodySmall
486 : MD.Tokens.typescale.bodyLarge
487 color: control._labelColor
488 opacity: control.enabled ? 1
489 : MD.Tokens.exposedDropdownMenu.disabledContentOpacity
490 horizontalAlignment: control.mirrored ? Text.AlignRight : Text.AlignLeft
491 wrapMode: Text.NoWrap
492 elide: Text.ElideRight
493 visible: text.length > 0
494
495 FontMetrics {
496 id: fieldLabelFontMetrics
497 font: fieldLabel.font
498 }
499 }
500
501 MD.Label {
502 objectName: "exposedDropdownPlaceholder"
503 anchors.left: parent.left
504 anchors.right: parent.right
505 anchors.baseline: parent.baseline
506 text: control.placeholderText
507 typescale: MD.Tokens.typescale.bodyLarge
508 color: control.enabled ? control.MD.Style.onSurfaceVariantColor
509 : control.MD.Style.onSurfaceColor
510 opacity: control.enabled ? 1
511 : MD.Tokens.exposedDropdownMenu.disabledContentOpacity
512 horizontalAlignment: control.mirrored ? Text.AlignRight : Text.AlignLeft
513 elide: Text.ElideRight
514 visible: field.text.length === 0 && text.length > 0
515 && (control.label.length === 0 || control._labelFloated)
516 }
517 }
518
519 indicator: Item {
520 objectName: "exposedDropdownIndicator"
521 x: control.mirrored ? MD.Tokens.exposedDropdownMenu.horizontalPadding
522 : control.width - width
523 - MD.Tokens.exposedDropdownMenu.horizontalPadding
524 y: (MD.Tokens.exposedDropdownMenu.fieldHeight - height) / 2
525 width: MD.Tokens.exposedDropdownMenu.trailingIconSize
526 height: width
527 opacity: control.enabled ? 1 : MD.Tokens.exposedDropdownMenu.disabledContentOpacity
528
529 MD.Symbol {
530 anchors.fill: parent
531 name: MD.SymbolNames.symbolArrowDropDown
532 iconWidth: parent.width
533 iconHeight: parent.height
534 color: control._trailingIconColor
535 rotation: control.popup.visible ? 180 : 0
536 Behavior on rotation {
537 NumberAnimation {
538 duration: MotionAnimation.expressiveFastEffectsDuration
539 easing.type: Easing.BezierSpline
540 easing.bezierCurve: MotionAnimation.expressiveFastEffectsCurve
541 }
542 }
543 }
544 }
545
546 background: Item {
547 Rectangle {
548 id: fieldContainer
549 objectName: "exposedDropdownBackground"
550 width: parent.width
551 height: MD.Tokens.exposedDropdownMenu.fieldHeight
552 readonly property var fieldShape: control._filledField
553 ? MD.Tokens.exposedDropdownMenu.filledContainerShape
554 : MD.Tokens.exposedDropdownMenu.outlinedContainerShape
555 color: control.flat || control._outlinedField
556 ? "transparent"
557 : !control.enabled
558 ? MD.Color.transparent(
559 control.MD.Style.onSurfaceColor,
560 MD.Tokens.exposedDropdownMenu.filledDisabledContainerOpacity)
561 : control.MD.Style.surfaceContainerHighestColor
562 border.color: !control.enabled
563 ? MD.Color.transparent(
564 control.MD.Style.onSurfaceColor,
565 MD.Tokens.exposedDropdownMenu.outlinedDisabledOutlineOpacity)
566 : control._indicatorColor
567 border.width: control._outlinedField && !control.flat
568 ? control._fieldActive
569 ? MD.Tokens.exposedDropdownMenu.outlinedFocusOutlineWidth
570 : control.hovered
571 ? MD.Tokens.exposedDropdownMenu.outlinedHoverOutlineWidth
572 : MD.Tokens.exposedDropdownMenu.outlinedOutlineWidth
573 : 0
574 topLeftRadius: UiMetrics.resolveShapeRadius(fieldShape.topLeft, width, height)
575 topRightRadius: UiMetrics.resolveShapeRadius(fieldShape.topRight, width, height)
576 bottomLeftRadius: UiMetrics.resolveShapeRadius(fieldShape.bottomLeft, width, height)
577 bottomRightRadius: UiMetrics.resolveShapeRadius(fieldShape.bottomRight, width, height)
578
579 Rectangle {
580 objectName: "exposedDropdownActiveIndicator"
581 anchors.left: parent.left
582 anchors.right: parent.right
583 anchors.bottom: parent.bottom
584 height: control._fieldActive
585 ? MD.Tokens.exposedDropdownMenu.filledFocusActiveIndicatorHeight
586 : control.hovered
587 ? MD.Tokens.exposedDropdownMenu.filledHoverActiveIndicatorHeight
588 : MD.Tokens.exposedDropdownMenu.filledActiveIndicatorHeight
589 color: control._indicatorColor
590 opacity: control.enabled ? 1 : MD.Tokens.exposedDropdownMenu.disabledContentOpacity
591 visible: control._filledField && !control.flat
592 }
593 }
594
595 Item {
596 id: leadingIcon
597 objectName: "exposedDropdownLeadingIcon"
598 x: control.mirrored
599 ? parent.width - width - MD.Tokens.exposedDropdownMenu.horizontalPadding
600 : MD.Tokens.exposedDropdownMenu.horizontalPadding
601 y: (MD.Tokens.exposedDropdownMenu.fieldHeight - height) / 2
602 width: control._leadingIconSize
603 height: width
604 visible: control._hasLeadingIcon
605 opacity: control.enabled ? 1 : MD.Tokens.exposedDropdownMenu.disabledContentOpacity
606 Image {
607 anchors.fill: parent
608 source: control.leadingIconSource
609 sourceSize: Qt.size(width, height)
610 fillMode: Image.PreserveAspectFit
611 visible: source.toString().length > 0
612 }
613 MD.Symbol {
614 anchors.fill: parent
615 name: control.leadingIconName
616 iconWidth: parent.width
617 iconHeight: parent.height
618 color: control._leadingIconColor
619 visible: control.leadingIconSource.toString().length === 0
620 && control.leadingIconName.length > 0
621 }
622 }
623
624 MD.Label {
625 id: supportingLabel
626 objectName: "exposedDropdownSupportingText"
627 x: MD.Tokens.exposedDropdownMenu.horizontalPadding
628 y: MD.Tokens.exposedDropdownMenu.fieldHeight
629 + MD.Tokens.exposedDropdownMenu.supportingTextTopSpace
630 width: parent.width - MD.Tokens.exposedDropdownMenu.horizontalPadding * 2
631 text: control.supportingText
632 typescale: MD.Tokens.typescale.bodySmall
633 color: control._supportingColor
634 opacity: control.enabled ? 1 : MD.Tokens.exposedDropdownMenu.disabledContentOpacity
635 horizontalAlignment: control.mirrored ? Text.AlignRight : Text.AlignLeft
636 elide: Text.ElideRight
637 visible: !control.error && text.length > 0
638 Accessible.ignored: true
639 }
640
641 MD.Label {
642 objectName: "exposedDropdownErrorText"
643 x: supportingLabel.x
644 y: supportingLabel.y
645 width: supportingLabel.width
646 text: control.errorText
647 typescale: MD.Tokens.typescale.bodySmall
648 color: control._supportingColor
649 opacity: control.enabled ? 1 : MD.Tokens.exposedDropdownMenu.disabledContentOpacity
650 horizontalAlignment: control.mirrored ? Text.AlignRight : Text.AlignLeft
651 elide: Text.ElideRight
652 visible: control.error && text.length > 0
653 Accessible.ignored: true
654 }
655 }
656
657 popup: T.Popup {
658 id: dropdownPopup
659 objectName: "exposedDropdownPopup"
660
662 property alias currentIndex: menuList.currentIndex
664 readonly property real _fieldWindowY: control.mapToItem(null, 0, 0).y
666 readonly property var _window: control.parent ? control.parent.Window.window : null
668 readonly property real _availableBelow: _window
669 ? _window.height - _fieldWindowY
670 - MD.Tokens.exposedDropdownMenu.fieldHeight
671 - MD.Tokens.menu.verticalViewportMargin
672 - MD.Tokens.exposedDropdownMenu.popupAnchorGap
673 : Number.POSITIVE_INFINITY
675 readonly property real _availableAbove: _window
676 ? _fieldWindowY
677 - MD.Tokens.menu.verticalViewportMargin
678 - MD.Tokens.exposedDropdownMenu.popupAnchorGap
679 : Number.POSITIVE_INFINITY
681 readonly property real _naturalHeight: menuList.contentHeight + topPadding + bottomPadding
683 readonly property bool _opensBelow: _availableBelow >= _naturalHeight
684 || _availableBelow >= _availableAbove
686 readonly property real _availableHeight: Math.max(0, _opensBelow
687 ? _availableBelow
688 : _availableAbove)
689
691 function itemAt(row) {
692 return menuList.itemAtIndex(row);
693 }
694
695 x: control.mirrored ? control.width - width : 0
696 y: _opensBelow
697 ? MD.Tokens.exposedDropdownMenu.fieldHeight
698 + MD.Tokens.exposedDropdownMenu.popupAnchorGap
699 : -implicitHeight - MD.Tokens.exposedDropdownMenu.popupAnchorGap
700 width: control.width
701 implicitWidth: control.width
702 implicitHeight: Math.min(_naturalHeight, _availableHeight)
703 margins: 0
704 leftMargin: MD.Tokens.menu.horizontalViewportMargin
705 rightMargin: MD.Tokens.menu.horizontalViewportMargin
706 topMargin: MD.Tokens.menu.verticalViewportMargin
707 bottomMargin: MD.Tokens.menu.verticalViewportMargin
708 topPadding: MD.Tokens.menu.verticalGroupPadding
709 bottomPadding: MD.Tokens.menu.verticalGroupPadding
710 leftPadding: MD.Tokens.menu.verticalGroupPadding
711 rightPadding: MD.Tokens.menu.verticalGroupPadding
712 focus: true
713 modal: false
714 closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutside
715 transformOrigin: _opensBelow ? Item.Top : Item.Bottom
716
717 contentItem: ListView {
718 id: menuList
719 objectName: "menuListView"
720 implicitHeight: contentHeight
721 model: control.delegateModel
722 currentIndex: control.highlightedIndex
723 interactive: contentHeight + dropdownPopup.topPadding + dropdownPopup.bottomPadding
724 > dropdownPopup.height
725 boundsBehavior: Flickable.StopAtBounds
726 spacing: MD.Tokens.menu.verticalSegmentedGap
727 clip: true
728 ScrollIndicator.vertical: MD.ScrollIndicator {}
729 }
730
731 background: MD.ElevationRectangle {
732 objectName: "exposedDropdownPopupBackground"
733 color: control._vibrantMenu
734 ? control.MD.Style.tertiaryContainerColor
735 : control.MD.Style.surfaceContainerLowColor
736 topLeftRadius: UiMetrics.resolveShapeRadius(
737 MD.Tokens.exposedDropdownMenu.popupContainerShape.topLeft,
738 width, height)
739 topRightRadius: UiMetrics.resolveShapeRadius(
740 MD.Tokens.exposedDropdownMenu.popupContainerShape.topRight,
741 width, height)
742 bottomLeftRadius: UiMetrics.resolveShapeRadius(
743 MD.Tokens.exposedDropdownMenu.popupContainerShape.bottomLeft,
744 width, height)
745 bottomRightRadius: UiMetrics.resolveShapeRadius(
746 MD.Tokens.exposedDropdownMenu.popupContainerShape.bottomRight,
747 width, height)
748 elevation: MD.Tokens.exposedDropdownMenu.popupContainerElevation
749 }
750
751 enter: Transition {
752 NumberAnimation {
753 property: "scale"
754 from: MD.Tokens.menu.closedScale
755 to: 1
756 duration: MotionAnimation.expressiveFastSpatialDuration
757 easing.type: Easing.BezierSpline
758 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
759 }
760 NumberAnimation {
761 property: "opacity"
762 from: 0
763 to: 1
764 duration: MotionAnimation.expressiveFastEffectsDuration
765 easing.type: Easing.BezierSpline
766 easing.bezierCurve: MotionAnimation.expressiveFastEffectsCurve
767 }
768 }
769
770 exit: Transition {
771 NumberAnimation {
772 property: "scale"
773 from: 1
774 to: MD.Tokens.menu.closedScale
775 duration: MotionAnimation.expressiveFastSpatialDuration
776 easing.type: Easing.BezierSpline
777 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
778 }
779 NumberAnimation {
780 property: "opacity"
781 from: 1
782 to: 0
783 duration: MotionAnimation.expressiveFastEffectsDuration
784 easing.type: Easing.BezierSpline
785 easing.bezierCurve: MotionAnimation.expressiveFastEffectsCurve
786 }
787 }
788 }
789}
Utility functions for colors.
Definition color.h:16
Material Design 3 design tokens.
Definition tokens.h:65
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