Kirigami2

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

KDE's Doxygen guidelines are available online.