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 import QtQuick 2.15
0008 import QtQml 2.15
0009 import QtQuick.Layouts 1.15
0010 import QtGraphicalEffects 1.12 as GE
0011 import QtQuick.Templates 2.15 as T
0012 import org.kde.kirigami 2.19 as Kirigami
0013 
0014 /**
0015  * @brief Page navigation tab-bar, used as an alternative to sidebars for 3-5 elements.
0016  *
0017  * Can be combined with secondary toolbars above (if in the footer) to provide page actions.
0018  *
0019  * Example usage:
0020  * @code{.qml}
0021  * import QtQuick 2.15
0022  * import QtQuick.Controls 2.15
0023  * import QtQuick.Layouts 1.15
0024  * import org.kde.kirigami 2.20 as Kirigami
0025  *
0026  * Kirigami.ApplicationWindow {
0027  *     title: "Clock"
0028  *
0029  *     pageStack.initialPage: worldPage
0030  *     Kirigami.Page {
0031  *         id: worldPage
0032  *         title: "World"
0033  *         visible: false
0034  *     }
0035  *     Kirigami.Page {
0036  *         id: timersPage
0037  *         title: "Timers"
0038  *         visible: false
0039  *     }
0040  *     Kirigami.Page {
0041  *         id: stopwatchPage
0042  *         title: "Stopwatch"
0043  *         visible: false
0044  *     }
0045  *     Kirigami.Page {
0046  *         id: alarmsPage
0047  *         title: "Alarms"
0048  *         visible: false
0049  *     }
0050  *
0051  *
0052  *     footer: Kirigami.NavigationTabBar {
0053  *         actions: [
0054  *             Kirigami.Action {
0055  *                 iconName: "globe"
0056  *                 text: "World"
0057  *                 checked: worldPage.visible
0058  *                 onTriggered: {
0059  *                      if (!worldPage.visible) {
0060  *                          while (pageStack.depth > 0) {
0061  *                              pageStack.pop();
0062  *                          }
0063  *                          pageStack.push(worldPage);
0064  *                     }
0065  *                 }
0066  *             },
0067  *             Kirigami.Action {
0068  *                 iconName: "player-time"
0069  *                 text: "Timers"
0070  *                 checked: timersPage.visible
0071  *                 onTriggered: {
0072  *                     if (!timersPage.visible) {
0073  *                         while (pageStack.depth > 0) {
0074  *                             pageStack.pop();
0075  *                         }
0076  *                         pageStack.push(timersPage);
0077  *                     }
0078  *                 }
0079  *             },
0080  *             Kirigami.Action {
0081  *                 iconName: "chronometer"
0082  *                 text: "Stopwatch"
0083  *                 checked: stopwatchPage.visible
0084  *                 onTriggered: {
0085  *                     if (!stopwatchPage.visible) {
0086  *                         while (pageStack.depth > 0) {
0087  *                             pageStack.pop();
0088  *                         }
0089  *                         pageStack.push(stopwatchPage);
0090  *                     }
0091  *                 }
0092  *             },
0093  *             Kirigami.Action {
0094  *                 iconName: "notifications"
0095  *                 text: "Alarms"
0096  *                 checked: alarmsPage.visible
0097  *                 onTriggered: {
0098  *                     if (!alarmsPage.visible) {
0099  *                         while (pageStack.depth > 0) {
0100  *                             pageStack.pop();
0101  *                         }
0102  *                         pageStack.push(alarmsPage);
0103  *                     }
0104  *                 }
0105  *             }
0106  *         ]
0107  *     }
0108  * }
0109  * @endcode
0110  * @see kirigami::NavigationTabButton
0111  * @see <a href="https://develop.kde.org/hig/components/navigation/navigationtabbar">Human Interface Guidelines on Navigation Tab Bars</a>
0112  * @since KDE Frameworks 5.87
0113  * @since org.kde.kirigami 2.19
0114  * @inherit QtQuick.Templates.Toolbar
0115  */
0116 
0117 T.ToolBar {
0118     id: root
0119 
0120 //BEGIN properties
0121     /**
0122      * @brief This property holds the list of actions displayed in the toolbar.
0123      */
0124     property list<Kirigami.Action> actions
0125 
0126     /**
0127      * @brief The property holds the maximum width of the toolbar actions, before margins are added.
0128      */
0129     property real maximumContentWidth: {
0130         const minDelegateWidth = Kirigami.Units.gridUnit * 5;
0131         // Always have at least the width of 5 items, so that small amounts of actions look natural.
0132         return minDelegateWidth * Math.max(actions.length, 5);
0133     }
0134 
0135     /**
0136      * @brief This property holds the background color of the toolbar.
0137      *
0138      * default: @link Kirigami.PlatformTheme.highlightColor Kirigami.Theme.highlightColor @endlink
0139      */
0140     property color backgroundColor: Kirigami.Theme.backgroundColor
0141 
0142     /**
0143      * @brief This property holds the foreground color of the toolbar (text, icon).
0144      */
0145     property color foregroundColor: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.85)
0146 
0147     /**
0148      * @brief This property holds the highlight foreground color (text, icon when action is checked).
0149      */
0150     property color highlightForegroundColor: Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.85)
0151 
0152     /**
0153      * @brief This property holds the color of the highlight bar when an action is checked.
0154      *
0155      * default: @link Kirigami.PlatformTheme.highlightColor Kirigami.Theme.highlightColor @endlink
0156      */
0157     property color highlightBarColor: Kirigami.Theme.highlightColor
0158 
0159     /**
0160      * @brief This property sets whether the toolbar should provide its own shadow.
0161      *
0162      * default: ``true``
0163      */
0164     property bool shadow: true
0165 
0166     /**
0167      * @brief This property holds the index of currently checked tab.
0168      *
0169      * If the index set is out of bounds, or the triggered signal did not change any checked property of an action, the index
0170      * will remain the same.
0171      */
0172     property int currentIndex: tabGroup.checkedButton && tabGroup.buttons.length > 0 ? tabGroup.checkedButton.tabIndex : -1
0173 
0174     /**
0175      * @brief This property holds the number of tab buttons.
0176      */
0177     readonly property int count: tabGroup.buttons.length
0178 
0179     /**
0180      * @brief This property holds the ButtonGroup used to manage the tabs.
0181      */
0182     readonly property T.ButtonGroup tabGroup: tabGroup
0183 
0184     /**
0185      * @brief This property sets whether the icon colors should be masked with a single color.
0186      *
0187      * This only applies to buttons generated by the actions property.
0188      *
0189      * default: ``true``
0190      *
0191      * @since KDE Frameworks 5.96
0192      */
0193     property bool recolorIcons: true
0194 
0195     /**
0196      * @brief This property holds the calculated width that buttons on the tab bar use.
0197      *
0198      * @since KDE Frameworks 5.102
0199      */
0200     property real buttonWidth: {
0201         // Counting buttons because Repeaters can be counted among visibleChildren
0202         let visibleButtonCount = 0;
0203         const minWidth = contentItem.height * 0.75;
0204         for (let i = 0; i < contentItem.visibleChildren.length; ++i) {
0205             if (contentItem.width / visibleButtonCount >= minWidth && // make buttons go off the screen if there is physically no room for them
0206                 contentItem.visibleChildren[i] instanceof T.AbstractButton) { // Checking for AbstractButtons because any AbstractButton can act as a tab
0207                 ++visibleButtonCount;
0208             }
0209         }
0210 
0211         return Math.round(contentItem.width / visibleButtonCount);
0212     }
0213 //END properties
0214 
0215     onCurrentIndexChanged: {
0216         if (currentIndex === -1) {
0217             if (tabGroup.checkState !== Qt.Unchecked) {
0218                 tabGroup.checkState = Qt.Unchecked;
0219             }
0220             return;
0221         }
0222         if (tabGroup.checkedButton.tabIndex !== currentIndex) {
0223             const buttonForCurrentIndex = tabGroup.buttons[currentIndex]
0224             if (buttonForCurrentIndex.action) {
0225                 // trigger also toggles and causes clicked() to be emitted
0226                 buttonForCurrentIndex.action.trigger();
0227             } else {
0228                 // toggle() does not trigger the action,
0229                 // so don't use it if you want to use an action.
0230                 // It also doesn't cause clicked() to be emitted.
0231                 buttonForCurrentIndex.toggle();
0232             }
0233         }
0234     }
0235 
0236     // Using Math.round() on horizontalPadding can cause the contentItem to jitter left and right when resizing the window.
0237     horizontalPadding: Math.floor(Math.max(0, width - root.maximumContentWidth) / 2)
0238     contentWidth: Math.ceil(Math.min(root.availableWidth, root.maximumContentWidth))
0239     implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding)
0240     implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding)
0241 
0242     Kirigami.Theme.colorSet: Kirigami.Theme.Window
0243 
0244     background: Rectangle { // color & shadow
0245         implicitHeight: Kirigami.Units.gridUnit * 3 + Kirigami.Units.smallSpacing * 2
0246         color: root.backgroundColor
0247         GE.RectangularGlow {
0248             anchors.fill: parent
0249             z: -1
0250             visible: root.shadow
0251             glowRadius: 5
0252             spread: 0.3
0253             color: Qt.rgba(0.0, 0.0, 0.0, 0.15)
0254         }
0255     }
0256 
0257     // Using Row because setting just width is more convenient than having to set Layout.minimumWidth and Layout.maximumWidth
0258     contentItem: Row {
0259         id: rowLayout
0260         spacing: root.spacing
0261     }
0262 
0263     // Used to manage which tab is checked and change the currentIndex
0264     T.ButtonGroup {
0265         id: tabGroup
0266         exclusive: true
0267         buttons: root.contentItem.children
0268 
0269         onCheckedButtonChanged: {
0270             if (!checkedButton) {
0271                 return
0272             }
0273             if (root.currentIndex !== checkedButton.tabIndex) {
0274                 root.currentIndex = checkedButton.tabIndex;
0275             }
0276         }
0277     }
0278 
0279     // Using a Repeater here because Instantiator was causing issues:
0280     // NavigationTabButtons that were supposed to be destroyed were still
0281     // registered as buttons in tabGroup.
0282     // NOTE: This will make Repeater show up as child through visibleChildren
0283     Repeater {
0284         id: instantiator
0285         model: root.actions
0286         delegate: NavigationTabButton {
0287             id: delegate
0288             parent: root.contentItem
0289             action: modelData
0290             visible: modelData.visible
0291             width: root.buttonWidth
0292             recolorIcon: root.recolorIcons
0293             T.ButtonGroup.group: tabGroup
0294             // Workaround setting the action when checkable is not explicitly set making tabs uncheckable
0295             onActionChanged: action.checkable = true
0296 
0297             foregroundColor: root.foregroundColor
0298             highlightForegroundColor: root.highlightForegroundColor
0299             highlightBarColor: root.highlightBarColor
0300         }
0301     }
0302 }