Warning, /frameworks/kirigami/src/controls/GlobalDrawer.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  *  SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 pragma ComponentBehavior: Bound
0008 
0009 import QtQuick
0010 import QtQuick.Templates as T
0011 import QtQuick.Controls as QQC2
0012 import QtQuick.Layouts
0013 import org.kde.kirigami as Kirigami
0014 import "private" as KP
0015 
0016 /**
0017  * A specialized form of the Drawer intended for showing an application's
0018  * always-available global actions. Think of it like a mobile version of
0019  * a desktop application's menubar.
0020  *
0021  * Example usage:
0022  * @code
0023  * import org.kde.kirigami 2.4 as Kirigami
0024  *
0025  * Kirigami.ApplicationWindow {
0026  *  [...]
0027  *     globalDrawer: Kirigami.GlobalDrawer {
0028  *         actions: [
0029  *            Kirigami.Action {
0030  *                text: "View"
0031  *                icon.name: "view-list-icons"
0032  *                Kirigami.Action {
0033  *                        text: "action 1"
0034  *                }
0035  *                Kirigami.Action {
0036  *                        text: "action 2"
0037  *                }
0038  *                Kirigami.Action {
0039  *                        text: "action 3"
0040  *                }
0041  *            },
0042  *            Kirigami.Action {
0043  *                text: "Sync"
0044  *                icon.name: "folder-sync"
0045  *            }
0046  *         ]
0047  *     }
0048  *  [...]
0049  * }
0050  * @endcode
0051  *
0052  */
0053 Kirigami.OverlayDrawer {
0054     id: root
0055 
0056     edge: Qt.application.layoutDirection === Qt.RightToLeft ? Qt.RightEdge : Qt.LeftEdge
0057 
0058     handleClosedIcon.source: null
0059     handleOpenIcon.source: null
0060 
0061     handleVisible: {
0062         // When drawer is inline with content and opened, there is no point is showing handle.
0063         if (!modal && drawerOpen) {
0064             return false;
0065         }
0066 
0067         // GlobalDrawer can be hidden by controlsVisible...
0068         if (typeof applicationWindow === "function") {
0069             const w = applicationWindow();
0070             if (w && !w.controlsVisible) {
0071                 return false;
0072             }
0073         }
0074 
0075         // ...but it still performs additional checks.
0076         return !isMenu || Kirigami.Settings.isMobile;
0077     }
0078 
0079     enabled: !isMenu || Kirigami.Settings.isMobile
0080 
0081 //BEGIN properties
0082     /**
0083      * @brief This property holds the title displayed at the top of the drawer.
0084      * @see org::kde::kirigami::private::BannerImage::title
0085      * @property string title
0086      */
0087     property string title
0088 
0089     /**
0090      * @brief This property holds an icon to be displayed alongside the title.
0091      * @see org::kde::kirigami::private::BannerImage::titleIcon
0092      * @see org::kde::kirigami::Icon::source
0093      * @property var titleIcon
0094      */
0095     property var titleIcon
0096 
0097     /**
0098      * @brief This property holds the actions displayed in the drawer.
0099      *
0100      * The list of actions can be nested having a tree structure.
0101      * A tree depth bigger than 2 is discouraged.
0102      *
0103      * Example usage:
0104      *
0105      * @code
0106      * import org.kde.kirigami as Kirigami
0107      *
0108      * Kirigami.ApplicationWindow {
0109      *     globalDrawer: Kirigami.GlobalDrawer {
0110      *         actions: [
0111      *            Kirigami.Action {
0112      *                text: "View"
0113      *                icon.name: "view-list-icons"
0114      *                Kirigami.Action {
0115      *                    text: "action 1"
0116      *                }
0117      *                Kirigami.Action {
0118      *                    text: "action 2"
0119      *                }
0120      *                Kirigami.Action {
0121      *                    text: "action 3"
0122      *                }
0123      *            },
0124      *            Kirigami.Action {
0125      *                text: "Sync"
0126      *                icon.name: "folder-sync"
0127      *            }
0128      *         ]
0129      *     }
0130      * }
0131      * @endcode
0132      * @property list<T.Action> actions
0133      */
0134     property list<T.Action> actions
0135 
0136     /**
0137      * @brief This property holds an item that will always be displayed at the top of the drawer.
0138      *
0139      * If the drawer contents can be scrolled, this item will stay still and won't scroll.
0140      *
0141      * @note This property is mainly intended for toolbars.
0142      * @since 2.12
0143      */
0144     property alias header: mainLayout.header
0145 
0146     /**
0147      * @brief This property holds an item that will always be displayed at the bottom of the drawer.
0148      *
0149      * If the drawer contents can be scrolled, this item will stay still and won't scroll.
0150      *
0151      * @note This property is mainly intended for toolbars.
0152      * @since 6.0
0153      */
0154     property alias footer: mainLayout.footer
0155 
0156     /**
0157      * @brief This property holds items that are displayed above the actions.
0158      *
0159      * Example usage:
0160      * @code
0161      * import org.kde.kirigami 2.4 as Kirigami
0162      *
0163      * Kirigami.ApplicationWindow {
0164      *  [...]
0165      *     globalDrawer: Kirigami.GlobalDrawer {
0166      *         actions: [...]
0167      *         topContent: [Button {
0168      *             text: "Button"
0169      *             onClicked: //do stuff
0170      *         }]
0171      *     }
0172      *  [...]
0173      * }
0174      * @endcode
0175      * @property list<QtObject> topContent
0176      */
0177     property alias topContent: topContent.data
0178 
0179     /**
0180      * @brief This property holds items that are displayed under the actions.
0181      *
0182      * Example usage:
0183      * @code
0184      * import org.kde.kirigami 2.4 as Kirigami
0185      *
0186      * Kirigami.ApplicationWindow {
0187      *  [...]
0188      *     globalDrawer: Kirigami.GlobalDrawer {
0189      *         actions: [...]
0190      *         Button {
0191      *             text: "Button"
0192      *             onClicked: //do stuff
0193      *         }
0194      *     }
0195      *  [...]
0196      * }
0197      * @endcode
0198      * @note This is a `default` property.
0199      * @property list<QtObject> content
0200      */
0201     default property alias content: mainContent.data
0202 
0203     /**
0204      * @brief This property sets whether content items at the top should be shown.
0205      * when the drawer is collapsed as a sidebar.
0206      *
0207      * If you want to keep some items visible and some invisible, set this to
0208      * false and control the visibility/opacity of individual items,
0209      * binded to the collapsed property
0210      *
0211      * default: ``false``
0212      *
0213      * @since 2.5
0214      */
0215     property bool showTopContentWhenCollapsed: false
0216 
0217     /**
0218      * @brief This property sets whether content items at the bottom should be shown.
0219      * when the drawer is collapsed as a sidebar.
0220      *
0221      * If you want to keep some items visible and some invisible, set this to
0222      * false and control the visibility/opacity of individual items,
0223      * binded to the collapsed property
0224      *
0225      * default: ``false``
0226      *
0227      * @see content
0228      * @since 2.5
0229      */
0230     property bool showContentWhenCollapsed: false
0231 
0232     // TODO
0233     property bool showHeaderWhenCollapsed: false
0234 
0235     /**
0236      * @brief This property sets whether activating a leaf action resets the
0237      * menu to show leaf's parent actions.
0238      *
0239      * A leaf action is an action without any child actions.
0240      *
0241      * default: ``true``
0242      */
0243     property bool resetMenuOnTriggered: true
0244 
0245     /**
0246      * @brief This property points to the action acting as a submenu
0247      */
0248     readonly property T.Action currentSubMenu: stackView.currentItem?.current ?? null
0249 
0250     /**
0251      * @brief This property sets whether the drawer becomes a menu on the desktop.
0252      *
0253      * default: ``false``
0254      *
0255      * @since 2.11
0256      */
0257     property bool isMenu: false
0258 
0259     /**
0260      * @brief This property sets the visibility of the collapse button
0261      * when the drawer collapsible.
0262      *
0263      * default: ``true``
0264      *
0265      * @since 2.12
0266      */
0267     property bool collapseButtonVisible: true
0268 //END properties
0269 
0270     /**
0271      * @brief This function reverts the menu back to its initial state
0272      */
0273     function resetMenu() {
0274         stackView.pop(stackView.get(0, T.StackView.DontLoad));
0275         if (root.modal) {
0276             root.drawerOpen = false;
0277         }
0278     }
0279 
0280     // rightPadding: !Kirigami.Settings.isMobile && mainFlickable.contentHeight > mainFlickable.height ? Kirigami.Units.gridUnit : Kirigami.Units.smallSpacing
0281 
0282     Kirigami.Theme.colorSet: modal ? Kirigami.Theme.Window : Kirigami.Theme.View
0283 
0284     onIsMenuChanged: drawerOpen = false
0285 
0286     Component {
0287         id: menuComponent
0288 
0289         Column {
0290             property alias model: actionsRepeater.model
0291             property T.Action current
0292             property int level: 0
0293 
0294             spacing: 0
0295             Layout.maximumHeight: Layout.minimumHeight
0296 
0297             QQC2.ItemDelegate {
0298                 id: backItem
0299 
0300                 visible: level > 0
0301                 width: parent.width
0302                 icon.name: mirrored ? "go-previous-symbolic-rtl" : "go-previous-symbolic"
0303 
0304                 text: Kirigami.MnemonicData.richTextLabel
0305 
0306                 Kirigami.MnemonicData.enabled: enabled && visible
0307                 Kirigami.MnemonicData.controlType: Kirigami.MnemonicData.MenuItem
0308                 Kirigami.MnemonicData.label: qsTr("Back")
0309 
0310                 onClicked: stackView.pop()
0311 
0312                 Keys.onEnterPressed: stackView.pop()
0313                 Keys.onReturnPressed: stackView.pop()
0314 
0315                 Keys.onDownPressed: nextItemInFocusChain().focus = true
0316                 Keys.onUpPressed: nextItemInFocusChain(false).focus = true
0317             }
0318 
0319             Shortcut {
0320                 sequence: backItem.Kirigami.MnemonicData.sequence
0321                 onActivated: backItem.clicked()
0322             }
0323 
0324             Repeater {
0325                 id: actionsRepeater
0326 
0327                 readonly property bool withSections: {
0328                     for (const action of root.actions) {
0329                         if (action.hasOwnProperty("expandible") && action.expandible) {
0330                             return true;
0331                         }
0332                     }
0333                     return false;
0334                 }
0335 
0336                 model: root.actions
0337 
0338                 delegate: ActionDelegate {
0339                     required property T.Action modelData
0340 
0341                     tAction: modelData
0342                     withSections: actionsRepeater.withSections
0343                 }
0344             }
0345         }
0346     }
0347 
0348     component ActionDelegate : Column {
0349         id: delegate
0350 
0351         required property int index
0352         required property T.Action tAction
0353         required property bool withSections
0354 
0355         // `as` case operator is still buggy
0356         readonly property Kirigami.Action kAction: tAction instanceof Kirigami.Action ? tAction : null
0357 
0358         readonly property bool isExpanded: {
0359             return !root.collapsed
0360                 && kAction
0361                 && kAction.expandible
0362                 && kAction.children.length > 0;
0363         }
0364 
0365         visible: kAction?.visible ?? true
0366 
0367         width: parent.width
0368 
0369         KP.GlobalDrawerActionItem {
0370             Kirigami.Theme.colorSet: !root.modal && !root.collapsed && delegate.withSections
0371                 ? Kirigami.Theme.Window : parent.Kirigami.Theme.colorSet
0372 
0373             visible: !delegate.isExpanded
0374             width: parent.width
0375 
0376             tAction: delegate.tAction
0377 
0378             onCheckedChanged: {
0379                 // move every checked item into view
0380                 if (checked && topContent.height + backItem.height + (delegate.index + 1) * height - mainFlickable.contentY > mainFlickable.height) {
0381                     mainFlickable.contentY += height
0382                 }
0383             }
0384         }
0385 
0386         Item {
0387             id: headerItem
0388 
0389             visible: delegate.isExpanded
0390             height: sectionHeader.implicitHeight
0391             width: parent.width
0392 
0393             Kirigami.ListSectionHeader {
0394                 id: sectionHeader
0395 
0396                 anchors.fill: parent
0397                 Kirigami.Theme.colorSet: root.modal ? Kirigami.Theme.View : Kirigami.Theme.Window
0398 
0399                 contentItem: RowLayout {
0400                     spacing: sectionHeader.spacing
0401 
0402                     Kirigami.Icon {
0403                         property int size: Kirigami.Units.iconSizes.smallMedium
0404                         Layout.minimumHeight: size
0405                         Layout.maximumHeight: size
0406                         Layout.minimumWidth: size
0407                         Layout.maximumWidth: size
0408                         source: delegate.tAction.icon.name || delegate.tAction.icon.source
0409                     }
0410 
0411                     Kirigami.Heading {
0412                         level: 4
0413                         text: delegate.tAction.text
0414                         elide: Text.ElideRight
0415                         Layout.fillWidth: true
0416                     }
0417                 }
0418             }
0419         }
0420 
0421         Repeater {
0422             model: delegate.isExpanded ? (delegate.kAction?.children ?? null) : null
0423 
0424             NestedActionDelegate {
0425                 required property T.Action modelData
0426 
0427                 tAction: modelData
0428                 withSections: delegate.withSections
0429             }
0430         }
0431     }
0432 
0433     component NestedActionDelegate : KP.GlobalDrawerActionItem {
0434         required property bool withSections
0435 
0436         width: parent.width
0437         opacity: !root.collapsed
0438         leftPadding: withSections && !root.collapsed && !root.modal ? padding * 2 : padding * 4
0439     }
0440 
0441     contentItem: Kirigami.HeaderFooterLayout {
0442         id: mainLayout
0443 
0444         anchors {
0445             fill: parent
0446             topMargin: root.collapsed && !showHeaderWhenCollapsed ? -contentItem.y : 0
0447         }
0448 
0449         Behavior on anchors.topMargin {
0450             NumberAnimation {
0451                 duration: Kirigami.Units.longDuration
0452                 easing.type: Easing.InOutQuad
0453             }
0454         }
0455 
0456         header: RowLayout {
0457             visible: root.title.length > 0 || Boolean(root.titleIcon)
0458 
0459             Kirigami.Icon {
0460                 source: root.titleIcon
0461             }
0462 
0463             Kirigami.Heading {
0464                 text: root.title
0465                 elide: Text.ElideRight
0466                 visible: !root.collapsed
0467                 Layout.fillWidth: true
0468             }
0469         }
0470 
0471         contentItem: QQC2.ScrollView {
0472             id: scrollView
0473 
0474             //ensure the attached property exists
0475             Kirigami.Theme.inherit: true
0476 
0477             // HACK: workaround for https://bugreports.qt.io/browse/QTBUG-83890
0478             QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
0479 
0480             implicitWidth: Math.min(Kirigami.Units.gridUnit * 20, root.parent.width * 0.8)
0481 
0482             Flickable {
0483                 id: mainFlickable
0484 
0485                 contentWidth: width
0486                 contentHeight: mainColumn.Layout.minimumHeight
0487 
0488                 clip: root.footer !== null
0489 
0490                 ColumnLayout {
0491                     id: mainColumn
0492                     width: mainFlickable.width
0493                     spacing: 0
0494                     height: Math.max(scrollView.height, Layout.minimumHeight)
0495 
0496                     ColumnLayout {
0497                         id: topContent
0498 
0499                         spacing: 0
0500 
0501                         Layout.alignment: Qt.AlignHCenter
0502                         Layout.leftMargin: root.leftPadding
0503                         Layout.rightMargin: root.rightPadding
0504                         Layout.bottomMargin: Kirigami.Units.smallSpacing
0505                         Layout.topMargin: root.topPadding
0506                         Layout.fillWidth: true
0507                         Layout.fillHeight: true
0508                         Layout.preferredHeight: implicitHeight * opacity
0509                         // NOTE: why this? just Layout.fillWidth: true doesn't seem sufficient
0510                         // as items are added only after this column creation
0511                         Layout.minimumWidth: parent.width - root.leftPadding - root.rightPadding
0512 
0513                         visible: children.length > 0 && childrenRect.height > 0 && opacity > 0
0514                         opacity: !root.collapsed || showTopContentWhenCollapsed
0515 
0516                         Behavior on opacity {
0517                             // not an animator as is binded
0518                             NumberAnimation {
0519                                 duration: Kirigami.Units.longDuration
0520                                 easing.type: Easing.InOutQuad
0521                             }
0522                         }
0523                     }
0524 
0525                     T.StackView {
0526                         id: stackView
0527 
0528                         property KP.ActionsMenu openSubMenu
0529 
0530                         clip: true
0531                         Layout.fillWidth: true
0532                         Layout.minimumHeight: currentItem ? currentItem.implicitHeight : 0
0533                         Layout.maximumHeight: Layout.minimumHeight
0534 
0535                         initialItem: menuComponent
0536 
0537                         // NOTE: it's important those are NumberAnimation and not XAnimators
0538                         // as while the animation is running the drawer may close, and
0539                         // the animator would stop when not drawing see BUG 381576
0540                         popEnter: Transition {
0541                             NumberAnimation { property: "x"; from: (stackView.mirrored ? -1 : 1) * -stackView.width; to: 0; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.OutCubic }
0542                         }
0543 
0544                         popExit: Transition {
0545                             NumberAnimation { property: "x"; from: 0; to: (stackView.mirrored ? -1 : 1) * stackView.width; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.OutCubic }
0546                         }
0547 
0548                         pushEnter: Transition {
0549                             NumberAnimation { property: "x"; from: (stackView.mirrored ? -1 : 1) * stackView.width; to: 0; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.OutCubic }
0550                         }
0551 
0552                         pushExit: Transition {
0553                             NumberAnimation { property: "x"; from: 0; to: (stackView.mirrored ? -1 : 1) * -stackView.width; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.OutCubic }
0554                         }
0555 
0556                         replaceEnter: Transition {
0557                             NumberAnimation { property: "x"; from: (stackView.mirrored ? -1 : 1) * stackView.width; to: 0; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.OutCubic }
0558                         }
0559 
0560                         replaceExit: Transition {
0561                             NumberAnimation { property: "x"; from: 0; to: (stackView.mirrored ? -1 : 1) * -stackView.width; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.OutCubic }
0562                         }
0563                     }
0564 
0565                     Item {
0566                         Layout.fillWidth: true
0567                         Layout.fillHeight: root.actions.length > 0
0568                         Layout.minimumHeight: Kirigami.Units.smallSpacing
0569                     }
0570 
0571                     ColumnLayout {
0572                         id: mainContent
0573                         Layout.alignment: Qt.AlignHCenter
0574                         Layout.leftMargin: root.leftPadding
0575                         Layout.rightMargin: root.rightPadding
0576                         Layout.fillWidth: true
0577                         Layout.fillHeight: true
0578                         // NOTE: why this? just Layout.fillWidth: true doesn't seem sufficient
0579                         // as items are added only after this column creation
0580                         Layout.minimumWidth: parent.width - root.leftPadding - root.rightPadding
0581                         visible: children.length > 0 && (opacity > 0 || mainContentAnimator.running)
0582                         opacity: !root.collapsed || showContentWhenCollapsed
0583                         Behavior on opacity {
0584                             OpacityAnimator {
0585                                 id: mainContentAnimator
0586                                 duration: Kirigami.Units.longDuration
0587                                 easing.type: Easing.InOutQuad
0588                             }
0589                         }
0590                     }
0591 
0592                     Item {
0593                         Layout.minimumWidth: Kirigami.Units.smallSpacing
0594                         Layout.minimumHeight: root.bottomPadding
0595                     }
0596 
0597                     QQC2.ToolButton {
0598                         Layout.fillWidth: true
0599 
0600                         icon.name: {
0601                             if (root.collapsible && root.collapseButtonVisible) {
0602                                 // Check for edge regardless of RTL/locale/mirrored status,
0603                                 // because edge can be set externally.
0604                                 const mirrored = root.edge === Qt.RightEdge;
0605 
0606                                 if (root.collapsed) {
0607                                     return mirrored ? "sidebar-expand-right" : "sidebar-expand-left";
0608                                 } else {
0609                                     return mirrored ? "sidebar-collapse-right" : "sidebar-collapse-left";
0610                                 }
0611                             }
0612                             return "";
0613                         }
0614 
0615                         visible: root.collapsible && root.collapseButtonVisible
0616                         text: root.collapsed ? "" : qsTr("Close Sidebar")
0617 
0618                         onClicked: root.collapsed = !root.collapsed
0619 
0620                         QQC2.ToolTip.visible: root.collapsed && (Kirigami.Settings.tabletMode ? pressed : hovered)
0621                         QQC2.ToolTip.text: qsTr("Open Sidebar")
0622                         QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
0623                     }
0624                 }
0625             }
0626         }
0627     }
0628 }