Warning, /frameworks/kirigami/src/controls/NavigationTabBar.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 * Copyright 2021 Devin Lin <espidev@gmail.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 pragma ComponentBehavior: Bound 0008 0009 import QtQuick 0010 import QtQml 0011 import QtQuick.Layouts 0012 import QtQuick.Controls as QQC2 0013 import QtQuick.Templates as T 0014 import org.kde.kirigami as Kirigami 0015 0016 /** 0017 * @brief Page navigation tab-bar, used as an alternative to sidebars for 3-5 elements. 0018 * 0019 * Can be combined with secondary toolbars above (if in the footer) to provide page actions. 0020 * 0021 * Example usage: 0022 * @code{.qml} 0023 * import QtQuick 2.15 0024 * import QtQuick.Controls 2.15 0025 * import QtQuick.Layouts 1.15 0026 * import org.kde.kirigami 2.20 as Kirigami 0027 * 0028 * Kirigami.ApplicationWindow { 0029 * title: "Clock" 0030 * 0031 * pageStack.initialPage: worldPage 0032 * 0033 * Kirigami.Page { 0034 * id: worldPage 0035 * title: "World" 0036 * visible: false 0037 * } 0038 * Kirigami.Page { 0039 * id: timersPage 0040 * title: "Timers" 0041 * visible: false 0042 * } 0043 * Kirigami.Page { 0044 * id: stopwatchPage 0045 * title: "Stopwatch" 0046 * visible: false 0047 * } 0048 * Kirigami.Page { 0049 * id: alarmsPage 0050 * title: "Alarms" 0051 * visible: false 0052 * } 0053 * 0054 * footer: Kirigami.NavigationTabBar { 0055 * actions: [ 0056 * Kirigami.Action { 0057 * icon.name: "globe" 0058 * text: "World" 0059 * checked: worldPage.visible 0060 * onTriggered: { 0061 * if (!worldPage.visible) { 0062 * while (pageStack.depth > 0) { 0063 * pageStack.pop(); 0064 * } 0065 * pageStack.push(worldPage); 0066 * } 0067 * } 0068 * }, 0069 * Kirigami.Action { 0070 * icon.name: "player-time" 0071 * text: "Timers" 0072 * checked: timersPage.visible 0073 * onTriggered: { 0074 * if (!timersPage.visible) { 0075 * while (pageStack.depth > 0) { 0076 * pageStack.pop(); 0077 * } 0078 * pageStack.push(timersPage); 0079 * } 0080 * } 0081 * }, 0082 * Kirigami.Action { 0083 * icon.name: "chronometer" 0084 * text: "Stopwatch" 0085 * checked: stopwatchPage.visible 0086 * onTriggered: { 0087 * if (!stopwatchPage.visible) { 0088 * while (pageStack.depth > 0) { 0089 * pageStack.pop(); 0090 * } 0091 * pageStack.push(stopwatchPage); 0092 * } 0093 * } 0094 * }, 0095 * Kirigami.Action { 0096 * icon.name: "notifications" 0097 * text: "Alarms" 0098 * checked: alarmsPage.visible 0099 * onTriggered: { 0100 * if (!alarmsPage.visible) { 0101 * while (pageStack.depth > 0) { 0102 * pageStack.pop(); 0103 * } 0104 * pageStack.push(alarmsPage); 0105 * } 0106 * } 0107 * } 0108 * ] 0109 * } 0110 * } 0111 * @endcode 0112 * 0113 * @see NavigationTabButton 0114 * @since 5.87 0115 * @since org.kde.kirigami 2.19 0116 * @inherit QtQuick.Templates.Toolbar 0117 */ 0118 0119 QQC2.ToolBar { 0120 id: root 0121 0122 //BEGIN properties 0123 /** 0124 * @brief This property holds the list of actions to be displayed in the toolbar. 0125 */ 0126 property list<T.Action> actions 0127 0128 /** 0129 * @brief This property holds a subset of visible actions of the list of actions. 0130 * 0131 * An action is considered visible if it is either a Kirigami.Action with 0132 * ``visible`` property set to true, or it is a plain QQC2.Action. 0133 */ 0134 readonly property list<T.Action> visibleActions: actions 0135 // Note: instanceof check implies `!== null` 0136 .filter(action => action instanceof Kirigami.Action 0137 ? action.visible 0138 : action !== null 0139 ) 0140 0141 /** 0142 * @brief The property holds the maximum width of the toolbar actions, before margins are added. 0143 */ 0144 property real maximumContentWidth: { 0145 const minDelegateWidth = Kirigami.Units.gridUnit * 5; 0146 // Always have at least the width of 5 items, so that small amounts of actions look natural. 0147 return minDelegateWidth * Math.max(visibleActions.length, 5); 0148 } 0149 0150 /** 0151 * @brief This property holds the index of currently checked tab. 0152 * 0153 * If the index set is out of bounds, or the triggered signal did not change any checked property of an action, the index 0154 * will remain the same. 0155 */ 0156 property int currentIndex: tabGroup.checkedButton && tabGroup.buttons.length > 0 ? tabGroup.checkedButton.tabIndex : -1 0157 0158 /** 0159 * @brief This property holds the number of tab buttons. 0160 */ 0161 readonly property int count: tabGroup.buttons.length 0162 0163 /** 0164 * @brief This property holds the ButtonGroup used to manage the tabs. 0165 */ 0166 readonly property T.ButtonGroup tabGroup: tabGroup 0167 0168 /** 0169 * @brief This property holds the calculated width that buttons on the tab bar use. 0170 * 0171 * @since 5.102 0172 */ 0173 property real buttonWidth: { 0174 // Counting buttons because Repeaters can be counted among visibleChildren 0175 let visibleButtonCount = 0; 0176 const minWidth = contentItem.height * 0.75; 0177 for (const visibleChild of contentItem.visibleChildren) { 0178 if (contentItem.width / visibleButtonCount >= minWidth && // make buttons go off the screen if there is physically no room for them 0179 visibleChild instanceof T.AbstractButton) { // Checking for AbstractButtons because any AbstractButton can act as a tab 0180 ++visibleButtonCount; 0181 } 0182 } 0183 0184 return Math.round(contentItem.width / visibleButtonCount); 0185 } 0186 //END properties 0187 0188 onCurrentIndexChanged: { 0189 if (currentIndex === -1) { 0190 if (tabGroup.checkState !== Qt.Unchecked) { 0191 tabGroup.checkState = Qt.Unchecked; 0192 } 0193 return; 0194 } 0195 if (!tabGroup.checkedButton || tabGroup.checkedButton.tabIndex !== currentIndex) { 0196 const buttonForCurrentIndex = tabGroup.buttons[currentIndex] 0197 if (buttonForCurrentIndex.action) { 0198 // trigger also toggles and causes clicked() to be emitted 0199 buttonForCurrentIndex.action.trigger(); 0200 } else { 0201 // toggle() does not trigger the action, 0202 // so don't use it if you want to use an action. 0203 // It also doesn't cause clicked() to be emitted. 0204 buttonForCurrentIndex.toggle(); 0205 } 0206 } 0207 } 0208 0209 // ensure that by default, we do not have unintended padding and spacing from the style 0210 spacing: 0 0211 padding: 0 0212 topPadding: undefined 0213 leftPadding: undefined 0214 rightPadding: undefined 0215 bottomPadding: undefined 0216 verticalPadding: undefined 0217 // Using Math.round() on horizontalPadding can cause the contentItem to jitter left and right when resizing the window. 0218 horizontalPadding: Math.floor(Math.max(0, width - root.maximumContentWidth) / 2) 0219 0220 contentWidth: Math.ceil(Math.min(root.availableWidth, root.maximumContentWidth)) 0221 implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding) 0222 implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding) 0223 position: { 0224 if (QQC2.ApplicationWindow.window?.footer === root) { 0225 return QQC2.ToolBar.Footer 0226 } else if (parent?.footer === root) { 0227 return QQC2.ToolBar.Footer 0228 } else if (parent?.parent?.footer === parent) { 0229 return QQC2.ToolBar.Footer 0230 } else { 0231 return QQC2.ToolBar.Header 0232 } 0233 } 0234 0235 // Using Row because setting just width is more convenient than having to set Layout.minimumWidth and Layout.maximumWidth 0236 contentItem: Row { 0237 id: rowLayout 0238 spacing: root.spacing 0239 } 0240 0241 // Used to manage which tab is checked and change the currentIndex 0242 T.ButtonGroup { 0243 id: tabGroup 0244 exclusive: true 0245 buttons: root.contentItem.children 0246 0247 onCheckedButtonChanged: { 0248 if (!checkedButton) { 0249 return 0250 } 0251 if (root.currentIndex !== checkedButton.tabIndex) { 0252 root.currentIndex = checkedButton.tabIndex; 0253 } 0254 } 0255 } 0256 0257 // Using a Repeater here because Instantiator was causing issues: 0258 // NavigationTabButtons that were supposed to be destroyed were still 0259 // registered as buttons in tabGroup. 0260 // NOTE: This will make Repeater show up as child through visibleChildren 0261 Repeater { 0262 id: instantiator 0263 model: root.visibleActions 0264 delegate: NavigationTabButton { 0265 id: delegate 0266 0267 required property T.Action modelData 0268 0269 parent: root.contentItem 0270 action: modelData 0271 width: root.buttonWidth 0272 // Workaround setting the action when checkable is not explicitly set making tabs uncheckable 0273 onActionChanged: action.checkable = true 0274 0275 Kirigami.Theme.textColor: root.Kirigami.Theme.textColor 0276 Kirigami.Theme.backgroundColor: root.Kirigami.Theme.backgroundColor 0277 Kirigami.Theme.highlightColor: root.Kirigami.Theme.highlightColor 0278 } 0279 } 0280 }