Kirigami2

templates/OverlaySheet.qml
1 /*
2  * SPDX-FileCopyrightText: 2016-2020 Marco Martin <[email protected]>
3  *
4  * SPDX-License-Identifier: LGPL-2.0-or-later
5  */
6 
7 
8 import QtQuick 2.12
9 import QtQuick.Layouts 1.2
10 import QtQuick.Window 2.2
11 import QtQuick.Controls 2.15
12 import org.kde.kirigami 2.14
13 import QtGraphicalEffects 1.0
14 import QtQuick.Templates 2.0 as T2
15 import "private"
16 import "../private"
17 
18 /**
19  * @brief An overlay sheet that covers the current Page content.
20  *
21  * Its contents can be scrolled up or down, scrolling all the way up or
22  * all the way down, dismisses it.
23  * Use this for big, modal dialogs or information display, that can't be
24  * logically done as a new separate Page, even if potentially
25  * are taller than the screen space.
26  *
27  * @since 2.0
28  * @inherit QtQml.QtObject
29  */
30 QtObject {
31  id: root
32 
33  Theme.colorSet: Theme.View
34  Theme.inherit: false
35 
36  /**
37  * This property holds the visual content item.
38  *
39  * Note: The content item is automatically resized inside the
40  * padding of the control.
41  * Conversely, the Sheet will be sized based on the size hints
42  * of the contentItem, so if you need a custom size sheet,
43  * redefine contentWidth and contentHeight of your contentItem
44  */
45  default property Item contentItem
46 
47  /**
48  * If true the sheet is open showing the contents of the OverlaySheet
49  * component.
50  */
51  property bool sheetOpen
52 
53  /**
54  * This property holds the left padding. Unless explicitly set,
55  * the value is equal to Units::largeSpacing
56  */
57  property int leftPadding: Units.largeSpacing
58 
59  /**
60  * This property holds the top padding. Unless explicitly set,
61  * the value is equal to Units::largeSpacing
62  */
63  property int topPadding: Units.largeSpacing
64 
65  /**
66  * This property holds the right padding. Unless explicitly set,
67  * the value is equal to Units::largeSpacing
68  */
69  property int rightPadding: Units.largeSpacing
70 
71  /**
72  * This property holds the bottom padding. Unless explicitly set,
73  * the value is equal to Units::largeSpacing
74  */
75  property int bottomPadding: Units.largeSpacing
76 
77  /**
78  * This property holds the left inset for the background. The
79  * inset gets applied to both the content *and* the background.
80  * @since 2.12
81  */
82  property real leftInset: 0
83 
84  /**
85  * This property holds the top inset for the background. The
86  * inset gets applied to both the content *and* the background.
87  * @since 2.12
88  */
89  property real topInset: 0
90 
91  /**
92  * This property holds the right inset for the background. The
93  * inset gets applied to both the content *and* the background.
94  * @since 2.12
95  */
96  property real rightInset: 0
97 
98  /**
99  * This property holds the bottom inset for the background. The
100  * inset gets applied to both the content *and* the background.
101  * @since 2.12
102  */
103  property real bottomInset: 0
104 
105  /**
106  * An optional item which will be used as the sheet's header,
107  * always kept on screen.
108  * @since 5.43
109  */
110  property Item header: Heading {
111  level: 1
112  text: root.title
113  elide: Text.ElideRight
114 
115  // use tooltip for long text that is elided
116  ToolTip.visible: truncated && titleHoverHandler.hovered
117  ToolTip.text: root.title
118  HoverHandler {
119  id: titleHoverHandler
120  }
121  }
123  /**
124  * An optional item which will be used as the sheet's footer,
125  * always kept on screen.
126  * @since 5.43
127  */
128  property Item footer
129 
130  /**
131  * This property holds the background item.
132  *
133  * @note If the background item has no explicit size specified,
134  * it automatically follows the control's size. In most cases,
135  * there is no need to specify width or height for a background item.
136  */
137  property Item background
138 
139  /**
140  * Whether to show the close button in the top-right corner.
141  * By default only show on desktop.
142  * @since 5.44
143  */
144  property bool showCloseButton: !Settings.isMobile
145 
146  /**
147  * title: string
148  * This property holds the sheet title.
149  *
150  * Note: If the header property is set, this will have no effect as the heading will be replaced by the header.
151  * @since 5.84
152  */
153  property string title
154 
155  property Item parent
156 
157  /**
158  * Open the overlay sheet.
159  */
160  function open() {
161  openAnimation.running = true;
162  root.sheetOpen = true;
163  contentLayout.initialHeight = contentLayout.height
164  mainItem.visible = true;
165  mainItem.forceActiveFocus();
166  }
167 
168  /**
169  * Close the overlay sheet.
170  */
171  function close() {
172  if (root.sheetOpen) {
173  root.sheetOpen = false;
174  }
175  }
176 
177  onBackgroundChanged: {
178  background.parent = contentLayout.parent;
179  background.anchors.fill = contentLayout;
180  background.anchors.margins = -1
181  background.z = -1;
182  }
183  onContentItemChanged: {
184  if (contentItem instanceof Flickable) {
185  scrollView.flickableItem = contentItem;
186  contentItem.parent = scrollView;
187  contentItem.anchors.fill = scrollView;
188  scrollView.contentItem = contentItem;
189  } else {
190  contentItem.parent = contentItemParent;
191  scrollView.contentItem = flickableContents;
192  contentItem.anchors.left = contentItemParent.left;
193  contentItem.anchors.right = contentItemParent.right;
194  }
195  scrollView.flickableItem.interactive = false;
196  scrollView.flickableItem.flickableDirection = Flickable.VerticalFlick;
197  }
198  onSheetOpenChanged: {
199  if (sheetOpen) {
200  open();
201  } else {
202  closeAnimation.restart()
203  Qt.inputMethod.hide();
204  root.parent.forceActiveFocus();
205  }
206  }
207  onHeaderChanged: headerItem.initHeader()
208  onFooterChanged: {
209  footer.parent = footerParent;
210  footer.anchors.fill = footerParent;
211  }
212 
213  Component.onCompleted: {
214  if (!root.parent && typeof applicationWindow !== "undefined") {
215  root.parent = applicationWindow().overlay
216  }
217  headerItem.initHeader();
218  }
219 
220  readonly property Item rootItem: FocusScope {
221  id: mainItem
222  Theme.colorSet: root.Theme.colorSet
223  Theme.inherit: root.Theme.inherit
224  z: 101
225  //we want to be over any possible OverlayDrawers, including handles
226  parent: {
227  if (root.parent && root.parent.ColumnView.view && (root.parent.ColumnView.view == root.parent || root.parent.ColumnView.view == root.parent.parent)) {
228  return root.parent.ColumnView.view.parent;
229  } else if (root.parent && root.parent.overlay) {
230  root.parent.overlay;
231  } else {
232  return root.parent;
233  }
234  }
235 
236  anchors.fill: parent
237 
238  visible: false
239  clip: true
240 
241  // differentiate between mouse and touch
242  HoverHandler {
243  id: mouseHover
244  acceptedDevices: PointerDevice.Mouse
245  }
246 
247  Keys.onEscapePressed: {
248  if (root.sheetOpen) {
249  root.close();
250  } else {
251  event.accepted = false;
252  }
253  }
254 
255  readonly property int contentItemPreferredWidth: root.contentItem.Layout.preferredWidth > 0 ? root.contentItem.Layout.preferredWidth : root.contentItem.implicitWidth
256 
257  readonly property int absoluteContentItemMaximumWidth: Math.round(width - Units.largeSpacing * 2)
258  readonly property int contentItemMaximumWidth: root.contentItem.Layout.maximumWidth > 0 ? Math.min(root.contentItem.Layout.maximumWidth, absoluteContentItemMaximumWidth) : width > Units.gridUnit * 30 ? width * 0.95 : absoluteContentItemMaximumWidth
259 
260  onHeightChanged: {
261  var focusItem;
262 
263  focusItem = Window.activeFocusItem;
264 
265  if (!focusItem) {
266  return;
267  }
268 
269  //NOTE: there is no function to know if an item is descended from another,
270  //so we have to walk the parent hierarchy by hand
271  var isDescendent = false;
272  var candidate = focusItem.parent;
273  while (candidate) {
274  if (candidate === root) {
275  isDescendent = true;
276  break;
277  }
278  candidate = candidate.parent;
279  }
280  if (!isDescendent) {
281  return;
282  }
283 
284  var cursorY = 0;
285  if (focusItem.cursorPosition !== undefined) {
286  cursorY = focusItem.positionToRectangle(focusItem.cursorPosition).y;
287  }
288 
289 
290  var pos = focusItem.mapToItem(flickableContents, 0, cursorY - Units.gridUnit*3);
291  //focused item already visible? add some margin for the space of the action buttons
292  if (pos.y >= scrollView.flickableItem.contentY && pos.y <= scrollView.flickableItem.contentY + scrollView.flickableItem.height - Units.gridUnit * 8) {
293  return;
294  }
295  scrollView.flickableItem.contentY = pos.y;
296  }
297 
298  ParallelAnimation {
299  id: openAnimation
300  property int margins: Units.gridUnit * 5
301  NumberAnimation {
302  target: outerFlickable
303  properties: "contentY"
304  from: -outerFlickable.height
305  to: outerFlickable.openPosition
306  duration: Units.longDuration
307  easing.type: Easing.OutQuad
308  }
309  OpacityAnimator {
310  target: mainItem
311  from: 0
312  to: 1
313  duration: Units.longDuration
314  easing.type: Easing.InQuad
315  }
316  }
317 
318  NumberAnimation {
319  id: resetAnimation
320  target: outerFlickable
321  properties: "contentY"
322  from: outerFlickable.contentY
323  to: outerFlickable.visibleArea.yPosition < (1 - outerFlickable.visibleArea.heightRatio)/2 || scrollView.flickableItem.contentHeight < outerFlickable.height
324  ? outerFlickable.openPosition
325  : outerFlickable.contentHeight - outerFlickable.height + outerFlickable.topEmptyArea + headerItem.height + footerItem.height
326  duration: Units.longDuration
327  easing.type: Easing.OutQuad
328  }
329 
330  SequentialAnimation {
331  id: closeAnimation
332  ParallelAnimation {
333  NumberAnimation {
334  target: outerFlickable
335  properties: "contentY"
336  from: outerFlickable.contentY + (contentLayout.initialHeight - contentLayout.height)
337  to: outerFlickable.visibleArea.yPosition < (1 - outerFlickable.visibleArea.heightRatio)/2 ? -mainItem.height : outerFlickable.contentHeight
338  duration: Units.longDuration
339  easing.type: Easing.InQuad
340  }
341  OpacityAnimator {
342  target: mainItem
343  from: 1
344  to: 0
345  duration: Units.longDuration
346  easing.type: Easing.InQuad
347  }
348  }
349  ScriptAction {
350  script: {
351  contentLayout.initialHeight = 0
352  scrollView.flickableItem.contentY = -mainItem.height;
353  mainItem.visible = false;
354  }
355  }
356  }
357  Rectangle {
358  anchors.fill: parent
359  color: "black"
360  opacity: 0.3 * Math.min(
361  (Math.min(outerFlickable.contentY + outerFlickable.height, outerFlickable.height) / outerFlickable.height),
362  (2 + (outerFlickable.contentHeight - outerFlickable.contentY - outerFlickable.topMargin - outerFlickable.bottomMargin)/outerFlickable.height))
363  }
364 
365  MouseArea {
366  anchors.fill: parent
367  drag.filterChildren: true
368  hoverEnabled: true
369 
370  onPressed: {
371  let pos = mapToItem(contentLayout, mouse.x, mouse.y);
372  if (contentLayout.contains(pos) && mouseHover.hovered) { // only on mouse event, not touch
373  // disable dragging the sheet with a mouse
374  outerFlickable.interactive = false
375  }
376  }
377  onReleased: {
378  let pos = mapToItem(contentLayout, mouse.x, mouse.y);
379  if (!contentLayout.contains(pos)) {
380  root.close();
381  }
382  // enable dragging of sheet once mouse is not clicked
383  outerFlickable.interactive = true
384  }
385 
386 
387  Item {
388  id: flickableContents
389 
390  readonly property real listHeaderHeight: scrollView.flickableItem ? -scrollView.flickableItem.originY : 0
391 
392  y: (scrollView.contentItem != flickableContents ? -scrollView.flickableItem.contentY - listHeaderHeight - (headerItem.visible ? headerItem.height : 0): 0)
393 
394  width: mainItem.contentItemPreferredWidth <= 0 ? mainItem.width : (mainItem.contentItemMaximumWidth > 0 ? Math.min( mainItem.contentItemMaximumWidth, Math.max( mainItem.width/2, mainItem.contentItemPreferredWidth ) ) : Math.max( mainItem.width / 2, mainItem.contentItemPreferredWidth ) )
395 
396  implicitHeight: scrollView.contentItem == flickableContents ? root.contentItem.height + topPadding + bottomPadding : 0
397  Connections {
398  target: enabled ? flickableContents.Window.activeFocusItem : null
399  enabled: flickableContents.focus && flickableContents.Window.activeFocusItem && flickableContents.Window.activeFocusItem.hasOwnProperty("text")
400  function onTextChanged() {
401  if (Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height > mainItem.Window.height) {
402  scrollView.flickableItem.contentY += (Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height) - mainItem.Window.height
403  }
404  }
405  }
406 
407  Item {
408  id: contentItemParent
409  anchors {
410  fill: parent
411  leftMargin: leftPadding
412  topMargin: topPadding
413  rightMargin: rightPadding
414  bottomMargin: bottomPadding
415  }
416  }
417  }
418 
419  Connections {
420  target: scrollView.flickableItem
421  property real oldContentHeight: 0
422  function onContentHeightChanged() {
423  if (openAnimation.running) {
424  openAnimation.running = false;
425  open();
426  } else {
427  // repositioning is relevant only when the content height is less than the viewport height.
428  // In that case the sheet looks like a dialog and should be centered. there is also a corner case when now is bigger then the viewport but prior to the
429  // resize event it was smaller, also in this case we need repositioning
430  if (scrollView.animatedContentHeight < outerFlickable.height
431  || scrollView.flickableItem.oldContentHeight < outerFlickable.height
432  ) {
433  outerFlickable.adjustPosition();
434  }
435  oldContentHeight = scrollView.animatedContentHeight
436  }
437  }
438  }
439 
440  Flickable {
441  id: outerFlickable
442  anchors.fill: parent
443  contentWidth: width
444  topMargin: height
445  bottomMargin: height
446  // +1: we need the flickable to be always interactive
447  contentHeight: Math.max(height+1, scrollView.animatedContentHeight + topEmptyArea)
448 
449  //readonly property int topEmptyArea: Math.max(height-scrollView.animatedContentHeight, Units.gridUnit * 3)
450  readonly property int topEmptyArea: Math.max(height-scrollView.animatedContentHeight, Units.gridUnit * 3)
451 
452  readonly property real openPosition: Math.max(0, outerFlickable.height - outerFlickable.contentHeight + headerItem.height + footerItem.height) + height/2 - contentLayout.height/2;
453 
454  onOpenPositionChanged: {
455  if (openAnimation.running) {
456  openAnimation.running = false;
457  root.open();
458  } else if (root.sheetOpen) {
459  adjustPosition();
460  }
461  }
462 
463  property int oldContentY: NaN
464  property int oldContentHeight: 0
465  property bool lastMovementWasDown: false
466  property real startDraggingPos
467  property bool layoutMovingGuard: false
468  WheelHandler {
469  target: outerFlickable
470  scrollFlickableTarget: false
471  }
472 
473  function adjustPosition() {
474  if(layoutMovingGuard) return;
475  openAnimation.running = false;
476  resetAnimation.running = false;
477  contentY = openPosition;
478  }
479 
480  // disable dragging the sheet with a mouse on header bar
481  MouseArea {
482  anchors.fill: parent
483  onPressed: {
484  if (mouseHover.hovered) { // only on mouse event, not touch
485  outerFlickable.interactive = false
486  }
487  }
488  onReleased: outerFlickable.interactive = true
489  }
490 
491  onContentYChanged: {
492  if (scrollView.userInteracting) {
493  return;
494  }
495 
496  let startPos = -scrollView.flickableItem.topMargin - flickableContents.listHeaderHeight;
497  let pos = contentY - topEmptyArea - flickableContents.listHeaderHeight;
498  let endPos = scrollView.animatedContentHeight - scrollView.flickableItem.height + scrollView.flickableItem.bottomMargin - flickableContents.listHeaderHeight;
499 
500  layoutMovingGuard = true;
501  if (endPos - pos > 0) {
502  contentLayout.y = Math.round(Math.max(root.topInset, scrollView.flickableItem.topMargin - pos - flickableContents.listHeaderHeight));
503  } else if (scrollView.flickableItem.topMargin - pos < 0) {
504  contentLayout.y = Math.round(endPos - pos + root.topInset);
505  }
506  layoutMovingGuard = false;
507 
508  scrollView.flickableItem.contentY = Math.max(
509  startPos, Math.min(pos, endPos));
510 
511  lastMovementWasDown = contentY < oldContentY;
512  oldContentY = contentY;
513  }
514 
515  onFlickEnded: {
516  if (openAnimation.running || closeAnimation.running) {
517  return;
518  }
519  if (scrollView.flickableItem.atYBeginning ||scrollView.flickableItem.atYEnd) {
520  resetAnimation.restart();
521  }
522  }
523 
524  onDraggingChanged: {
525  if (dragging) {
526  startDraggingPos = contentY;
527  return;
528  }
529 
530  let shouldClose = false;
531 
532  // close
533  if (scrollView.flickableItem.atYBeginning) {
534  if (startDraggingPos - contentY > Units.gridUnit * 4 &&
535  contentY < -Units.gridUnit * 4 &&
536  lastMovementWasDown) {
537  shouldClose = true;
538  }
539  }
540 
541  if (scrollView.flickableItem.atYEnd) {
542  if (contentY - startDraggingPos > Units.gridUnit * 4 &&
543  contentY > contentHeight - height + Units.gridUnit * 4 &&
544  !lastMovementWasDown) {
545  shouldClose = true;
546  }
547  }
548 
549  if (shouldClose) {
550  root.sheetOpen = false
551  } else if (scrollView.flickableItem.atYBeginning || scrollView.flickableItem.atYEnd) {
552  resetAnimation.restart();
553  }
554  }
555 
556  onHeightChanged: {
557  adjustPosition();
558  }
559 
560  onContentHeightChanged: {
561  // repositioning is relevant only when the content height is less than the viewport height.
562  // In that case the sheet looks like a dialog and should be centered. there is also a corner case when now is bigger then the viewport but prior to the
563  // resize event it was smaller, also in this case we need repositioning
564  if (contentHeight < height || oldContentHeight < height) {
565  adjustPosition();
566  }
567  oldContentHeight = contentHeight;
568  }
569 
570  ColumnLayout {
571  id: contentLayout
572  spacing: 0
573  // Its events should be filtered but not scrolled
574  parent: outerFlickable
575  anchors.horizontalCenter: parent.horizontalCenter
576  width: mainItem.contentItemPreferredWidth <= 0 ? mainItem.width : (mainItem.contentItemMaximumWidth > 0 ? Math.min( mainItem.contentItemMaximumWidth, Math.max( mainItem.width/2, mainItem.contentItemPreferredWidth ) ) : Math.max( mainItem.width / 2, mainItem.contentItemPreferredWidth ) ) - root.leftInset - root.rightInset
577  height: Math.min(implicitHeight, parent.height) - root.topInset - root.bottomInset
578  property real initialHeight
579 
580  Behavior on height {
581  NumberAnimation {
582  duration: Units.shortDuration
583  easing.type: Easing.InOutCubic
584  }
585  }
586 
587  // Even though we're not actually using any shadows here,
588  // we're using a ShadowedRectangle instead of a regular
589  // rectangle because it allows fine-grained control over which
590  // corners to round, which we need here
591  ShadowedRectangle {
592  id: headerItem
593  Layout.fillWidth: true
594  Layout.alignment: Qt.AlignTop
595  //Layout.margins: 1
596  visible: root.header || root.showCloseButton
597  implicitHeight: Math.max(headerParent.implicitHeight, closeIcon.height) + Units.smallSpacing * 2
598  z: 2
599  corners.topLeftRadius: Units.smallSpacing
600  corners.topRightRadius: Units.smallSpacing
601  Theme.colorSet: Theme.Header
602  Theme.inherit: false
603  color: Theme.backgroundColor
604 
605  function initHeader() {
606  if (header) {
607  header.parent = headerParent;
608  header.anchors.fill = headerParent;
609 
610  //TODO: special case for actual ListViews
611  }
612  }
613 
614  Item {
615  id: headerParent
616  implicitHeight: header ? header.implicitHeight : 0
617  anchors {
618  fill: parent
619  leftMargin: Units.largeSpacing
620  margins: Units.smallSpacing
621  rightMargin: (root.showCloseButton ? closeIcon.width : 0) + Units.smallSpacing
622  }
623  }
624  Icon {
625  // We want to position the close button in the top-right
626  // corner if the header is very tall, but we want to
627  // vertically center it in a short header
628  readonly property bool tallHeader: headerItem.height > (Units.iconSizes.smallMedium + Units.largeSpacing + Units.largeSpacing)
629  id: closeIcon
630  anchors {
631  right: parent.right
632  rightMargin: Units.largeSpacing
633  top: tallHeader ? parent.top : undefined
634  topMargin: tallHeader ? Units.largeSpacing : undefined
635  verticalCenter: tallHeader ? undefined : headerItem.verticalCenter
636  margins: Units.smallSpacing
637  }
638  z: 3
639  visible: root.showCloseButton
640  width: Units.iconSizes.smallMedium
641  height: width
642  source: closeMouseArea.containsMouse ? "window-close" : "window-close-symbolic"
643  active: closeMouseArea.containsMouse
644  MouseArea {
645  id: closeMouseArea
646  hoverEnabled: true
647  anchors.fill: parent
648  onClicked: root.close();
649  }
650  }
651  Separator {
652  anchors {
653  right: parent.right
654  left: parent.left
655  top: parent.bottom
656  }
657  }
658  }
659 
660  ScrollView {
661  id: scrollView
662 
663  //Don't do the automatic interactive enable/disable
664  canFlickWithMouse: true
665  property real animatedContentHeight: contentItem ? flickableItem.contentHeight : 0
666  property bool userInteracting: false
667  Layout.fillWidth: true
668  Layout.fillHeight: true
669 
670  focus: false
671 
672  implicitHeight: flickableItem.contentHeight
673  Layout.maximumHeight: flickableItem.contentHeight
674 
675  Layout.alignment: Qt.AlignTop
676 
677  Behavior on animatedContentHeight {
678  NumberAnimation {
679  duration: Units.shortDuration
680  easing.type: Easing.InOutCubic
681  }
682  }
683  }
684 
685  Item { Layout.fillHeight: true }
686 
687  Connections {
688  target: scrollView.flickableItem
689  property real oldContentY: 0
690  function onContentYChanged() {
691  if (outerFlickable.moving) {
692  oldContentY = scrollView.flickableItem.contentY;
693  return;
694  }
695  scrollView.userInteracting = true;
696 
697  let diff = scrollView.flickableItem.contentY - oldContentY
698 
699  outerFlickable.contentY = outerFlickable.contentY + diff;
700 
701  if (diff > 0) {
702  contentLayout.y = Math.max(root.topInset, contentLayout.y - diff);
703  } else if (scrollView.flickableItem.contentY < outerFlickable.topEmptyArea + headerItem.height) {
704  contentLayout.y = Math.min(outerFlickable.topEmptyArea, contentLayout.y + (contentLayout.y - diff)) + root.topInset;
705  }
706 
707  oldContentY = scrollView.flickableItem.contentY;
708  scrollView.userInteracting = false;
709  }
710  }
711  Item {
712  visible: footerItem.visible
713  implicitHeight: footerItem.height
714  }
715  }
716 
717  // footer item is outside the layout as it should never scroll away
718 
719  // Even though we're not actually using any shadows here,
720  // we're using a ShadowedRectangle instead of a regular
721  // rectangle because it allows fine-grained control over which
722  // corners to round, which we need here
723  ShadowedRectangle {
724  id: footerItem
725  width: contentLayout.width - 2
726  corners.bottomLeftRadius: Units.smallSpacing
727  corners.bottomRightRadius: Units.smallSpacing
728  parent: outerFlickable
729  x: contentLayout.x + 1
730  y: Math.min(parent.height, contentLayout.y + contentLayout.height -1) - height
731  visible: root.footer
732  implicitHeight: footerParent.implicitHeight + Units.smallSpacing * 2 + extraMargin
733  Theme.colorSet: Theme.Window
734  Theme.inherit: false
735  color: Theme.backgroundColor
736 
737  //Show an extra margin when:
738  //* the application is in mobile mode (no toolbarapplicationheader)
739  //* the bottom screen controls are visible
740  //* the sheet is displayed *under* the controls
741  property int extraMargin: (!root.parent ||
742  typeof applicationWindow === "undefined" ||
743  (root.parent === applicationWindow().overlay) ||
744  !applicationWindow().controlsVisible ||
745  (applicationWindow().pageStack && applicationWindow().pageStack.globalToolBar && applicationWindow().pageStack.globalToolBar.actualStyle === ApplicationHeaderStyle.ToolBar) ||
746  (applicationWindow().header && applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") === 0))
747  ? 0 : Units.gridUnit * 3
748 
749  z: 2
750  Item {
751  id: footerParent
752  implicitHeight: footer ? footer.implicitHeight : 0
753  anchors {
754  top: parent.top
755  left: parent.left
756  right: parent.right
757  margins: Units.smallSpacing
758  }
759  }
760 
761  Separator {
762  anchors {
763  right: parent.right
764  left: parent.left
765  bottom: parent.top
766  leftMargin: -1
767  rightMargin: -1
768  }
769  }
770  }
771  }
772  }
773  }
774 }
A heading label used for subsections of texts.
Definition: Heading.qml:37
int shortDuration
int gridUnit
int largeSpacing
Class for rendering an icon in UI.
Definition: icon.h:26
QTextStream & left(QTextStream &s)
bool isMobile
This property holds whether the application is running on a small mobile device such as a mobile phon...
Definition: settings.h:33
This class contains global kirigami settings about the current device setup It is exposed to QML as t...
Definition: settings.h:16
int smallSpacing
QTextStream & right(QTextStream &s)
This class intercepts the mouse wheel events of its target, and gives them to the user code as a sign...
Definition: wheelhandler.h:164
QQmlPropertyMap iconSizes
A visual separator.
Definition: Separator.qml:18
int longDuration
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Oct 24 2021 22:39:11 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.