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 }