4pragma ComponentBehavior: Bound
7import "../core/UiMetrics.js" as UiMetrics
8import "../internal/MotionAnimation.js" as MotionAnimation
11import QtQuick.Templates as T
105 property int variant: Menu.Vertical
112 property int colorStyle: Menu.Standard
115 readonly property real _viewportWidth: parent && parent.Window.window && parent.Window.window.width > 0
116 ? parent.Window.window.width - leftMargin - rightMargin
117 : MD.Tokens.menu.maximumWidth
120 readonly property real _viewportHeight: parent && parent.Window.window && parent.Window.window.height > 0
121 ? parent.Window.window.height - topMargin - bottomMargin
122 : Number.POSITIVE_INFINITY
125 readonly property bool _layoutMirrored: mirrored || locale.textDirection === Qt.RightToLeft || (parent && parent.LayoutMirroring.enabled)
128 property var _surfaceGroups: []
131 function _updateSurfaceGroups() {
132 if (variant !== Menu.Vertical) {
137 let firstItem = null;
139 for (let index = 0; index < count; ++index) {
140 const item = itemAt(index);
143 if (_contentType(item) === 2) {
145 groups.push({ first: firstItem, last: lastItem });
155 groups.push({ first: firstItem, last: lastItem });
156 _surfaceGroups = groups;
160 function _scheduleSurfaceUpdate() {
163 control._updateSurfaceGroups();
168 function _contentType(item) {
169 return item && item._menuContentType !== undefined ? item._menuContentType : -1;
173 function _isItemGroupBoundary(item) {
174 const contentType = _contentType(item);
175 return contentType === 1 || contentType === 2;
179 function _itemGroupPosition(menuIndex) {
181 return MD.MenuItem.Only;
183 let firstAction = -1;
185 for (let index = menuIndex; index >= 0; --index) {
186 const item = itemAt(index);
187 if (_isItemGroupBoundary(item))
189 if (_contentType(item) === 0)
192 for (let index = menuIndex; index < count; ++index) {
193 const item = itemAt(index);
194 if (_isItemGroupBoundary(item))
196 if (_contentType(item) === 0)
200 if (firstAction < 0 || firstAction === lastAction)
201 return MD.MenuItem.Only;
202 if (menuIndex === firstAction)
203 return MD.MenuItem.First;
204 if (menuIndex === lastAction)
205 return MD.MenuItem.Last;
206 return MD.MenuItem.Middle;
210 function _surfaceShape(index, surfaceCount) {
211 if (surfaceCount <= 1)
212 return MD.Tokens.menu.verticalOnlyGroupShape;
214 return MD.Tokens.menu.verticalFirstGroupShape;
215 if (index === surfaceCount - 1)
216 return MD.Tokens.menu.verticalLastGroupShape;
217 return MD.Tokens.menu.verticalMiddleGroupShape;
221 function _surfaceY(index, group) {
222 return index === 0 ? 0 : topPadding + group.first.y;
226 function _surfaceHeight(index, group) {
227 return group.last.y + group.last.height - group.first.y
228 + (index === 0 ? topPadding : 0)
229 + (index === _surfaceGroups.length - 1 ? bottomPadding : 0);
232 onCountChanged: control._scheduleSurfaceUpdate()
233 onVariantChanged: control._updateSurfaceGroups()
234 onAboutToShow: control._updateSurfaceGroups()
235 onOpened: control._updateSurfaceGroups()
236 Component.onCompleted: control._scheduleSurfaceUpdate()
239 readonly
property real _itemsImplicitWidth: {
241 for (let index = 0; index < count; ++index) {
242 const item = itemAt(index);
244 itemWidth = Math.max(itemWidth, item.implicitWidth);
250 readonly
property real _itemsImplicitHeight: {
252 for (let index = 0; index < count; ++index) {
253 const item = itemAt(index);
255 itemHeight += item.implicitHeight;
257 return itemHeight + Math.max(0, count - 1) * menuList.spacing;
261 readonly
property real _naturalWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, _itemsImplicitWidth + leftPadding + rightPadding)
264 readonly
property real _naturalHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, _itemsImplicitHeight + topPadding + bottomPadding)
266 implicitWidth: Math.max(0, Math.min(_viewportWidth, Math.max(MD.Tokens.menu.minimumWidth, Math.min(MD.Tokens.menu.maximumWidth, _naturalWidth))))
267 implicitHeight: Math.max(0, Math.min(_viewportHeight, _naturalHeight))
271 rightMargin: leftMargin
273 bottomMargin: topMargin
277 rightPadding: leftPadding
282 closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutside
283 transformOrigin: cascade ? (_layoutMirrored ? Item.TopRight : Item.TopLeft) : Item.Top
289 readonly property MD.
Action _materialAction: action instanceof MD.
Action
290 ? (action as MD.
Action) : null
293 readonly property int _menuIndex: {
294 for (let itemIndex = 0; itemIndex < control.count; ++itemIndex) {
295 if (control.itemAt(itemIndex) === menuDelegate)
301 variant: control.variant ===
Menu.Vertical ? MD.MenuItem.Vertical : MD.MenuItem.Baseline
302 colorStyle: control.colorStyle ===
Menu.Vibrant ? MD.MenuItem.Vibrant : MD.MenuItem.Standard
303 groupPosition: control._itemGroupPosition(_menuIndex)
304 supportingText: _materialAction ? _materialAction.supportingText :
""
305 trailingText: _materialAction ? _materialAction.trailingText :
""
306 badgeContent: _materialAction ? _materialAction.badgeContent :
""
307 LayoutMirroring.enabled: control._layoutMirrored
308 LayoutMirroring.childrenInherit:
true
311 contentItem: ListView {
313 objectName:
"menuListView"
315 implicitWidth: control._itemsImplicitWidth
316 implicitHeight: control._itemsImplicitHeight
317 model: control.contentModel
318 currentIndex: control.currentIndex
319 interactive: contentHeight + control.
topPadding + control.bottomPadding > control.height
320 boundsBehavior: Flickable.StopAtBounds
323 LayoutMirroring.enabled: control._layoutMirrored
324 LayoutMirroring.childrenInherit:
true
326 onContentHeightChanged: {
328 control._updateSurfaceGroups();
334 background: MD.ElevationRectangle {
335 objectName:
"menuBackground"
337 implicitWidth: MD.Tokens.menu.minimumWidth
338 readonly
property var containerShape: control.variant ===
Menu.Vertical
340 : MD.Tokens.menu.containerShape
342 implicitHeight: control.variant ===
Menu.Vertical ? MD.Tokens.menu.
verticalItemHeight : MD.Tokens.menu.itemHeight
343 color: control.variant ===
Menu.Vertical
345 : control.colorStyle ===
Menu.Vibrant
346 ? control.MD.Style.tertiaryContainerColor
347 : control.MD.Style.surfaceContainerColor
348 topLeftRadius: UiMetrics.resolveShapeRadius(containerShape.topLeft,
350 topRightRadius: UiMetrics.resolveShapeRadius(containerShape.topRight,
352 bottomLeftRadius: UiMetrics.resolveShapeRadius(containerShape.bottomLeft,
354 bottomRightRadius: UiMetrics.resolveShapeRadius(containerShape.bottomRight,
359 model: control._surfaceGroups
361 delegate: MD.ElevationRectangle {
362 required
property int index
363 required
property var modelData
365 objectName:
"menuGroupSurface" + index
366 readonly
property var groupShape: control._surfaceShape(index,
367 control._surfaceGroups.length)
372 y: control._surfaceY(index, modelData)
374 height: control._surfaceHeight(index, modelData)
375 color: control.colorStyle ===
Menu.Vibrant
376 ? control.MD.Style.tertiaryContainerColor
377 : control.MD.Style.surfaceContainerLowColor
378 topLeftRadius: UiMetrics.resolveShapeRadius(groupShape.topLeft, width, height)
379 topRightRadius: UiMetrics.resolveShapeRadius(groupShape.topRight, width, height)
380 bottomLeftRadius: UiMetrics.resolveShapeRadius(groupShape.bottomLeft, width, height)
381 bottomRightRadius: UiMetrics.resolveShapeRadius(groupShape.bottomRight, width, height)
390 from: control.variant ===
Menu.Vertical ? MD.Tokens.menu.
closedScale : 0.94
392 duration: MotionAnimation.expressiveFastSpatialDuration
393 easing.type: Easing.BezierSpline
394 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
400 duration: MotionAnimation.expressiveFastEffectsDuration
401 easing.type: Easing.BezierSpline
402 easing.bezierCurve: MotionAnimation.expressiveFastEffectsCurve
410 to: control.variant ===
Menu.Vertical ? MD.Tokens.menu.
closedScale : 0.94
411 duration: MotionAnimation.expressiveFastSpatialDuration
412 easing.type: Easing.BezierSpline
413 easing.bezierCurve: MotionAnimation.expressiveFastSpatialCurve
419 duration: MotionAnimation.expressiveFastEffectsDuration
420 easing.type: Easing.BezierSpline
421 easing.bezierCurve: MotionAnimation.expressiveFastEffectsCurve
Describes a reusable Fluid action.