Kirigami2

GlobalDrawer.qml
1 /*
2  * SPDX-FileCopyrightText: 2015 Marco Martin <[email protected]>
3  *
4  * SPDX-License-Identifier: LGPL-2.0-or-later
5  */
6 
7 import QtQuick 2.12
8 import QtQuick.Templates 2.3 as T2
9 import QtQuick.Controls 2.2 as QQC2
10 import QtQuick.Layouts 1.2
11 import org.kde.kirigami 2.13 as Kirigami
12 import "private" as P
13 
14 /**
15  * A specialized form of the Drawer intended for showing an application's
16  * always-available global actions. Think of it like a mobile version of
17  * a desktop application's menubar.
18  *
19  * Example usage:
20  * @code
21  * import org.kde.kirigami 2.4 as Kirigami
22  *
23  * Kirigami.ApplicationWindow {
24  * [...]
25  * globalDrawer: Kirigami.GlobalDrawer {
26  * actions: [
27  * Kirigami.Action {
28  * text: "View"
29  * icon.name: "view-list-icons"
30  * Kirigami.Action {
31  * text: "action 1"
32  * }
33  * Kirigami.Action {
34  * text: "action 2"
35  * }
36  * Kirigami.Action {
37  * text: "action 3"
38  * }
39  * },
40  * Kirigami.Action {
41  * text: "Sync"
42  * icon.name: "folder-sync"
43  * }
44  * ]
45  * }
46  * [...]
47  * }
48  * @endcode
49  *
50  */
52  id: root
53  edge: Qt.application.layoutDirection === Qt.RightToLeft ? Qt.RightEdge : Qt.LeftEdge
54  handleClosedIcon.source: null
55  handleOpenIcon.source: null
56  handleVisible: (modal || !drawerOpen) && (typeof(applicationWindow)===typeof(Function) && applicationWindow() ? applicationWindow().controlsVisible : true) && (!isMenu || Kirigami.Settings.isMobile)
57 
58  enabled: !isMenu || Kirigami.Settings.isMobile
59 
60 //BEGIN properties
61  /**
62  * @brief This property holds the title displayed at the top of the drawer.
63  * @see org::kde::kirigami::private::BannerImage::title
64  * @property string title
65  */
66  property alias title: bannerImage.title
67 
68  /**
69  * @brief This property holds an icon to be displayed alongside the title.
70  * @see org::kde::kirigami::private::BannerImage::titleIcon
71  * @see org::kde::kirigami::Icon::source
72  * @property var titleIcon
73  */
74  property alias titleIcon: bannerImage.titleIcon
75 
76  /**
77  * @brief This property holds the banner image source.
78  * @see org::kde::kirigami::ShadowedImage::source
79  * @property url bannerImageSource
80  */
81  property alias bannerImageSource: bannerImage.source
82 
83  /**
84  * @brief This property holds the actions displayed in the drawer.
85  *
86  * The list of actions can be nested having a tree structure.
87  * A tree depth bigger than 2 is discouraged.
88  *
89  * Example usage:
90  * @code
91  * import org.kde.kirigami 2.4 as Kirigami
92  *
93  * Kirigami.ApplicationWindow {
94  * [...]
95  * globalDrawer: Kirigami.GlobalDrawer {
96  * actions: [
97  * Kirigami.Action {
98  * text: "View"
99  * icon.name: "view-list-icons"
100  * Kirigami.Action {
101  * text: "action 1"
102  * }
103  * Kirigami.Action {
104  * text: "action 2"
105  * }
106  * Kirigami.Action {
107  * text: "action 3"
108  * }
109  * },
110  * Kirigami.Action {
111  * text: "Sync"
112  * icon.name: "folder-sync"
113  * }
114  * ]
115  * }
116  * [...]
117  * }
118  * @endcode
119  * @property list<Action> actions
120  */
121  property list<QtObject> actions
122 
123  /**
124  * @brief This property holds an item that will always be displayed at the top of the drawer.
125  *
126  * If the drawer contents can be scrolled, this item will stay still and won't scroll.
127  *
128  * @note This property is mainly intended for toolbars.
129  * @since 2.12
130  */
131  property Item header
132 
133  /**
134  * @brief This property sets drawers banner visibility.
135  *
136  * If true, the banner area (which can contain an image,
137  * an icon, and a title) will be visible.
138  *
139  * default: `the banner will be visible only on mobile platforms`
140  *
141  * @since 2.12
142  */
143  property bool bannerVisible: Kirigami.Settings.isMobile
144 
145  /**
146  * @brief This property holds items that are displayed above the actions.
147  *
148  * Example usage:
149  * @code
150  * import org.kde.kirigami 2.4 as Kirigami
151  *
152  * Kirigami.ApplicationWindow {
153  * [...]
154  * globalDrawer: Kirigami.GlobalDrawer {
155  * actions: [...]
156  * topContent: [Button {
157  * text: "Button"
158  * onClicked: //do stuff
159  * }]
160  * }
161  * [...]
162  * }
163  * @endcode
164  * @property list<QtObject> topContent
165  */
166  property alias topContent: topContent.data
167 
168  /**
169  * @brief This property holds items that are displayed under the actions.
170  *
171  * Example usage:
172  * @code
173  * import org.kde.kirigami 2.4 as Kirigami
174  *
175  * Kirigami.ApplicationWindow {
176  * [...]
177  * globalDrawer: Kirigami.GlobalDrawer {
178  * actions: [...]
179  * Button {
180  * text: "Button"
181  * onClicked: //do stuff
182  * }
183  * }
184  * [...]
185  * }
186  * @endcode
187  * @note This is a `default` property.
188  * @property list<QtObject> content
189  */
190  default property alias content: mainContent.data
191 
192  /**
193  * @brief This property sets whether content items at the top should be shown.
194  * when the drawer is collapsed as a sidebar.
195  *
196  * If you want to keep some items visible and some invisible, set this to
197  * false and control the visibility/opacity of individual items,
198  * binded to the collapsed property
199  *
200  * default: ``false``
201  *
202  * @since 2.5
203  */
204  property bool showTopContentWhenCollapsed: false
205 
206  /**
207  * @brief This property sets whether content items at the bottom should be shown.
208  * when the drawer is collapsed as a sidebar.
209  *
210  * If you want to keep some items visible and some invisible, set this to
211  * false and control the visibility/opacity of individual items,
212  * binded to the collapsed property
213  *
214  * default: ``false``
215  *
216  * @see content
217  * @since 2.5
218  */
219  property bool showContentWhenCollapsed: false
220 
221  // TODO
222  property bool showHeaderWhenCollapsed: false
223 
224  /**
225  * @brief This property sets whether activating a leaf action resets the
226  * menu to show leaf's parent actions.
227  *
228  * A leaf action is an action without any child actions.
229  *
230  * default: ``true``
231  */
232  property bool resetMenuOnTriggered: true
233 
234  /**
235  * @brief This property points to the action acting as a submenu
236  */
237  readonly property Action currentSubMenu: stackView.currentItem ? stackView.currentItem.current: null
238 
239  /**
240  * @brief This property sets whether the drawer becomes a menu on the desktop.
241  *
242  * default: ``false``
243  *
244  * @since 2.11
245  */
246  property bool isMenu: false
247 
248  /**
249  * @brief This property sets the visibility of the collapse button
250  * when the drawer collapsible.
251  *
252  * default: ``true``
253  *
254  * @since 2.12
255  */
256  property bool collapseButtonVisible: true
257 //END properties
258 
259  /**
260  * @brief This signal notifies that the banner has been clicked.
261  */
262  signal bannerClicked()
263 
264  /**
265  * @brief This function reverts the menu back to its initial state
266  */
267  function resetMenu() {
268  stackView.pop(stackView.get(0, T2.StackView.DontLoad));
269  if (root.modal) {
270  root.drawerOpen = false;
271  }
272  }
273 
274  // rightPadding: !Kirigami.Settings.isMobile && mainFlickable.contentHeight > mainFlickable.height ? Kirigami.Units.gridUnit : Kirigami.Units.smallSpacing
275 
276  Kirigami.Theme.colorSet: modal ? Kirigami.Theme.Window : Kirigami.Theme.View
277 
278  onHeaderChanged: {
279  if (header) {
280  header.parent = headerContainer
281  header.Layout.fillWidth = true;
282  if (header.z === undefined) {
283  header.z = 1;
284  }
285  if (header instanceof T2.ToolBar) {
286  header.position = T2.ToolBar.Header
287  } else if (header instanceof T2.TabBar) {
288  header.position = T2.TabBar.Header
289  } else if (header instanceof T2.DialogButtonBox) {
290  header.position = T2.DialogButtonBox.Header
291  }
292  }
293  }
294 
295  contentItem: QQC2.ScrollView {
296  id: scrollView
297  //ensure the attached property exists
298  Kirigami.Theme.inherit: true
299  anchors.fill: parent
300  implicitWidth: Math.min (Kirigami.Units.gridUnit * 20, root.parent.width * 0.8)
301  QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
302  QQC2.ScrollBar.vertical.anchors {
303  top: scrollView.top
304  bottom: scrollView.bottom
305  topMargin: headerParent.height + headerParent.y
306  }
307 
308  Flickable {
309  id: mainFlickable
310  contentWidth: width
311  contentHeight: mainColumn.Layout.minimumHeight
312  topMargin: headerParent.height
313 
314  ColumnLayout {
315  id: headerParent
316  parent: mainFlickable
317  anchors {
318  left: parent.left
319  right: parent.right
320  rightMargin: Math.min(0, -scrollView.width + mainFlickable.width)
321  }
322  spacing: 0
323  y: bannerImage.visible ? Math.max(headerContainer.height, -mainFlickable.contentY) - height : 0
324 
325  Layout.fillWidth: true
326  // visible: !bannerImage.empty || root.collapsible
327 
328  P.BannerImage {
329  id: bannerImage
330 
331 
332  visible: !bannerImage.empty && opacity > 0 && root.bannerVisible
333  opacity: !root.collapsed
334  fillMode: Image.PreserveAspectCrop
335 
336  Behavior on opacity {
337  OpacityAnimator {
338  duration: Kirigami.Units.longDuration
339  easing.type: Easing.InOutQuad
340  }
341  }
342  // leftPadding: root.collapsible ? collapseButton.width + Kirigami.Units.smallSpacing*2 : topPadding
343  MouseArea {
344  anchors.fill: parent
345  onClicked: mouse => root.bannerClicked()
346  }
347  P.EdgeShadow {
348  edge: Qt.BottomEdge
349  visible: bannerImageSource != ""
350  anchors {
351  left: parent.left
352  right: parent.right
353  bottom: parent.top
354  }
355  }
356  }
357  RowLayout {
358  id: headerContainer
359  Kirigami.Theme.inherit: false
360  Kirigami.Theme.colorSet: Kirigami.Theme.Window
361 
362  Layout.fillWidth: true
363  visible: contentItem && opacity > 0
364  // Workaround for https://bugreports.qt.io/browse/QTBUG-90034
365  Layout.preferredHeight: implicitHeight <= 0 || opacity === 1 ? -1 : implicitHeight * opacity
366  opacity: !root.collapsed || showHeaderWhenCollapsed
367  Behavior on opacity {
368  // not an animator as is binded
369  NumberAnimation {
370  duration: Kirigami.Units.longDuration
371  easing.type: Easing.InOutQuad
372  }
373  }
374  }
375  }
376 
377 
378  ColumnLayout {
379  id: mainColumn
380  width: mainFlickable.width
381  spacing: 0
382  height: Math.max(root.height - headerParent.height, Layout.minimumHeight)
383 
384  ColumnLayout {
385  id: topContent
386  spacing: 0
387  Layout.alignment: Qt.AlignHCenter
388  Layout.leftMargin: root.leftPadding
389  Layout.rightMargin: root.rightPadding
390  Layout.bottomMargin: Kirigami.Units.smallSpacing
391  Layout.topMargin: root.topPadding
392  Layout.fillWidth: true
393  Layout.fillHeight: true
394  Layout.preferredHeight: implicitHeight * opacity
395  // NOTE: why this? just Layout.fillWidth: true doesn't seem sufficient
396  // as items are added only after this column creation
397  Layout.minimumWidth: parent.width - root.leftPadding - root.rightPadding
398  visible: children.length > 0 && childrenRect.height > 0 && opacity > 0
399  opacity: !root.collapsed || showTopContentWhenCollapsed
400  Behavior on opacity {
401  // not an animator as is binded
402  NumberAnimation {
403  duration: Kirigami.Units.longDuration
404  easing.type: Easing.InOutQuad
405  }
406  }
407  }
408 
409  T2.StackView {
410  id: stackView
411  clip: true
412  Layout.fillWidth: true
413  Layout.minimumHeight: currentItem ? currentItem.implicitHeight : 0
414  Layout.maximumHeight: Layout.minimumHeight
415  property P.ActionsMenu openSubMenu
416  initialItem: menuComponent
417  // NOTE: it's important those are NumberAnimation and not XAnimators
418  // as while the animation is running the drawer may close, and
419  // the animator would stop when not drawing see BUG 381576
420  popEnter: Transition {
421  NumberAnimation { property: "x"; from: (stackView.mirrored ? -1 : 1) * -stackView.width; to: 0; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.OutCubic }
422  }
423 
424  popExit: Transition {
425  NumberAnimation { property: "x"; from: 0; to: (stackView.mirrored ? -1 : 1) * stackView.width; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.OutCubic }
426  }
427 
428  pushEnter: Transition {
429  NumberAnimation { property: "x"; from: (stackView.mirrored ? -1 : 1) * stackView.width; to: 0; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.OutCubic }
430  }
431 
432  pushExit: Transition {
433  NumberAnimation { property: "x"; from: 0; to: (stackView.mirrored ? -1 : 1) * -stackView.width; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.OutCubic }
434  }
435 
436  replaceEnter: Transition {
437  NumberAnimation { property: "x"; from: (stackView.mirrored ? -1 : 1) * stackView.width; to: 0; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.OutCubic }
438  }
439 
440  replaceExit: Transition {
441  NumberAnimation { property: "x"; from: 0; to: (stackView.mirrored ? -1 : 1) * -stackView.width; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.OutCubic }
442  }
443  }
444  Item {
445  Layout.fillWidth: true
446  Layout.fillHeight: root.actions.length>0
447  Layout.minimumHeight: Kirigami.Units.smallSpacing
448  }
449 
450  ColumnLayout {
451  id: mainContent
452  Layout.alignment: Qt.AlignHCenter
453  Layout.leftMargin: root.leftPadding
454  Layout.rightMargin: root.rightPadding
455  Layout.fillWidth: true
456  Layout.fillHeight: true
457  // NOTE: why this? just Layout.fillWidth: true doesn't seem sufficient
458  // as items are added only after this column creation
459  Layout.minimumWidth: parent.width - root.leftPadding - root.rightPadding
460  visible: children.length > 0 && (opacity > 0 || mainContentAnimator.running)
461  opacity: !root.collapsed || showContentWhenCollapsed
462  Behavior on opacity {
463  OpacityAnimator {
464  id: mainContentAnimator
465  duration: Kirigami.Units.longDuration
466  easing.type: Easing.InOutQuad
467  }
468  }
469  }
470  Item {
471  Layout.minimumWidth: Kirigami.Units.smallSpacing
472  Layout.minimumHeight: root.bottomPadding
473  }
474 
475  Component {
476  id: menuComponent
477 
478  Column {
479  spacing: 0
480  property alias model: actionsRepeater.model
481  property Action current
482 
483  property int level: 0
484  Layout.maximumHeight: Layout.minimumHeight
485 
486  BasicListItem {
487  id: backItem
488  visible: level > 0
489  icon: (LayoutMirroring.enabled ? "go-previous-symbolic-rtl" : "go-previous-symbolic")
490 
491  label: Kirigami.MnemonicData.richTextLabel
492  Kirigami.MnemonicData.enabled: backItem.enabled && backItem.visible
493  Kirigami.MnemonicData.controlType: Kirigami.MnemonicData.MenuItem
494  Kirigami.MnemonicData.label: qsTr("Back")
495 
496  separatorVisible: false
497  onClicked: stackView.pop()
498 
499  Keys.onEnterPressed: stackView.pop()
500  Keys.onReturnPressed: stackView.pop()
501 
502  Keys.onDownPressed: nextItemInFocusChain().focus = true
503  Keys.onUpPressed: nextItemInFocusChain(false).focus = true
504  }
505  Shortcut {
506  sequence: backItem.Kirigami.MnemonicData.sequence
507  onActivated: backItem.clicked()
508  }
509 
510  Repeater {
511  id: actionsRepeater
512 
513  readonly property bool withSections: {
514  for (let i = 0; i < root.actions.length; i++) {
515  const action = root.actions[i];
516  if (!(action.hasOwnProperty("expandible") && action.expandible)) {
517  return false;
518  }
519  }
520  return true;
521  }
522 
523  model: root.actions
524  delegate: Column {
525  width: parent.width
526  P.GlobalDrawerActionItem {
527  id: drawerItem
528  visible: (modelData.hasOwnProperty("visible") && modelData.visible) && (root.collapsed || !(modelData.hasOwnProperty("expandible") && modelData.expandible))
529  width: parent.width
530  onCheckedChanged: {
531  // move every checked item into view
532  if (checked && topContent.height + backItem.height + (model.index + 1) * height - mainFlickable.contentY > mainFlickable.height) {
533  mainFlickable.contentY += height
534  }
535  }
536  Kirigami.Theme.colorSet: drawerItem.visible && !root.modal && !root.collapsed && actionsRepeater.withSections ? Kirigami.Theme.Window : parent.Kirigami.Theme.colorSet
537  backgroundColor: Kirigami.Theme.backgroundColor
538  }
539  Item {
540  id: headerItem
541  visible: !root.collapsed && (modelData.hasOwnProperty("expandible") && modelData.expandible && !!modelData.children && modelData.children.length > 0)
542  height: sectionHeader.implicitHeight
543  width: parent.width
544  Kirigami.ListSectionHeader {
545  id: sectionHeader
546  anchors.fill: parent
547  Kirigami.Theme.colorSet: root.modal ? Kirigami.Theme.View : Kirigami.Theme.Window
548  contentItem: RowLayout {
549  Kirigami.Icon {
550  property int size: Kirigami.Units.iconSizes.smallMedium
551  Layout.minimumHeight: size
552  Layout.maximumHeight: size
553  Layout.minimumWidth: size
554  Layout.maximumWidth: size
555  source: modelData.icon.name || modelData.icon.source
556  }
557  Heading {
558  id: header
559  level: 4
560  text: modelData.text
561  }
562  Item {
563  Layout.fillWidth: true
564  }
565  }
566  }
567  }
568  Repeater {
569  id: __repeater
570  model: headerItem.visible ? modelData.children : null
571  delegate: P.GlobalDrawerActionItem {
572  width: parent.width
573  opacity: !root.collapsed
574  leftPadding: actionsRepeater.withSections && !root.collapsed && !root.modal ? padding * 2 : padding * 4
575  }
576  }
577  }
578  }
579  }
580  }
581 
582  QQC2.ToolButton {
583  icon.name: root.collapsed ? "view-right-new" : "view-right-close"
584  Layout.fillWidth: root.collapsed
585  onClicked: root.collapsed = !root.collapsed
586  visible: root.collapsible && root.collapseButtonVisible
587  text: root.collapsed ? "" : qsTr("Close Sidebar")
588 
589  QQC2.ToolTip.visible: root.collapsed && hovered
590  QQC2.ToolTip.text: qsTr("Open Sidebar")
591  QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
592  }
593  }
594  }
595  }
596 }
597 
QTextStream & right(QTextStream &stream)
A BasicListItem provides a simple list item design that can handle the most common list item usecases...
QTextStream & left(QTextStream &stream)
QStringView level(QStringView ifopt)
QTextStream & left(QTextStream &s)
A heading label used for subsections of texts.
Definition: Heading.qml:36
Overlay Drawers are used to expose additional UI elements needed for small secondary tasks for which ...
QTextStream & right(QTextStream &s)
An item that represents an abstract Action.
Definition: Action.qml:14
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Jan 29 2023 04:11:03 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.