Fluid 1.3.0
Material Design for QtQuick and Qml
Loading...
Searching...
No Matches
NavigationRailItem.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/BadgeContent.js" as BadgeContent
10import "../internal" as Internal
11
37T.ItemDelegate {
38 id: control
39
41 property var badgeContent: _badgeAction ? _badgeAction.badgeContent : ""
42
44 property bool badgeNotification: _badgeAction ? _badgeAction.badgeNotification : false
45
47 property bool badgeVisible: _badgeAction ? _badgeAction.badgeVisible : true
48
50 property string badgeAccessibleDescription: _badgeAction ? _badgeAction.badgeAccessibleDescription : ""
51
53 property int badgeVariant: MD.Badge.Auto
54
55 readonly property MD.Action _badgeAction: action as MD.Action
56 readonly property bool _showBadge: badgeVisible
57 && (badgeNotification || BadgeContent.hasContent(badgeContent))
58 readonly property bool _useTrailingBadge: _expandedPlacement && _hasLabel
59 && !badgeNotification && badgeVariant !== MD.Badge.Small
60
65 property bool tintSourceIcon: true
66
68 property color selectedIconColor: control.MD.Style.onSecondaryContainerColor
69
71 property color selectedLabelColor: control.MD.Style.secondaryColor
72
74 property color selectedIndicatorColor: control.MD.Style.secondaryContainerColor
75
77 property color unselectedIconColor: control.MD.Style.onSurfaceVariantColor
78
80 property color unselectedLabelColor: control.MD.Style.onSurfaceVariantColor
81
83 property MD.NavigationRail rail: null
84
86 property int _railIndex: -1
87
89 readonly property bool _railExpanded: rail ? rail.expanded : false
90
92 readonly property bool _tabStop: !rail || rail._tabStopIndex === _railIndex
93
95 readonly property real _positionProgress: rail ? rail._layoutProgress
96 : (_railExpanded ? 1 : 0)
97
99 readonly property bool _expandedPlacement: _positionProgress >= 0.5
100
102 readonly property bool _hasNamedIcon: icon.name.length > 0
103
105 readonly property bool _hasSourceIcon: icon.source.toString().length > 0
106
108 readonly property bool _hasLabel: text.length > 0
109
111 readonly property color _iconColor: checked ? selectedIconColor : unselectedIconColor
112
114 readonly property color _labelColor: checked ? selectedLabelColor : unselectedLabelColor
115
117 readonly property real _contentOpacity: enabled ? 1 : MD.Tokens.navigationRail.disabledContentOpacity
118
120 readonly property real _stateLayerOpacity: {
121 if (!enabled)
122 return 0;
123 if (down)
124 return MD.Tokens.navigationRail.pressedStateLayerOpacity;
125 if (visualFocus)
126 return MD.Tokens.navigationRail.focusStateLayerOpacity;
127 if (hovered)
128 return MD.Tokens.navigationRail.hoverStateLayerOpacity;
129 return 0;
130 }
131
132 checkable: true
133 autoExclusive: true
134 hoverEnabled: true
135 focusPolicy: _tabStop || activeFocus ? Qt.StrongFocus : Qt.ClickFocus
136 padding: 0
137 spacing: _railExpanded ? MD.Tokens.navigationRail.horizontalIconLabelSpace
138 : MD.Tokens.navigationRail.verticalIconLabelSpace
139
140 icon.width: MD.Tokens.navigationRail.iconSize
141 icon.height: MD.Tokens.navigationRail.iconSize
142
143 Accessible.role: Accessible.PageTab
144 Accessible.name: BadgeContent.accessibleName(text,
145 trailingBadge && trailingBadge.visible ? trailingBadge.accessibleDescription
146 : overlayBadge && overlayBadge.visible ? overlayBadge.accessibleDescription : "")
147 Accessible.selectable: true
148 Accessible.selected: checked
149 // PageTab does not receive AbstractButton's automatic accessible press action.
150 Accessible.onPressAction: control.click()
151
153 readonly property real _expandedImplicitWidth:
154 MD.Tokens.navigationRail.horizontalFullWidthLeadingSpace
155 + icon.width
156 + (_hasLabel
157 ? MD.Tokens.navigationRail.horizontalIconLabelSpace
158 + expandedLabelMeasure.implicitWidth
159 : 0)
160 + MD.Tokens.navigationRail.horizontalFullWidthTrailingSpace
161
162 implicitWidth: _railExpanded
163 ? _expandedImplicitWidth
164 : MD.Tokens.navigationRail.collapsedContainerWidth
165 implicitHeight: MD.Tokens.navigationRail.itemContainerHeight
166 + (MD.Tokens.navigationRail.horizontalActiveIndicatorHeight
167 - MD.Tokens.navigationRail.itemContainerHeight) * _positionProgress
168
169 Keys.onUpPressed: event => {
170 if (control.rail)
171 control.rail._moveSelection(-1);
172 else
173 event.accepted = false;
174 }
175 Keys.onDownPressed: event => {
176 if (control.rail)
177 control.rail._moveSelection(1);
178 else
179 event.accepted = false;
180 }
181 Keys.onPressed: event => {
182 if (!control.rail) {
183 event.accepted = false;
184 } else if (event.key === Qt.Key_Home) {
185 control.rail._selectBoundary(false);
186 event.accepted = true;
187 } else if (event.key === Qt.Key_End) {
188 control.rail._selectBoundary(true);
189 event.accepted = true;
190 } else {
191 event.accepted = false;
192 }
193 }
194
195 Connections {
196 target: control
197
198 function onClicked() {
199 if (control.rail)
200 control.rail._activateItem(control);
201 }
202
203 function onCheckedChanged() {
204 if (control.rail)
205 control.rail._itemCheckedChanged(control);
206 }
207 }
208
209 contentItem: Item {
210 id: content
211 objectName: "navigationRailItemContent"
212
213 function lerp(start, end, progress) {
214 return start + (end - start) * progress;
215 }
216
217 readonly property real collapsedIndicatorX: (width - MD.Tokens.navigationRail.verticalActiveIndicatorWidth) / 2
218 readonly property real collapsedIndicatorY: control._hasLabel
219 ? 0
220 : (height - MD.Tokens.navigationRail.verticalActiveIndicatorHeight) / 2
221 readonly property real expandedIndicatorY: (height - MD.Tokens.navigationRail.horizontalActiveIndicatorHeight) / 2
222 readonly property real collapsedIconX: (width - control.icon.width) / 2
223 readonly property real collapsedIconY: collapsedIndicatorY
224 + (MD.Tokens.navigationRail.verticalActiveIndicatorHeight - control.icon.height) / 2
225 readonly property real expandedIconX: control._hasLabel
226 ? control.mirrored
227 ? width - MD.Tokens.navigationRail.horizontalFullWidthLeadingSpace - control.icon.width
228 : MD.Tokens.navigationRail.horizontalFullWidthLeadingSpace
229 : (width - control.icon.width) / 2
230 readonly property real expandedIconY: (height - control.icon.height) / 2
231 readonly property real collapsedLabelWidth: Math.min(width,
232 collapsedLabelMeasure.implicitWidth)
233 readonly property real badgeReservedWidth: trailingBadge.visible
234 ? trailingBadge.width + MD.Tokens.measurement.space100 : 0
235
236 implicitWidth: control.implicitWidth
237 implicitHeight: control.implicitHeight
238
239 MD.Label {
240 id: collapsedLabelMeasure
241 visible: false
242 text: control.text
243 typescale: MD.Tokens.typescale.labelMedium
244 wrapMode: Text.NoWrap
245 Accessible.ignored: true
246 }
247
248 MD.Label {
249 id: expandedLabelMeasure
250 visible: false
251 text: control.text
252 typescale: MD.Tokens.typescale.labelLarge
253 wrapMode: Text.NoWrap
254 Accessible.ignored: true
255 }
256
257 Internal.NavigationRailItemIndicator {
258 id: indicator
259 objectName: "navigationRailItemIndicator"
260
261 x: content.lerp(content.collapsedIndicatorX, 0, control._positionProgress)
262 y: content.lerp(content.collapsedIndicatorY, content.expandedIndicatorY,
263 control._positionProgress)
264 width: content.lerp(MD.Tokens.navigationRail.verticalActiveIndicatorWidth,
265 content.width, control._positionProgress)
266 height: content.lerp(MD.Tokens.navigationRail.verticalActiveIndicatorHeight,
267 MD.Tokens.navigationRail.horizontalActiveIndicatorHeight,
268 control._positionProgress)
269 selected: control.checked
270 pressed: control.down
271 pressX: control.pressX - x
272 pressY: control.pressY - y
273 selectedColor: control.selectedIndicatorColor
274 stateLayerColor: control._iconColor
275 stateLayerOpacity: control._stateLayerOpacity
276
277 }
278
279 Item {
280 id: iconSlot
281 objectName: "navigationRailItemIcon"
282
283 x: content.lerp(content.collapsedIconX, content.expandedIconX,
284 control._positionProgress)
285 y: content.lerp(content.collapsedIconY, content.expandedIconY,
286 control._positionProgress)
287 width: control.icon.width
288 height: control.icon.height
289 opacity: control._contentOpacity
290 visible: control._hasNamedIcon || control._hasSourceIcon
291 Accessible.ignored: true
292
293 Internal.IconImage {
294 id: sourceIcon
295 anchors.fill: iconSlot
296 source: control.icon.source
297 sourceSize: Qt.size(sourceIcon.width, sourceIcon.height)
298 fillMode: Image.PreserveAspectFit
299 color: control.tintSourceIcon ? control._iconColor : "transparent"
300 visible: control._hasSourceIcon
301 Accessible.ignored: true
302 }
303
304 MD.Symbol {
305 anchors.fill: parent
306 name: control.icon.name
307 iconWidth: width
308 iconHeight: height
309 color: control._iconColor
310 fill: control.checked
311 visible: !control._hasSourceIcon && control._hasNamedIcon
312 Accessible.ignored: true
313 }
314 }
315
316 MD.BadgeOverlay {
317 id: overlayBadge
318 objectName: "navigationRailBadgeOverlay"
319 target: iconSlot
320 layoutMirrored: control.mirrored
321 count: BadgeContent.count(control.badgeContent)
322 text: BadgeContent.text(control.badgeContent)
323 locale: control.locale
324 variant: control.badgeNotification ? MD.Badge.Small : control.badgeVariant
325 accessibleDescription: control.badgeAccessibleDescription.length > 0
326 ? control.badgeAccessibleDescription : _defaultAccessibleDescription
327 visible: control._showBadge && !control._useTrailingBadge && targetAvailable
328 }
329
330 MD.Label {
331 id: label
332 objectName: "navigationRailItemLabel"
333
334 x: control._expandedPlacement
335 ? control.mirrored
336 ? MD.Tokens.navigationRail.horizontalFullWidthTrailingSpace
337 + content.badgeReservedWidth
338 : MD.Tokens.navigationRail.horizontalFullWidthLeadingSpace
339 + control.icon.width + MD.Tokens.navigationRail.horizontalIconLabelSpace
340 : (content.width - content.collapsedLabelWidth) / 2
341 y: control._expandedPlacement
342 ? (content.height - height) / 2
343 : content.collapsedIndicatorY
344 + MD.Tokens.navigationRail.verticalActiveIndicatorHeight
345 + MD.Tokens.navigationRail.verticalIconLabelSpace
346 width: control._expandedPlacement
347 ? control.mirrored
348 ? Math.max(0, content.expandedIconX
349 - MD.Tokens.navigationRail.horizontalIconLabelSpace
350 - MD.Tokens.navigationRail.horizontalFullWidthTrailingSpace
351 - content.badgeReservedWidth)
352 : Math.max(0, content.width
353 - MD.Tokens.navigationRail.horizontalFullWidthLeadingSpace
354 - control.icon.width
355 - MD.Tokens.navigationRail.horizontalIconLabelSpace
356 - MD.Tokens.navigationRail.horizontalFullWidthTrailingSpace
357 - content.badgeReservedWidth)
358 : content.collapsedLabelWidth
359 text: control.text
360 color: control._labelColor
361 opacity: control._contentOpacity
362 * Math.pow(control._positionProgress * 2 - 1, 2)
363 visible: control._hasLabel
364 elide: Text.ElideRight
365 wrapMode: Text.NoWrap
366 typescale: control._expandedPlacement ? MD.Tokens.typescale.labelLarge
367 : MD.Tokens.typescale.labelMedium
368 horizontalAlignment: control._expandedPlacement
369 ? (control.mirrored ? Text.AlignRight : Text.AlignLeft)
370 : Text.AlignHCenter
371 Accessible.ignored: true
372
373 }
374
375 MD.Badge {
376 id: trailingBadge
377 objectName: "navigationRailBadgeTrailing"
378 x: control.mirrored ? MD.Tokens.navigationRail.horizontalFullWidthTrailingSpace
379 : content.width - MD.Tokens.navigationRail.horizontalFullWidthTrailingSpace - width
380 y: (content.height - height) / 2
381 count: BadgeContent.count(control.badgeContent)
382 text: BadgeContent.text(control.badgeContent)
383 locale: control.locale
384 variant: control.badgeVariant
385 accessibleDescription: control.badgeAccessibleDescription.length > 0
386 ? control.badgeAccessibleDescription : _defaultAccessibleDescription
387 visible: control._showBadge && control._useTrailingBadge
388 }
389 }
390
391 background: Item {}
392}
Places a badge at an icon's logical upper trailing corner.
Material Design 3 design tokens.
Definition tokens.h:71
Tokens(QObject *parent=nullptr)
Definition tokens.cpp:8
Displays text using a selectable Material 3 type scale.
Definition Label.qml:15
Immutable Material Design 3 badge metrics.
Definition badge.h:20
Material Design 3 Expressive navigation rail tokens.
Material Symbols icon tokens.
Definition symbol.h:17