Kirigami2

NavigationTabBar.qml
1 /*
2  * Copyright 2021 Devin Lin <[email protected]>
3  *
4  * SPDX-License-Identifier: LGPL-2.0-or-later
5  */
6 
7 import QtQuick 2.15
8 import QtQml 2.15
9 import QtQuick.Layouts 1.15
10 import QtGraphicalEffects 1.12 as GE
11 import QtQuick.Templates 2.15 as T
12 import org.kde.kirigami 2.19 as Kirigami
13 
14 /**
15  * @brief Page navigation tab-bar, used as an alternative to sidebars for 3-5 elements.
16  *
17  * Can be combined with secondary toolbars above (if in the footer) to provide page actions.
18  *
19  * Example usage:
20  * @code{.qml}
21  * import org.kde.kirigami 2.19 as Kirigami
22  *
23  * import QtQuick 2.15
24  * import QtQuick.Controls 2.15
25  * import QtQuick.Layouts 1.15
26  * import org.kde.kirigami 2.19 as Kirigami
27  *
28  * Kirigami.ApplicationWindow {
29  * title: "Clock"
30  *
31  * pageStack.initialPage: worldPage
32  * Kirigami.Page {
33  * id: worldPage
34  * title: "World"
35  * visible: false
36  * }
37  * Kirigami.Page {
38  * id: timersPage
39  * title: "Timers"
40  * visible: false
41  * }
42  * Kirigami.Page {
43  * id: stopwatchPage
44  * title: "Stopwatch"
45  * visible: false
46  * }
47  * Kirigami.Page {
48  * id: alarmsPage
49  * title: "Alarms"
50  * visible: false
51  * }
52  *
53  *
54  * footer: Kirigami.NavigationTabBar {
55  * actions: [
56  * Kirigami.Action {
57  * iconName: "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  * iconName: "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  * iconName: "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  * iconName: "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  *
112  * @endcode
113  *
114  * @see NavigationTabButton
115  * @since 5.87
116  * @since org.kde.kirigami 2.19
117  * @inherit QtQuick.Templates.Toolbar
118  */
119 
120 T.ToolBar {
121  id: root
122 
123 //BEGIN properties
124  /**
125  * @brief This property holds the list of actions displayed in the toolbar.
126  */
127  property list<Kirigami.Action> actions
128 
129  /**
130  * @brief The property holds the maximum width of the toolbar actions, before margins are added.
131  */
132  property real maximumContentWidth: {
133  const minDelegateWidth = Kirigami.Units.gridUnit * 5;
134  // always have at least the width of 5 items (so small amounts of actions look natural)
135  return Math.max(minDelegateWidth * actions.length, minDelegateWidth * 5);
136  }
137 
138  /**
139  * @brief This property holds the background color of the toolbar.
140  *
141  * default: ``Kirigami.Theme.highlightColor``
142  */
143  property color backgroundColor: Kirigami.Theme.backgroundColor
144 
145  /**
146  * @brief This property holds the foreground color of the toolbar (text, icon).
147  */
148  property color foregroundColor: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.85)
149 
150  /**
151  * @brief This property holds the highlight foreground color (text, icon when action is checked).
152  */
153  property color highlightForegroundColor: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.85)
154 
155  /**
156  * @brief This property holds the color of the highlight bar when an action is checked.
157  *
158  * default: ``Kirigami.Theme.highlightColor``
159  */
160  property color highlightBarColor: Kirigami.Theme.highlightColor
161 
162  /**
163  * @brief This property sets whether the toolbar should provide its own shadow.
164  *
165  * default: ``true``
166  */
167  property bool shadow: true
168 
169  /**
170  * @brief This property holds the index of currently checked tab.
171  *
172  * If the index set is out of bounds, or the triggered signal did not change any checked property of an action, the index
173  * will remain the same.
174  */
175  property int currentIndex: tabGroup.checkedButton && tabGroup.buttons.length > 0 ? tabGroup.checkedButton.tabIndex : -1
176 
177  /**
178  * @brief This property holds the number of tab buttons.
179  */
180  readonly property int count: tabGroup.buttons.length
181 
182  /**
183  * @brief This property holds the ButtonGroup used to manage the tabs.
184  */
185  readonly property T.ButtonGroup tabGroup: tabGroup
186 
187  /**
188  * @brief This property sets whether the icon colors should be masked with a single color.
189  *
190  * This only applies to buttons generated by the actions property.
191  *
192  * default: ``true``
193  *
194  * @since 5.96
195  */
196  property bool recolorIcons: true
197 
198  /**
199  * @brief This property holds the calculated width that buttons on the tab bar use.
200  *
201  * @since 5.102
202  */
203  property real buttonWidth: {
204  // Counting buttons because Repeaters can be counted among visibleChildren
205  let visibleButtonCount = 0;
206  const minWidth = contentItem.height * 0.75;
207  for (let i = 0; i < contentItem.visibleChildren.length; ++i) {
208  if (contentItem.width / visibleButtonCount >= minWidth && // make buttons go off the screen if there is physically no room for them
209  contentItem.visibleChildren[i] instanceof T.AbstractButton) { // Checking for AbstractButtons because any AbstractButton can act as a tab
210  ++visibleButtonCount;
211  }
212  }
213 
214  return Math.round(contentItem.width / visibleButtonCount);
215  }
216 //END properties
217 
218  onCurrentIndexChanged: {
219  if (currentIndex === -1) {
220  if (tabGroup.checkState !== Qt.Unchecked) {
221  tabGroup.checkState = Qt.Unchecked;
222  }
223  return;
224  }
225  if (tabGroup.checkedButton.tabIndex !== currentIndex) {
226  const buttonForCurrentIndex = tabGroup.buttons[currentIndex]
227  if (buttonForCurrentIndex.action) {
228  // trigger also toggles and causes clicked() to be emitted
229  buttonForCurrentIndex.action.trigger();
230  } else {
231  // toggle() does not trigger the action,
232  // so don't use it if you want to use an action.
233  // It also doesn't cause clicked() to be emitted.
234  buttonForCurrentIndex.toggle();
235  }
236  }
237  }
238 
239  // Using Math.round() on horizontalPadding can cause the contentItem to jitter left and right when resizing the window.
240  horizontalPadding: Math.floor(Math.max(0, width - root.maximumContentWidth) / 2)
241  contentWidth: Math.ceil(Math.min(root.availableWidth, root.maximumContentWidth))
242  implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding)
243  implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding)
244 
245  Kirigami.Theme.colorSet: Kirigami.Theme.Window
246 
247  background: Rectangle { // color & shadow
248  implicitHeight: Kirigami.Units.gridUnit * 3 + Kirigami.Units.smallSpacing * 2
249  color: root.backgroundColor
250  GE.RectangularGlow {
251  anchors.fill: parent
252  z: -1
253  visible: root.shadow
254  glowRadius: 5
255  spread: 0.3
256  color: Qt.rgba(0.0, 0.0, 0.0, 0.15)
257  }
258  }
259 
260  // Using Row because setting just width is more convenient than having to set Layout.minimumWidth and Layout.maximumWidth
261  contentItem: Row {
262  id: rowLayout
263  spacing: root.spacing
264  }
265 
266  // Used to manage which tab is checked and change the currentIndex
267  T.ButtonGroup {
268  id: tabGroup
269  exclusive: true
270  buttons: root.contentItem.children
271 
272  onCheckedButtonChanged: {
273  if (!checkedButton) {
274  return
275  }
276  if (root.currentIndex !== checkedButton.tabIndex) {
277  root.currentIndex = checkedButton.tabIndex;
278  }
279  }
280  }
281 
282  // Using an Instantiator instead of a Repeater allows us to use parent.visibleChildren.length without including a Repeater in that count.
283  Instantiator {
284  id: instantiator
285  model: root.actions
286  delegate: NavigationTabButton {
287  id: delegate
288  parent: root.contentItem
289  action: modelData
290  visible: modelData.visible
291  width: root.buttonWidth
292  recolorIcon: root.recolorIcons
293  T.ButtonGroup.group: tabGroup
294  // Workaround setting the action when checkable is not explicitly set making tabs uncheckable
295  onActionChanged: action.checkable = true
296 
297  foregroundColor: root.foregroundColor
298  highlightForegroundColor: root.highlightForegroundColor
299  highlightBarColor: root.highlightBarColor
300  }
301  }
302 }
Navigation buttons to be used for the NavigationTabBar component.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Jan 29 2023 04:11:03 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.