9 import QtQuick.Layouts 1.2
10 import QtQuick.Window 2.2
11 import QtQuick.Controls 2.15 as QQC2
12 import org.kde.kirigami 2.14 as Kirigami
13 import QtQuick.Templates 2.0 as T2
15 import "../private" as PP
32 Kirigami.Theme.colorSet: Kirigami.Theme.View
33 Kirigami.Theme.inherit:
false
44 default property Item contentItem
49 property bool sheetOpen
56 property int leftPadding: Kirigami.Units.largeSpacing
63 property int topPadding: Kirigami.Units.largeSpacing
70 property int rightPadding: Kirigami.Units.largeSpacing
77 property int bottomPadding: Kirigami.Units.largeSpacing
88 property real leftInset: 0
99 property real topInset: 0
110 property real rightInset: 0
121 property real bottomInset: 0
128 property Item header: Kirigami.Heading {
131 elide: Text.ElideRight
134 QQC2.ToolTip.visible: truncated && titleHoverHandler.hovered
135 QQC2.ToolTip.text: root.title
137 id: titleHoverHandler
155 property Item background
164 property bool showCloseButton: !Kirigami.Settings.isMobile
171 property string title
179 openAnimation.running =
true;
180 root.sheetOpen =
true;
181 contentLayout.initialHeight = contentLayout.height
182 mainItem.visible =
true;
183 mainItem.forceActiveFocus();
190 if (root.sheetOpen) {
191 root.sheetOpen =
false;
195 onBackgroundChanged: {
196 background.parent = contentLayout.parent;
197 background.anchors.fill = contentLayout;
198 background.anchors.margins = -1
201 onContentItemChanged: {
202 if (contentItem instanceof Flickable) {
203 scrollView.flickableItem = contentItem;
204 contentItem.parent = scrollView;
205 scrollView.contentItem = contentItem;
206 scrollView.viewContent = contentItem.contentItem;
208 contentItem.parent = contentItemParent;
209 flickableContents.parent = scrollView.flickableItem.contentItem;
210 flickableContents.anchors.top = scrollView.flickableItem.contentItem.top;
211 flickableContents.anchors.left = scrollView.flickableItem.contentItem.left;
212 flickableContents.anchors.right = scrollView.flickableItem.contentItem.right;
213 scrollView.viewContent = flickableContents;
214 contentItem.anchors.left = contentItemParent.left;
215 contentItem.anchors.right = contentItemParent.right;
217 scrollView.flickableItem.interactive =
false;
218 scrollView.flickableItem.flickableDirection = Flickable.VerticalFlick;
220 onSheetOpenChanged: {
224 closeAnimation.restart()
225 Qt.inputMethod.hide();
226 root.parent.forceActiveFocus();
229 onHeaderChanged: headerItem.initHeader()
231 footer.parent = footerParent;
232 footer.anchors.fill = footerParent;
239 if (!root.parent && typeof applicationWindow !==
"undefined") {
240 root.parent = applicationWindow().overlay
242 headerItem.initHeader();
246 readonly
property Item rootItem: FocusScope {
248 Kirigami.Theme.colorSet: root.Kirigami.Theme.colorSet
249 Kirigami.Theme.inherit: root.Kirigami.Theme.inherit
253 if (root.parent && root.parent.Kirigami.ColumnView.view && (root.parent.Kirigami.ColumnView.view === root.parent || root.parent.Kirigami.ColumnView.view === root.parent.parent)) {
254 return root.parent.Kirigami.ColumnView.view.parent;
255 }
else if (root.parent && root.parent.overlay) {
256 return root.parent.overlay;
270 acceptedDevices: PointerDevice.Mouse
273 Keys.onEscapePressed:
event => {
274 if (root.sheetOpen) {
277 event.accepted =
false;
281 readonly
property int contentItemPreferredWidth: root.contentItem.Layout.preferredWidth > 0 ? root.contentItem.Layout.preferredWidth : root.contentItem.implicitWidth
283 readonly
property int absoluteContentItemMaximumWidth: width <= 0 ? contentItemPreferredWidth : Math.round(width - Kirigami.Units.largeSpacing * 2)
284 readonly
property int contentItemMaximumWidth: root.contentItem.Layout.maximumWidth > 0 ? Math.min(root.contentItem.Layout.maximumWidth, absoluteContentItemMaximumWidth) : width > Kirigami.Units.gridUnit * 30 ? width * 0.95 : absoluteContentItemMaximumWidth
287 const focusItem =
Window.activeFocusItem;
295 let isDescendent =
false;
296 let candidate = focusItem.
parent;
298 if (candidate === root) {
302 candidate = candidate.
parent;
309 if (focusItem.cursorPosition !== undefined) {
310 cursorY = focusItem.positionToRectangle(focusItem.cursorPosition).y;
314 const pos = focusItem.mapToItem(flickableContents, 0, cursorY -
Units.
gridUnit*3);
316 if (pos.y >= scrollView.flickableItem.contentY && pos.y <= scrollView.flickableItem.contentY + scrollView.flickableItem.height - Kirigami.Units.gridUnit * 8) {
319 scrollView.flickableItem.contentY = pos.y;
324 property int margins: Kirigami.Units.gridUnit * 5
326 target: outerFlickable
328 from: -outerFlickable.height
329 to: outerFlickable.openPosition
330 duration: Kirigami.Units.longDuration
331 easing.type: Easing.OutQuad
337 duration: Kirigami.Units.longDuration
338 easing.type: Easing.InQuad
344 target: outerFlickable
346 from: outerFlickable.contentY
347 to: outerFlickable.visibleArea.yPosition < (1 - outerFlickable.visibleArea.heightRatio)/2 || scrollView.flickableItem.contentHeight < outerFlickable.height
348 ? outerFlickable.openPosition
349 : outerFlickable.contentHeight - outerFlickable.height + outerFlickable.topEmptyArea + headerItem.height + footerItem.height
350 duration: Kirigami.Units.longDuration
351 easing.type: Easing.OutQuad
354 SequentialAnimation {
358 target: outerFlickable
360 from: outerFlickable.contentY + (contentLayout.initialHeight - contentLayout.height)
361 to: outerFlickable.visibleArea.yPosition < (1 - outerFlickable.visibleArea.heightRatio)/2 ? -mainItem.height : outerFlickable.contentHeight
362 duration: Kirigami.Units.longDuration
363 easing.type: Easing.InQuad
369 duration: Kirigami.Units.longDuration
370 easing.type: Easing.InQuad
375 contentLayout.initialHeight = 0
376 scrollView.flickableItem.contentY = -mainItem.height;
377 mainItem.visible =
false;
384 opacity: 0.3 * Math.min(
385 (Math.min(outerFlickable.contentY + outerFlickable.height, outerFlickable.height) / outerFlickable.height),
386 (2 + (outerFlickable.contentHeight - outerFlickable.contentY - outerFlickable.topMargin - outerFlickable.bottomMargin)/outerFlickable.height))
391 drag.filterChildren:
true
394 onPressed: mouse => {
395 const pos = mapToItem(contentLayout, mouse.x, mouse.y);
396 if (contentLayout.contains(pos) && mouseHover.hovered) {
398 outerFlickable.interactive =
false
401 onReleased: mouse => {
402 const pos = mapToItem(contentLayout, mouse.x, mouse.y);
403 if (!contentLayout.contains(pos)) {
407 outerFlickable.interactive =
true
412 id: flickableContents
414 readonly
property real listHeaderHeight: scrollView.flickableItem ? -scrollView.flickableItem.originY : 0
416 y: (scrollView.contentItem !== flickableContents ? -scrollView.flickableItem.contentY - listHeaderHeight - (headerItem.visible ? headerItem.height : 0): 0)
418 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 ) ) + leftPadding + rightPadding
421 implicitHeight: scrollView.viewContent === flickableContents ? root.contentItem.height + topPadding + bottomPadding : 0
424 target: enabled ? flickableContents.Window.activeFocusItem :
null
425 enabled: flickableContents.focus && flickableContents.Window.activeFocusItem && flickableContents.Window.activeFocusItem.hasOwnProperty(
"text")
426 function onTextChanged() {
427 if (
Qt.inputMethod.cursorRectangle.y +
Qt.inputMethod.cursorRectangle.height > mainItem.Window.height) {
428 scrollView.flickableItem.contentY += (
Qt.inputMethod.cursorRectangle.y +
Qt.inputMethod.cursorRectangle.height) - mainItem.Window.height
434 id: contentItemParent
437 leftMargin: leftPadding
438 topMargin: topPadding
439 rightMargin: rightPadding
440 bottomMargin: bottomPadding
446 target: scrollView.flickableItem
447 property real oldContentHeight: 0
448 function onContentHeightChanged() {
449 if (openAnimation.running) {
450 openAnimation.running =
false;
456 if (scrollView.animatedContentHeight < outerFlickable.height
457 || scrollView.flickableItem.oldContentHeight < outerFlickable.height
459 outerFlickable.adjustPosition();
461 oldContentHeight = scrollView.animatedContentHeight
473 contentHeight: Math.max(height+1, scrollView.animatedContentHeight + topEmptyArea)
476 readonly
property int topEmptyArea: Math.max(height-scrollView.animatedContentHeight, Kirigami.Units.gridUnit * 3)
478 readonly
property real openPosition: Math.max(0, outerFlickable.height - outerFlickable.contentHeight + headerItem.height + footerItem.height) + height/2 - contentLayout.height/2;
480 onOpenPositionChanged: {
481 if (openAnimation.running) {
482 openAnimation.running =
false;
484 }
else if (root.sheetOpen) {
489 property real oldContentY: NaN
490 property real oldContentHeight: 0
491 property bool lastMovementWasDown:
false
492 property real startDraggingPos
493 property bool layoutMovingGuard:
false
494 Kirigami.WheelHandler {
495 target: outerFlickable
496 scrollFlickableTarget:
false
499 function adjustPosition() {
500 if(layoutMovingGuard)
return;
502 if (openAnimation.running) {
503 openAnimation.running =
false;
506 resetAnimation.running =
false;
507 contentY = openPosition;
514 onPressed: mouse => {
515 if (mouseHover.hovered) {
516 outerFlickable.interactive =
false
519 onReleased: mouse => {
520 outerFlickable.interactive =
true
525 if (scrollView.userInteracting) {
529 const startPos = -scrollView.flickableItem.topMargin - flickableContents.listHeaderHeight;
530 const pos = contentY - topEmptyArea - flickableContents.listHeaderHeight;
531 const endPos = scrollView.animatedContentHeight - scrollView.flickableItem.height + scrollView.flickableItem.bottomMargin - flickableContents.listHeaderHeight;
533 layoutMovingGuard =
true;
534 if (endPos - pos > 0) {
535 contentLayout.y = Math.round(Math.max(root.topInset, scrollView.flickableItem.topMargin - pos - flickableContents.listHeaderHeight));
536 }
else if (scrollView.flickableItem.topMargin - pos < 0) {
537 contentLayout.y = Math.round(endPos - pos + root.topInset);
539 layoutMovingGuard =
false;
541 scrollView.flickableItem.contentY = Math.max(
542 startPos, Math.min(pos, endPos));
544 lastMovementWasDown = contentY < oldContentY;
545 oldContentY = contentY;
549 if (openAnimation.running || closeAnimation.running) {
552 if (scrollView.flickableItem.atYBeginning ||scrollView.flickableItem.atYEnd) {
553 resetAnimation.restart();
559 startDraggingPos = contentY;
563 let shouldClose =
false;
566 if (scrollView.flickableItem.atYBeginning) {
567 if (startDraggingPos - contentY > Kirigami.Units.gridUnit * 4 &&
568 contentY < -Kirigami.Units.gridUnit * 4 &&
569 lastMovementWasDown) {
574 if (scrollView.flickableItem.atYEnd) {
575 if (contentY - startDraggingPos > Kirigami.Units.gridUnit * 4 &&
576 contentY > contentHeight - height + Kirigami.Units.gridUnit * 4 &&
577 !lastMovementWasDown) {
583 root.sheetOpen =
false
584 }
else if (scrollView.flickableItem.atYBeginning || scrollView.flickableItem.atYEnd) {
585 resetAnimation.restart();
593 onContentHeightChanged: {
597 if (contentHeight < height || oldContentHeight < height) {
600 oldContentHeight = contentHeight;
607 parent: outerFlickable
608 anchors.horizontalCenter: parent.horizontalCenter
609 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 + root.leftPadding + root.rightPadding
610 height: Math.min(implicitHeight, parent.height) - root.topInset - root.bottomInset
611 property real initialHeight
615 duration: Kirigami.Units.shortDuration
616 easing.type: Easing.InOutCubic
624 Kirigami.ShadowedRectangle {
626 Layout.fillWidth:
true
627 Layout.alignment:
Qt.AlignTop
629 visible: root.header || root.showCloseButton
630 implicitHeight: Math.max(headerParent.implicitHeight, closeIcon.height) + Kirigami.Units.smallSpacing * 2
632 corners.topLeftRadius: Kirigami.Units.smallSpacing
633 corners.topRightRadius: Kirigami.Units.smallSpacing
634 Kirigami.Theme.colorSet: Kirigami.Theme.Header
635 Kirigami.Theme.inherit:
false
636 color: Kirigami.Theme.backgroundColor
640 header.parent = headerParent;
641 header.anchors.fill = headerParent;
649 implicitHeight: header ? header.implicitHeight : 0
652 leftMargin: Kirigami.Units.largeSpacing
653 margins: Kirigami.Units.smallSpacing
654 rightMargin: (root.showCloseButton ? closeIcon.width : 0) + Kirigami.Units.smallSpacing
660 readonly
property bool tallHeader: headerItem.height > (Kirigami.Units.iconSizes.smallMedium + Kirigami.Units.largeSpacing + Kirigami.Units.largeSpacing)
664 rightMargin: Kirigami.Units.largeSpacing
665 verticalCenter: headerItem.verticalCenter
666 margins: Kirigami.Units.smallSpacing
671 onTallHeaderChanged: {
674 anchors.verticalCenter = undefined
675 anchors.topMargin = Kirigami.Units.largeSpacing
676 anchors.top = headerItem.top
679 anchors.top = undefined
680 anchors.topMargin = undefined
681 anchors.verticalCenter = headerItem.verticalCenter
684 Component.onCompleted: tallHeaderChanged()
687 visible: root.showCloseButton
688 width: Kirigami.
Units.iconSizes.smallMedium
690 source: closeMouseArea.containsMouse ? "window-close" : "window-close-symbolic"
691 active: closeMouseArea.containsMouse
696 onClicked: mouse => root.close();
713 property Item viewContent
714 property real animatedContentHeight: flickableItem.contentHeight
715 property bool userInteracting:
false
716 Layout.fillWidth:
true
717 Layout.fillHeight:
true
718 property alias flickableItem: scrollView.contentItem
722 implicitHeight: flickableItem.contentHeight
723 Layout.maximumHeight: flickableItem.contentHeight
725 Layout.alignment:
Qt.AlignTop
728 QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
730 Behavior on animatedContentHeight {
732 duration: Kirigami.Units.shortDuration
733 easing.type: Easing.InOutCubic
738 Item { Layout.fillHeight:
true }
741 target: scrollView.flickableItem
742 property real oldContentY: 0
743 function onContentYChanged() {
744 if (outerFlickable.moving) {
745 oldContentY = scrollView.flickableItem.contentY;
748 scrollView.userInteracting =
true;
750 const diff = scrollView.flickableItem.contentY - oldContentY
752 outerFlickable.contentY = outerFlickable.contentY + diff;
755 contentLayout.y = Math.max(root.topInset, contentLayout.y - diff);
756 }
else if (scrollView.flickableItem.contentY < outerFlickable.topEmptyArea + headerItem.height) {
757 contentLayout.y = Math.min(outerFlickable.topEmptyArea + root.topInset, contentLayout.y - diff);
759 oldContentY = scrollView.flickableItem.contentY;
760 scrollView.userInteracting =
false;
764 visible: footerItem.visible
765 implicitHeight: footerItem.height
775 Kirigami.ShadowedRectangle {
777 width: contentLayout.width
778 corners.bottomLeftRadius: Kirigami.Units.smallSpacing
779 corners.bottomRightRadius: Kirigami.Units.smallSpacing
780 parent: outerFlickable
782 y: Math.min(parent.height, contentLayout.y + contentLayout.height) - height
784 implicitHeight: footerParent.implicitHeight + Kirigami.Units.smallSpacing * 2 + extraMargin
785 Kirigami.Theme.colorSet: Kirigami.Theme.Window
786 Kirigami.Theme.inherit:
false
787 color: Kirigami.Theme.backgroundColor
794 property int extraMargin: (!root.parent ||
795 !Kirigami.Settings.isMobile ||
796 typeof applicationWindow ===
"undefined" ||
797 (root.parent === applicationWindow().overlay) ||
798 !applicationWindow().controlsVisible ||
799 (applicationWindow().pageStack && applicationWindow().pageStack.globalToolBar && applicationWindow().pageStack.globalToolBar.actualStyle === Kirigami.ApplicationHeaderStyle.ToolBar) ||
800 (applicationWindow().header && applicationWindow().header.toString().indexOf(
"ToolBarApplicationHeader") === 0))
801 ? 0 : Kirigami.Units.gridUnit * 3
806 implicitHeight: footer ? footer.implicitHeight : 0
811 margins: Kirigami.Units.smallSpacing