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