Kirigami2

NavigationTabBar.qml
1/*
2 * SPDX-FileCopyrightText: 2021 Devin Lin <espidev@gmail.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7pragma ComponentBehavior: Bound
8
9import QtQuick
10import QtQml
11import QtQuick.Layouts
12import QtQuick.Controls as QQC2
13import QtQuick.Templates as T
14import org.kde.kirigami as Kirigami
15
16/**
17 * @brief Page navigation tab-bar, used as an alternative to sidebars for 3-5 elements.
18 *
19 * Can be combined with secondary toolbars above (if in the footer) to provide page actions.
20 *
21 * Example usage:
22 * @code{.qml}
23 * import QtQuick
24 * import org.kde.kirigami as Kirigami
25 *
26 * Kirigami.ApplicationWindow {
27 * title: "Clock"
28 *
29 * pageStack.initialPage: worldPage
30 *
31 * Kirigami.Page {
32 * id: worldPage
33 * title: "World"
34 * visible: false
35 * }
36 * Kirigami.Page {
37 * id: timersPage
38 * title: "Timers"
39 * visible: false
40 * }
41 * Kirigami.Page {
42 * id: stopwatchPage
43 * title: "Stopwatch"
44 * visible: false
45 * }
46 * Kirigami.Page {
47 * id: alarmsPage
48 * title: "Alarms"
49 * visible: false
50 * }
51 *
52 * footer: Kirigami.NavigationTabBar {
53 * actions: [
54 * Kirigami.Action {
55 * icon.name: "globe"
56 * text: "World"
57 * checked: worldPage.visible
58 * onTriggered: {
59 * if (!worldPage.visible) {
60 * while (pageStack.depth > 0) {
61 * pageStack.pop();
62 * }
63 * pageStack.push(worldPage);
64 * }
65 * }
66 * },
67 * Kirigami.Action {
68 * icon.name: "player-time"
69 * text: "Timers"
70 * checked: timersPage.visible
71 * onTriggered: {
72 * if (!timersPage.visible) {
73 * while (pageStack.depth > 0) {
74 * pageStack.pop();
75 * }
76 * pageStack.push(timersPage);
77 * }
78 * }
79 * },
80 * Kirigami.Action {
81 * icon.name: "chronometer"
82 * text: "Stopwatch"
83 * checked: stopwatchPage.visible
84 * onTriggered: {
85 * if (!stopwatchPage.visible) {
86 * while (pageStack.depth > 0) {
87 * pageStack.pop();
88 * }
89 * pageStack.push(stopwatchPage);
90 * }
91 * }
92 * },
93 * Kirigami.Action {
94 * icon.name: "notifications"
95 * text: "Alarms"
96 * checked: alarmsPage.visible
97 * onTriggered: {
98 * if (!alarmsPage.visible) {
99 * while (pageStack.depth > 0) {
100 * pageStack.pop();
101 * }
102 * pageStack.push(alarmsPage);
103 * }
104 * }
105 * }
106 * ]
107 * }
108 * }
109 * @endcode
110 *
111 * @see NavigationTabButton
112 * @since 5.87
113 * @since org.kde.kirigami 2.19
114 * @inherit QtQuick.Templates.Toolbar
115 */
116
117QQC2.ToolBar {
118 id: root
119
120//BEGIN properties
121 /**
122 * @brief This property holds the list of actions to be displayed in the toolbar.
123 */
124 property list<T.Action> actions
125
126 /**
127 * @brief This property holds a subset of visible actions of the list of actions.
128 *
129 * An action is considered visible if it is either a Kirigami.Action with
130 * ``visible`` property set to true, or it is a plain QQC2.Action.
131 */
132 readonly property list<T.Action> visibleActions: actions
133 // Note: instanceof check implies `!== null`
134 .filter(action => action instanceof Kirigami.Action
135 ? action.visible
136 : action !== null
137 )
139 /**
140 * @brief The property holds the maximum width of the toolbar actions, before margins are added.
141 */
142 property real maximumContentWidth: {
143 const minDelegateWidth = Kirigami.Units.gridUnit * 5;
144 // Always have at least the width of 5 items, so that small amounts of actions look natural.
145 return minDelegateWidth * Math.max(visibleActions.length, 5);
146 }
147
148 /**
149 * @brief This property holds the index of currently checked tab.
150 *
151 * If the index set is out of bounds, or the triggered signal did not change any checked property of an action, the index
152 * will remain the same.
153 */
154 property int currentIndex: tabGroup.checkedButton && tabGroup.buttons.length > 0 ? tabGroup.checkedButton.tabIndex : -1
155
156 /**
157 * @brief This property holds the number of tab buttons.
158 */
159 readonly property int count: tabGroup.buttons.length
160
161 /**
162 * @brief This property holds the ButtonGroup used to manage the tabs.
163 */
164 readonly property T.ButtonGroup tabGroup: tabGroup
165
166 /**
167 * @brief This property holds the calculated width that buttons on the tab bar use.
168 *
169 * @since 5.102
170 */
171 property real buttonWidth: {
172 // Counting buttons because Repeaters can be counted among visibleChildren
173 let visibleButtonCount = 0;
174 const minWidth = contentItem.height * 0.75;
175 for (const visibleChild of contentItem.visibleChildren) {
176 if (contentItem.width / visibleButtonCount >= minWidth && // make buttons go off the screen if there is physically no room for them
177 visibleChild instanceof T.AbstractButton) { // Checking for AbstractButtons because any AbstractButton can act as a tab
178 ++visibleButtonCount;
179 }
180 }
181
182 return Math.round(contentItem.width / visibleButtonCount);
183 }
184//END properties
185
186 onCurrentIndexChanged: {
187 if (currentIndex === -1) {
188 if (tabGroup.checkState !== Qt.Unchecked) {
189 tabGroup.checkState = Qt.Unchecked;
190 }
191 return;
192 }
193 if (!tabGroup.checkedButton || tabGroup.checkedButton.tabIndex !== currentIndex) {
194 const buttonForCurrentIndex = tabGroup.buttons[currentIndex]
195 if (buttonForCurrentIndex.action) {
196 // trigger also toggles and causes clicked() to be emitted
197 buttonForCurrentIndex.action.trigger();
198 } else {
199 // toggle() does not trigger the action,
200 // so don't use it if you want to use an action.
201 // It also doesn't cause clicked() to be emitted.
202 buttonForCurrentIndex.toggle();
203 }
204 }
205 }
206
207 // ensure that by default, we do not have unintended padding and spacing from the style
208 spacing: 0
209 padding: 0
210 topPadding: undefined
211 leftPadding: undefined
212 rightPadding: undefined
213 bottomPadding: undefined
214 verticalPadding: undefined
215 // Using Math.round() on horizontalPadding can cause the contentItem to jitter left and right when resizing the window.
216 horizontalPadding: Math.floor(Math.max(0, width - root.maximumContentWidth) / 2)
217
218 contentWidth: Math.ceil(Math.min(root.availableWidth, root.maximumContentWidth))
219 implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding)
220 implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding)
221 position: {
222 if (QQC2.ApplicationWindow.window?.footer === root) {
223 return QQC2.ToolBar.Footer
224 } else if (parent?.footer === root) {
225 return QQC2.ToolBar.Footer
226 } else if (parent?.parent?.footer === parent) {
227 return QQC2.ToolBar.Footer
228 } else {
229 return QQC2.ToolBar.Header
230 }
231 }
232
233 contentItem: RowLayout {
234 id: rowLayout
235 spacing: root.spacing
236 }
237
238 // Used to manage which tab is checked and change the currentIndex
239 T.ButtonGroup {
240 id: tabGroup
241 exclusive: true
242 buttons: root.contentItem.children.filter((child) => child !== instantiator)
243
244 onCheckedButtonChanged: {
245 if (!checkedButton) {
246 return
247 }
248 if (root.currentIndex !== checkedButton.tabIndex) {
249 root.currentIndex = checkedButton.tabIndex;
250 }
251 }
252 }
253
254 // Using a Repeater here because Instantiator was causing issues:
255 // NavigationTabButtons that were supposed to be destroyed were still
256 // registered as buttons in tabGroup.
257 // NOTE: This will make Repeater show up as child through visibleChildren
258 Repeater {
259 id: instantiator
260 model: root.visibleActions
261 delegate: NavigationTabButton {
262 id: delegate
263
264 required property T.Action modelData
265
266 parent: root.contentItem
267 action: modelData
268 // Workaround setting the action when checkable is not explicitly set making tabs uncheckable
269 onActionChanged: action.checkable = true
270
271 Layout.minimumWidth: root.buttonWidth
272 Layout.maximumWidth: root.buttonWidth
273 Layout.fillHeight: true
274
275 Kirigami.Theme.textColor: root.Kirigami.Theme.textColor
276 Kirigami.Theme.backgroundColor: root.Kirigami.Theme.backgroundColor
277 Kirigami.Theme.highlightColor: root.Kirigami.Theme.highlightColor
278 }
279 }
280}
int currentIndex
This property holds the index of currently checked tab.
listTAction visibleActions
This property holds a subset of visible actions of the list of actions.
real buttonWidth
This property holds the calculated width that buttons on the tab bar use.
listTAction actions
This property holds the list of actions to be displayed in the toolbar.
TButtonGroup tabGroup
This property holds the ButtonGroup used to manage the tabs.
int count
This property holds the number of tab buttons.
real maximumContentWidth
The property holds the maximum width of the toolbar actions, before margins are added.
Navigation buttons to be used for the NavigationTabBar component.
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 21 2025 11:47:53 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.