Changeset View
Changeset View
Standalone View
Standalone View
src/controls/templates/OverlaySheet.qml
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | * SPDX-FileCopyrightText: 2016 by Marco Martin <mart@kde.org> | 2 | * SPDX-FileCopyrightText: 2016-2020 Marco Martin <notmart@gmail.com> | ||
3 | * | 3 | * | ||
4 | * SPDX-License-Identifier: LGPL-2.0-or-later | 4 | * SPDX-License-Identifier: LGPL-2.0-or-later | ||
ngraham: Is this intentional? | |||||
mart: ah, sorry no derives from conflicts resolution | |||||
5 | */ | 5 | */ | ||
6 | 6 | | |||
7 | | ||||
anthonyfieroni: Keep SPDX. | |||||
7 | import QtQuick 2.11 | 8 | import QtQuick 2.11 | ||
8 | import QtQuick.Layouts 1.2 | 9 | import QtQuick.Layouts 1.2 | ||
9 | import QtQuick.Window 2.2 | 10 | import QtQuick.Window 2.2 | ||
10 | import org.kde.kirigami 2.7 | 11 | import org.kde.kirigami 2.11 | ||
11 | import QtGraphicalEffects 1.0 | 12 | import QtGraphicalEffects 1.0 | ||
12 | import QtQuick.Templates 2.0 as T2 | 13 | import QtQuick.Templates 2.0 as T2 | ||
13 | import "private" | 14 | import "private" | ||
14 | import "../private" | 15 | import "../private" | ||
15 | 16 | | |||
16 | /** | 17 | /** | ||
17 | * An overlay sheet that covers the current Page content. | 18 | * An overlay sheet that covers the current Page content. | ||
18 | * Its contents can be scrolled up or down, scrolling all the way up or | 19 | * Its contents can be scrolled up or down, scrolling all the way up or | ||
▲ Show 20 Lines • Show All 84 Lines • ▼ Show 20 Line(s) | 26 | QtObject { | |||
103 | * @since 5.44 | 104 | * @since 5.44 | ||
104 | */ | 105 | */ | ||
105 | property alias showCloseButton: closeIcon.visible | 106 | property alias showCloseButton: closeIcon.visible | ||
106 | 107 | | |||
107 | property Item parent | 108 | property Item parent | ||
108 | 109 | | |||
109 | 110 | | |||
110 | function open() { | 111 | function open() { | ||
111 | openAnimation.from = -mainItem.height; | | |||
112 | openAnimation.to = openAnimation.topOpenPosition; | | |||
113 | openAnimation.running = true; | 112 | openAnimation.running = true; | ||
114 | root.sheetOpen = true; | 113 | root.sheetOpen = true; | ||
115 | mainItem.visible = true; | 114 | mainItem.visible = true; | ||
116 | } | 115 | } | ||
117 | 116 | | |||
118 | function close() { | 117 | function close() { | ||
119 | if (scrollView.flickableItem.contentY < 0) { | | |||
120 | closeAnimation.to = -height; | | |||
121 | } else { | | |||
122 | closeAnimation.to = scrollView.flickableItem.contentHeight; | | |||
123 | } | | |||
124 | closeAnimation.running = true; | 118 | closeAnimation.running = true; | ||
125 | } | 119 | } | ||
126 | 120 | | |||
127 | onBackgroundChanged: { | 121 | onBackgroundChanged: { | ||
128 | background.parent = flickableContents; | 122 | background.parent = contentLayout.parent; | ||
123 | background.anchors.fill = contentLayout; | ||||
129 | background.z = -1; | 124 | background.z = -1; | ||
130 | } | 125 | } | ||
131 | onContentItemChanged: { | 126 | onContentItemChanged: { | ||
132 | if (contentItem.hasOwnProperty("contentY") && // Check if flickable | 127 | if (contentItem instanceof Flickable) { | ||
133 | contentItem.hasOwnProperty("contentHeight")) { | 128 | scrollView.flickableItem = contentItem; | ||
134 | contentItem.parent = scrollView; | 129 | contentItem.parent = scrollView; | ||
130 | contentItem.anchors.fill = scrollView; | ||||
135 | scrollView.contentItem = contentItem; | 131 | scrollView.contentItem = contentItem; | ||
136 | } else { | 132 | } else { | ||
137 | contentItem.parent = contentItemParent; | 133 | contentItem.parent = contentItemParent; | ||
138 | scrollView.contentItem = flickableContents; | 134 | scrollView.contentItem = flickableContents; | ||
139 | contentItem.anchors.left = contentItemParent.left; | 135 | contentItem.anchors.left = contentItemParent.left; | ||
140 | contentItem.anchors.right = contentItemParent.right; | 136 | contentItem.anchors.right = contentItemParent.right; | ||
141 | } | 137 | } | ||
138 | scrollView.flickableItem.interactive = false; | ||||
142 | scrollView.flickableItem.flickableDirection = Flickable.VerticalFlick; | 139 | scrollView.flickableItem.flickableDirection = Flickable.VerticalFlick; | ||
143 | } | 140 | } | ||
144 | onSheetOpenChanged: { | 141 | onSheetOpenChanged: { | ||
145 | if (sheetOpen) { | 142 | if (sheetOpen) { | ||
146 | open(); | 143 | open(); | ||
147 | } else { | 144 | } else { | ||
148 | close(); | 145 | close(); | ||
149 | Qt.inputMethod.hide(); | 146 | Qt.inputMethod.hide(); | ||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Line(s) | 191 | if (!flickableContents.contains(pos)) { | |||
195 | root.close(); | 192 | root.close(); | ||
196 | } | 193 | } | ||
197 | } | 194 | } | ||
198 | 195 | | |||
199 | readonly property int contentItemPreferredWidth: root.contentItem.Layout.preferredWidth > 0 ? root.contentItem.Layout.preferredWidth : root.contentItem.implicitWidth | 196 | readonly property int contentItemPreferredWidth: root.contentItem.Layout.preferredWidth > 0 ? root.contentItem.Layout.preferredWidth : root.contentItem.implicitWidth | ||
200 | 197 | | |||
201 | readonly property int contentItemMaximumWidth: width > Units.gridUnit * 30 ? width * 0.95 : width | 198 | readonly property int contentItemMaximumWidth: width > Units.gridUnit * 30 ? width * 0.95 : width | ||
202 | 199 | | |||
203 | property bool ownSizeUpdate: false | | |||
204 | function updateContentWidth() { | | |||
205 | if (!contentItem.contentItem) { | | |||
206 | return; | | |||
207 | } | | |||
208 | | ||||
209 | var newWidth = Math.min(contentItemMaximumWidth, Math.max(mainItem.width/2, Math.min(mainItem.width, mainItem.contentItemPreferredWidth))); | | |||
210 | | ||||
211 | if (scrollView.verticalScrollBar && scrollView.verticalScrollBar.interactive) { | | |||
212 | newWidth -= scrollView.verticalScrollBar.width; | | |||
213 | } | | |||
214 | | ||||
215 | ownSizeUpdate = true; | | |||
216 | contentItem.contentItem.x = (mainItem.width - newWidth)/2 | | |||
217 | contentItem.contentItem.width = newWidth; | | |||
218 | ownSizeUpdate = false; | | |||
219 | } | | |||
220 | onContentItemMaximumWidthChanged: updateContentWidth() | | |||
221 | onWidthChanged: updateContentWidth() | | |||
222 | Connections { | | |||
223 | target: typeof contentItem.contentItem === "undefined" ? null : contentItem.contentItem | | |||
224 | onWidthChanged: { | | |||
225 | if (!mainItem.ownSizeUpdate) { | | |||
226 | mainItem.updateContentWidth(); | | |||
227 | } | | |||
228 | } | | |||
229 | } | | |||
230 | onHeightChanged: { | 200 | onHeightChanged: { | ||
231 | var focusItem; | 201 | var focusItem; | ||
232 | 202 | | |||
233 | focusItem = Window.activeFocusItem; | 203 | focusItem = Window.activeFocusItem; | ||
234 | 204 | | |||
235 | if (!focusItem) { | 205 | if (!focusItem) { | ||
236 | return; | 206 | return; | ||
237 | } | 207 | } | ||
Show All 25 Lines | 232 | if (pos.y >= scrollView.flickableItem.contentY && pos.y <= scrollView.flickableItem.contentY + scrollView.flickableItem.height - Units.gridUnit * 8) { | |||
263 | return; | 233 | return; | ||
264 | } | 234 | } | ||
265 | scrollView.flickableItem.contentY = pos.y; | 235 | scrollView.flickableItem.contentY = pos.y; | ||
266 | } | 236 | } | ||
267 | 237 | | |||
268 | ParallelAnimation { | 238 | ParallelAnimation { | ||
269 | id: openAnimation | 239 | id: openAnimation | ||
270 | property int margins: Units.gridUnit * 5 | 240 | property int margins: Units.gridUnit * 5 | ||
271 | property int topOpenPosition: Math.min(-mainItem.height*0.15, scrollView.flickableItem.contentHeight - mainItem.height + margins) | | |||
272 | property alias from: openAnimationInternal.from | | |||
273 | property alias to: openAnimationInternal.to | | |||
274 | NumberAnimation { | 241 | NumberAnimation { | ||
275 | id: openAnimationInternal | 242 | target: outerFlickable | ||
276 | target: scrollView.flickableItem | | |||
277 | properties: "contentY" | 243 | properties: "contentY" | ||
278 | from: -mainItem.height | 244 | from: -outerFlickable.height | ||
279 | to: openAnimation.topOpenPosition | 245 | to: Math.max(0, outerFlickable.height - outerFlickable.contentHeight + Units.gridUnit * 2) | ||
280 | duration: Units.longDuration | 246 | duration: Units.longDuration | ||
281 | easing.type: Easing.OutQuad | 247 | easing.type: Easing.OutQuad | ||
282 | } | 248 | } | ||
283 | OpacityAnimator { | 249 | OpacityAnimator { | ||
284 | target: mainItem | 250 | target: mainItem | ||
285 | from: 0 | 251 | from: 0 | ||
286 | to: 1 | 252 | to: 1 | ||
287 | duration: Units.longDuration | 253 | duration: Units.longDuration | ||
288 | easing.type: Easing.InQuad | 254 | easing.type: Easing.InQuad | ||
289 | } | 255 | } | ||
290 | } | 256 | } | ||
291 | 257 | | |||
258 | NumberAnimation { | ||||
259 | id: resetAnimation | ||||
260 | target: outerFlickable | ||||
261 | properties: "contentY" | ||||
262 | from: outerFlickable.contentY | ||||
263 | to: outerFlickable.visibleArea.yPosition < (1 - outerFlickable.visibleArea.heightRatio)/2 || scrollView.flickableItem.contentHeight < outerFlickable.height | ||||
264 | ? Math.max(0, outerFlickable.height - outerFlickable.contentHeight + Units.gridUnit * 2) | ||||
265 | : outerFlickable.contentHeight - outerFlickable.height + outerFlickable.topEmptyArea + headerItem.height + footerItem.height | ||||
266 | duration: Units.longDuration | ||||
I thought we weren't supposed to multiply duration values. :) is the new veryLongDuration not long enough? do we need superDuperLongDuration? ngraham: I thought we weren't supposed to multiply duration values. :) is the new `veryLongDuration` not… | |||||
mart: was a debug thing forgotten in, sorry :) | |||||
267 | easing.type: Easing.OutQuad | ||||
268 | } | ||||
269 | | ||||
292 | SequentialAnimation { | 270 | SequentialAnimation { | ||
293 | id: closeAnimation | 271 | id: closeAnimation | ||
294 | property int to: -mainItem.height | | |||
295 | ParallelAnimation { | 272 | ParallelAnimation { | ||
296 | NumberAnimation { | 273 | NumberAnimation { | ||
297 | target: scrollView.flickableItem | 274 | target: outerFlickable | ||
298 | properties: "contentY" | 275 | properties: "contentY" | ||
299 | to: closeAnimation.to | 276 | to: outerFlickable.visibleArea.yPosition < (1 - outerFlickable.visibleArea.heightRatio)/2 ? -mainItem.height : outerFlickable.contentHeight | ||
300 | duration: Units.longDuration | 277 | duration: Units.longDuration | ||
301 | easing.type: Easing.InQuad | 278 | easing.type: Easing.InQuad | ||
302 | } | 279 | } | ||
303 | OpacityAnimator { | 280 | OpacityAnimator { | ||
304 | target: mainItem | 281 | target: mainItem | ||
305 | from: 1 | 282 | from: 1 | ||
306 | to: 0 | 283 | to: 0 | ||
307 | duration: Units.longDuration | 284 | duration: Units.longDuration | ||
308 | easing.type: Easing.InQuad | 285 | easing.type: Easing.InQuad | ||
309 | } | 286 | } | ||
310 | } | 287 | } | ||
311 | ScriptAction { | 288 | ScriptAction { | ||
312 | script: { | 289 | script: { | ||
313 | scrollView.flickableItem.contentY = -mainItem.height; | 290 | scrollView.flickableItem.contentY = -mainItem.height; | ||
314 | mainItem.visible = root.sheetOpen = false; | 291 | mainItem.visible = root.sheetOpen = false; | ||
315 | } | 292 | } | ||
316 | } | 293 | } | ||
317 | } | 294 | } | ||
318 | Rectangle { | 295 | Rectangle { | ||
319 | anchors.fill: parent | 296 | anchors.fill: parent | ||
320 | color: "black" | 297 | color: "black" | ||
321 | opacity: 0.6 * Math.min( | 298 | opacity: 0.6 * Math.min( | ||
322 | (Math.min(scrollView.flickableItem.contentY + scrollView.flickableItem.height, scrollView.flickableItem.height) / scrollView.flickableItem.height), | 299 | (Math.min(outerFlickable.contentY + outerFlickable.height, outerFlickable.height) / outerFlickable.height), | ||
323 | (2 + (scrollView.flickableItem.contentHeight - scrollView.flickableItem.contentY - scrollView.flickableItem.topMargin - scrollView.flickableItem.bottomMargin)/scrollView.flickableItem.height)) | 300 | (2 + (outerFlickable.contentHeight - outerFlickable.contentY - outerFlickable.topMargin - outerFlickable.bottomMargin)/outerFlickable.height)) | ||
301 | } | ||||
302 | | ||||
303 | FocusScope { | ||||
304 | id: flickableContents | ||||
305 | | ||||
306 | readonly property real listHeaderHeight: scrollView.flickableItem ? -scrollView.flickableItem.originY : 0 | ||||
307 | | ||||
308 | y: (scrollView.contentItem != flickableContents ? -scrollView.flickableItem.contentY - listHeaderHeight - (headerItem.visible ? headerItem.height : 0): 0) | ||||
309 | | ||||
310 | width: mainItem.contentItemPreferredWidth <= 0 ? mainItem.width : Math.max(mainItem.width/2, Math.min(mainItem.contentItemMaximumWidth, mainItem.contentItemPreferredWidth)) | ||||
311 | | ||||
312 | height: scrollView.contentItem == flickableContents ? (root.contentItem.height + topPadding + bottomPadding) + (headerItem.visible ? headerItem.height : 0) + (footerItem.visible ? footerItem.height : 0) : 0 | ||||
313 | Connections { | ||||
314 | target: enabled ? flickableContents.Window.activeFocusItem : null | ||||
315 | enabled: flickableContents.focus && flickableContents.Window.activeFocusItem && flickableContents.Window.activeFocusItem.hasOwnProperty("text") | ||||
316 | onTextChanged: { | ||||
317 | if (Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height > mainItem.Window.height) { | ||||
318 | scrollView.flickableItem.contentY += (Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height) - mainItem.Window.height | ||||
319 | } | ||||
320 | } | ||||
321 | } | ||||
322 | | ||||
323 | Item { | ||||
324 | id: contentItemParent | ||||
325 | anchors { | ||||
326 | fill: parent | ||||
327 | leftMargin: leftPadding | ||||
328 | topMargin: topPadding + (headerItem.visible ? headerItem.height : 0) | ||||
329 | rightMargin: rightPadding + (scrollView.verticalScrollBar && scrollView.verticalScrollBar.interactive ? scrollView.verticalScrollBar.width : 0) | ||||
330 | bottomMargin: bottomPadding + (footerItem.visible ? footerItem.height : 0) | ||||
331 | } | ||||
332 | } | ||||
333 | } | ||||
334 | | ||||
335 | Connections { | ||||
336 | target: scrollView.flickableItem | ||||
337 | onContentHeightChanged: { | ||||
338 | if (openAnimation.running) { | ||||
339 | openAnimation.running = false; | ||||
340 | open(); | ||||
324 | } | 341 | } | ||
342 | } | ||||
343 | } | ||||
344 | | ||||
345 | Flickable { | ||||
346 | id: outerFlickable | ||||
347 | anchors.fill: parent | ||||
348 | contentWidth: width | ||||
349 | topMargin: height | ||||
350 | bottomMargin: height | ||||
351 | // +1: we need the flickable to be always interactive | ||||
ngraham: what's this `+1` for? | |||||
to make the view always scrolling, or the swipe to dismiss can get broken mart: to make the view always scrolling, or the swipe to dismiss can get broken | |||||
352 | contentHeight: Math.max(height+1, scrollView.flickableItem.contentHeight + topEmptyArea) | ||||
353 | | ||||
354 | readonly property int topEmptyArea: Math.max(height-scrollView.flickableItem.contentHeight, Units.gridUnit * 3) | ||||
355 | | ||||
356 | property int oldContentY: NaN | ||||
357 | property bool lastMovementWasDown: false | ||||
358 | property real startDraggingPos | ||||
359 | onContentYChanged: { | ||||
360 | if (scrollView.userInteracting) { | ||||
361 | return; | ||||
362 | } | ||||
363 | | ||||
364 | let startPos = -scrollView.flickableItem.topMargin - flickableContents.listHeaderHeight; | ||||
365 | let pos = contentY - topEmptyArea - flickableContents.listHeaderHeight; | ||||
366 | let endPos = scrollView.flickableItem.contentHeight - scrollView.flickableItem.height + scrollView.flickableItem.bottomMargin - flickableContents.listHeaderHeight; | ||||
367 | | ||||
368 | if (endPos - pos > 0) { | ||||
369 | contentLayout.y = Math.max(0, scrollView.flickableItem.topMargin - pos - flickableContents.listHeaderHeight); | ||||
370 | } else if (scrollView.flickableItem.topMargin - pos < 0) { | ||||
371 | contentLayout.y = endPos - pos; | ||||
372 | } | ||||
373 | | ||||
374 | scrollView.flickableItem.contentY = Math.max( | ||||
375 | startPos, Math.min(pos, endPos)); | ||||
376 | | ||||
377 | lastMovementWasDown = contentY < oldContentY; | ||||
378 | oldContentY = contentY; | ||||
379 | } | ||||
380 | | ||||
381 | onFlickEnded: { | ||||
382 | if (openAnimation.running || closeAnimation.running) { | ||||
383 | return; | ||||
384 | } | ||||
385 | if (scrollView.flickableItem.atYBeginning ||scrollView.flickableItem.atYEnd) { | ||||
386 | resetAnimation.restart(); | ||||
387 | } | ||||
388 | } | ||||
389 | | ||||
390 | onDraggingChanged: { | ||||
391 | if (dragging) { | ||||
392 | startDraggingPos = contentY; | ||||
393 | return; | ||||
394 | } | ||||
395 | | ||||
396 | let shouldClose = false; | ||||
397 | | ||||
398 | // close | ||||
399 | if (scrollView.flickableItem.atYBeginning) { | ||||
400 | if (startDraggingPos - contentY > Units.gridUnit * 4 && | ||||
401 | contentY < -Units.gridUnit * 4 && | ||||
402 | lastMovementWasDown) { | ||||
403 | shouldClose = true; | ||||
404 | } | ||||
405 | } | ||||
406 | | ||||
407 | if (scrollView.flickableItem.atYEnd) { | ||||
408 | if (contentY - startDraggingPos > Units.gridUnit * 4 && | ||||
409 | contentY > contentHeight - height + Units.gridUnit * 4 && | ||||
410 | !lastMovementWasDown) { | ||||
411 | shouldClose = true; | ||||
412 | } | ||||
413 | } | ||||
414 | | ||||
415 | if (shouldClose) { | ||||
416 | closeAnimation.restart(); | ||||
417 | } else if (scrollView.flickableItem.atYBeginning || scrollView.flickableItem.atYEnd) { | ||||
418 | resetAnimation.restart(); | ||||
419 | } | ||||
420 | } | ||||
421 | | ||||
422 | onHeightChanged: { | ||||
423 | if (scrollView.flickableItem.contentHeight < height) { | ||||
424 | contentYChanged(); | ||||
425 | } | ||||
426 | } | ||||
427 | | ||||
428 | ColumnLayout { | ||||
429 | id: contentLayout | ||||
430 | spacing: 0 | ||||
431 | // Its events should be filtered but not scrolled | ||||
432 | parent: outerFlickable | ||||
433 | anchors.horizontalCenter: parent.horizontalCenter | ||||
434 | width: mainItem.contentItemPreferredWidth <= 0 ? mainItem.width : Math.max(mainItem.width/2, Math.min(mainItem.contentItemMaximumWidth, mainItem.contentItemPreferredWidth)) | ||||
435 | height: Math.min(implicitHeight, parent.height) | ||||
325 | 436 | | |||
326 | Icon { | 437 | Icon { | ||
327 | id: closeIcon | 438 | id: closeIcon | ||
328 | anchors { | 439 | anchors { | ||
329 | right: headerItem.right | 440 | right: contentLayout.right | ||
330 | margins: Units.smallSpacing | 441 | margins: Units.smallSpacing | ||
331 | top: headerItem.top | 442 | top: contentLayout.top | ||
332 | } | 443 | } | ||
444 | parent: outerFlickable | ||||
333 | z: 3 | 445 | z: 3 | ||
334 | visible: !Settings.isMobile | 446 | visible: !Settings.isMobile | ||
335 | width: Units.iconSizes.smallMedium | 447 | width: Units.iconSizes.smallMedium | ||
336 | height: width | 448 | height: width | ||
337 | source: closeMouseArea.containsMouse ? "window-close" : "window-close-symbolic" | 449 | source: closeMouseArea.containsMouse ? "window-close" : "window-close-symbolic" | ||
338 | active: closeMouseArea.containsMouse | 450 | active: closeMouseArea.containsMouse | ||
339 | MouseArea { | 451 | MouseArea { | ||
340 | id: closeMouseArea | 452 | id: closeMouseArea | ||
341 | hoverEnabled: true | 453 | hoverEnabled: true | ||
342 | anchors.fill: parent | 454 | anchors.fill: parent | ||
343 | onClicked: root.close(); | 455 | onClicked: root.close(); | ||
344 | } | 456 | } | ||
457 | Binding { | ||||
458 | target: scrollView.verticalScrollBar | ||||
459 | property: "anchors.topMargin" | ||||
460 | value: headerItem.visible ? 0 : closeIcon.height + closeIcon.anchors.margins * 2 | ||||
345 | } | 461 | } | ||
462 | Separator { | ||||
463 | visible: !headerItem.visible && scrollView.verticalScrollBar.visible | ||||
464 | anchors { | ||||
465 | bottom: parent.bottom | ||||
466 | right: parent.right | ||||
467 | margins: -closeIcon.anchors.margins | ||||
468 | } | ||||
469 | width: scrollView.verticalScrollBar.width | ||||
470 | } | ||||
471 | } | ||||
472 | | ||||
346 | Rectangle { | 473 | Rectangle { | ||
347 | id: headerItem | 474 | id: headerItem | ||
348 | width: flickableContents.width | 475 | Layout.fillWidth: true | ||
349 | x: flickableContents.x | | |||
350 | visible: root.header | 476 | visible: root.header | ||
351 | height: Math.max(headerParent.implicitHeight, closeIcon.height) + Units.smallSpacing * 2 | 477 | implicitHeight: Math.max(headerParent.implicitHeight, closeIcon.height) + Units.smallSpacing * 2 | ||
352 | color: Theme.backgroundColor | 478 | color: Theme.backgroundColor | ||
353 | //different y depending if we're a listview or a normal item | | |||
354 | y: Math.max(0, -scrollView.flickableItem.contentY - (scrollView.contentItem != flickableContents ? height : 0)) | | |||
355 | z: 2 | 479 | z: 2 | ||
356 | Item { | 480 | Item { | ||
357 | id: headerParent | 481 | id: headerParent | ||
358 | implicitHeight: header ? header.implicitHeight : 0 | 482 | implicitHeight: header ? header.implicitHeight : 0 | ||
359 | anchors { | 483 | anchors { | ||
360 | fill: parent | 484 | fill: parent | ||
361 | margins: Units.smallSpacing | 485 | margins: Units.smallSpacing | ||
362 | rightMargin: closeIcon.width + Units.smallSpacing | 486 | rightMargin: closeIcon.width + Units.smallSpacing | ||
363 | } | 487 | } | ||
364 | } | 488 | } | ||
365 | 489 | | |||
366 | EdgeShadow { | 490 | Separator { | ||
367 | z: -2 | | |||
368 | edge: Qt.TopEdge | | |||
369 | anchors { | 491 | anchors { | ||
370 | right: parent.right | 492 | right: parent.right | ||
371 | left: parent.left | 493 | left: parent.left | ||
372 | top: parent.bottom | 494 | top: parent.bottom | ||
373 | } | 495 | } | ||
496 | } | ||||
497 | } | ||||
374 | 498 | | |||
375 | opacity: parent.y == 0 ? 1 : 0 | 499 | ScrollView { | ||
500 | id: scrollView | ||||
376 | 501 | | |||
377 | Behavior on opacity { | 502 | property bool userInteracting: false | ||
378 | NumberAnimation { | 503 | Layout.fillWidth: true | ||
379 | duration: Units.longDuration | 504 | Layout.fillHeight: true | ||
380 | easing.type: Easing.InOutQuad | 505 | | ||
506 | implicitHeight: flickableItem.contentHeight | ||||
381 | } | 507 | } | ||
508 | Connections { | ||||
509 | target: scrollView.flickableItem | ||||
510 | property real oldContentY: 0 | ||||
511 | onContentYChanged: { | ||||
512 | if (outerFlickable.moving) { | ||||
513 | oldContentY = scrollView.flickableItem.contentY; | ||||
514 | return; | ||||
382 | } | 515 | } | ||
516 | scrollView.userInteracting = true; | ||||
517 | | ||||
518 | let diff = scrollView.flickableItem.contentY - oldContentY | ||||
519 | | ||||
520 | outerFlickable.contentY = outerFlickable.contentY + diff; | ||||
521 | | ||||
522 | if (diff > 0) { | ||||
523 | contentLayout.y = Math.max(0, contentLayout.y - diff); | ||||
524 | } else if (scrollView.flickableItem.contentY < outerFlickable.topEmptyArea + headerItem.height) { | ||||
525 | contentLayout.y = Math.min(outerFlickable.topEmptyArea, contentLayout.y + (contentLayout.y - diff)); | ||||
383 | } | 526 | } | ||
527 | | ||||
528 | oldContentY = scrollView.flickableItem.contentY; | ||||
529 | scrollView.userInteracting = false; | ||||
384 | } | 530 | } | ||
531 | } | ||||
532 | Item { | ||||
533 | implicitHeight: footerItem.height | ||||
534 | } | ||||
535 | } | ||||
536 | | ||||
537 | // footer item is outside the layout as it should never scroll away | ||||
385 | Rectangle { | 538 | Rectangle { | ||
386 | id: footerItem | 539 | id: footerItem | ||
387 | width: flickableContents.width | 540 | width: contentLayout.width | ||
388 | x: flickableContents.x | 541 | parent: outerFlickable | ||
542 | x: contentLayout.x | ||||
543 | y: Math.min(parent.height, contentLayout.y + contentLayout.height) - height | ||||
389 | visible: root.footer | 544 | visible: root.footer | ||
390 | height: footerParent.implicitHeight + Units.smallSpacing * 2 + extraMargin | 545 | implicitHeight: footerParent.implicitHeight + Units.smallSpacing * 2 + extraMargin | ||
391 | color: Theme.backgroundColor | 546 | color: Theme.backgroundColor | ||
392 | y: mainItem.mapFromItem(flickableContents, 0, flickableContents.height).y - height | 547 | | ||
393 | onHeightChanged: y = Math.min(mainItem.height, mainItem.mapFromItem(flickableContents, 0, flickableContents.height).y) - footerItem.height; | | |||
394 | //Show an extra margin when: | 548 | //Show an extra margin when: | ||
395 | //* the application is in mobile mode (no toolbarapplicationheader) | 549 | //* the application is in mobile mode (no toolbarapplicationheader) | ||
396 | //* the bottom screen controls are visible | 550 | //* the bottom screen controls are visible | ||
397 | //* the sheet is displayed *under* the controls | 551 | //* the sheet is displayed *under* the controls | ||
398 | property int extraMargin: (!root.parent || | 552 | property int extraMargin: (!root.parent || | ||
399 | typeof applicationWindow === "undefined" || | 553 | typeof applicationWindow === "undefined" || | ||
400 | (root.parent === applicationWindow().overlay) || | 554 | (root.parent === applicationWindow().overlay) || | ||
401 | !applicationWindow().controlsVisible || | 555 | !applicationWindow().controlsVisible || | ||
402 | (applicationWindow().pageStack && applicationWindow().pageStack.globalToolBar && applicationWindow().pageStack.globalToolBar.actualStyle === ApplicationHeaderStyle.ToolBar) || | 556 | (applicationWindow().pageStack && applicationWindow().pageStack.globalToolBar && applicationWindow().pageStack.globalToolBar.actualStyle === ApplicationHeaderStyle.ToolBar) || | ||
403 | (applicationWindow().header && applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") === 0)) | 557 | (applicationWindow().header && applicationWindow().header.toString().indexOf("ToolBarApplicationHeader") === 0)) | ||
404 | ? 0 : Units.gridUnit * 3 | 558 | ? 0 : Units.gridUnit * 3 | ||
405 | Connections { | | |||
406 | target: scrollView.flickableItem | | |||
407 | onContentYChanged: footerItem.y = Math.min(mainItem.height, mainItem.mapFromItem(flickableContents, 0, flickableContents.height).y) - footerItem.height; | | |||
408 | 559 | | |||
409 | onHeightChanged: scrollView.flickableItem.contentYChanged() | | |||
410 | } | | |||
411 | z: 2 | 560 | z: 2 | ||
412 | Item { | 561 | Item { | ||
413 | id: footerParent | 562 | id: footerParent | ||
414 | implicitHeight: footer ? footer.implicitHeight : 0 | 563 | implicitHeight: footer ? footer.implicitHeight : 0 | ||
415 | anchors { | 564 | anchors { | ||
416 | top: parent.top | 565 | top: parent.top | ||
417 | left: parent.left | 566 | left: parent.left | ||
418 | right: parent.right | 567 | right: parent.right | ||
419 | margins: Units.smallSpacing | 568 | margins: Units.smallSpacing | ||
420 | } | 569 | } | ||
421 | } | 570 | } | ||
422 | 571 | | |||
423 | EdgeShadow { | 572 | Separator { | ||
424 | z: -2 | | |||
425 | edge: Qt.BottomEdge | | |||
426 | anchors { | 573 | anchors { | ||
427 | right: parent.right | 574 | right: parent.right | ||
428 | left: parent.left | 575 | left: parent.left | ||
429 | bottom: parent.top | 576 | bottom: parent.top | ||
430 | } | 577 | } | ||
431 | | ||||
432 | opacity: parent.y + parent.height < mainItem.height ? 0 : 1 | | |||
433 | | ||||
434 | Behavior on opacity { | | |||
435 | NumberAnimation { | | |||
436 | duration: Units.longDuration | | |||
437 | easing.type: Easing.InOutQuad | | |||
438 | } | 578 | } | ||
439 | } | 579 | } | ||
440 | } | 580 | } | ||
441 | } | 581 | } | ||
442 | | ||||
443 | FocusScope { | | |||
444 | id: flickableContents | | |||
445 | //anchors.horizontalCenter: parent.horizontalCenter | | |||
446 | x: (mainItem.width - width) / 2 | | |||
447 | | ||||
448 | readonly property real listHeaderHeight: scrollView.flickableItem && root.contentItem.headerItem ? root.contentItem.headerItem.height : 0 | | |||
449 | | ||||
450 | y: (scrollView.contentItem != flickableContents ? -scrollView.flickableItem.contentY - listHeaderHeight - (headerItem.visible ? headerItem.height : 0): 0) | | |||
451 | | ||||
452 | width: mainItem.contentItemPreferredWidth <= 0 ? mainItem.width : Math.max(mainItem.width/2, Math.min(mainItem.contentItemMaximumWidth, mainItem.contentItemPreferredWidth)) | | |||
453 | | ||||
454 | height: (scrollView.contentItem != flickableContents ? scrollView.flickableItem.contentHeight + listHeaderHeight : (root.contentItem.height + topPadding + bottomPadding)) + (headerItem.visible ? headerItem.height : 0) + (footerItem.visible ? footerItem.height : 0) | | |||
455 | Connections { | | |||
456 | target: enabled ? flickableContents.Window.activeFocusItem : null | | |||
457 | enabled: flickableContents.focus && flickableContents.Window.activeFocusItem && flickableContents.Window.activeFocusItem.hasOwnProperty("text") | | |||
458 | onTextChanged: { | | |||
459 | if (Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height > mainItem.Window.height) { | | |||
460 | scrollView.flickableItem.contentY += (Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height) - mainItem.Window.height | | |||
461 | } | | |||
462 | } | | |||
463 | } | | |||
464 | | ||||
465 | Item { | | |||
466 | id: contentItemParent | | |||
467 | anchors { | | |||
468 | fill: parent | | |||
469 | leftMargin: leftPadding | | |||
470 | topMargin: topPadding + (headerItem.visible ? headerItem.height : 0) | | |||
471 | rightMargin: rightPadding + (scrollView.verticalScrollBar && scrollView.verticalScrollBar.interactive ? scrollView.verticalScrollBar.width : 0) | | |||
472 | bottomMargin: bottomPadding + (footerItem.visible ? footerItem.height : 0) | | |||
473 | } | | |||
474 | } | | |||
475 | } | | |||
476 | Binding { | | |||
477 | when: scrollView.flickableItem != null | | |||
478 | target: scrollView.flickableItem | | |||
479 | property: "topMargin" | | |||
480 | //hack needed for smoother open anim | | |||
481 | value: openAnimation.running ? -scrollView.flickableItem.contentY : -openAnimation.topOpenPosition | | |||
482 | } | | |||
483 | Binding { | | |||
484 | when: scrollView.flickableItem != null | | |||
485 | target: scrollView.flickableItem | | |||
486 | property: "bottomMargin" | | |||
487 | value: openAnimation.margins | | |||
488 | } | | |||
489 | | ||||
490 | Binding { | | |||
491 | target: scrollView.verticalScrollBar ? scrollView.verticalScrollBar.anchors : null | | |||
492 | property: "topMargin" | | |||
493 | value: headerItem.y + headerItem.height | | |||
494 | } | | |||
495 | Binding { | | |||
496 | target: scrollView.verticalScrollBar | | |||
497 | property: "height" | | |||
498 | value: mainItem.height - (scrollView.verticalScrollBar ? scrollView.verticalScrollBar.anchors.topMargin : 0) - (mainItem.height - footerItem.y) | | |||
499 | } | | |||
500 | Binding { | | |||
501 | target: scrollView.verticalScrollBar ? scrollView.verticalScrollBar.anchors : null | | |||
502 | property: "rightMargin" | | |||
503 | value: mainItem.width - flickableContents.width - flickableContents.x | | |||
504 | } | | |||
505 | | ||||
506 | Connections { | | |||
507 | target: scrollView.flickableItem | | |||
508 | onContentHeightChanged: { | | |||
509 | if (openAnimation.running) { | | |||
510 | openAnimation.running = false; | | |||
511 | open(); | | |||
512 | } | | |||
513 | } | | |||
514 | onDraggingChanged: { | | |||
515 | if (scrollView.flickableItem.dragging) { | | |||
516 | return; | | |||
517 | } | | |||
518 | | ||||
519 | //close | | |||
520 | if ((mainItem.height + scrollView.flickableItem.contentY) < mainItem.height/2) { | | |||
521 | closeAnimation.to = -mainItem.height; | | |||
522 | closeAnimation.running = true; | | |||
523 | | ||||
524 | } else if ((mainItem.height*0.6 + scrollView.flickableItem.contentY) > scrollView.flickableItem.contentHeight) { | | |||
525 | closeAnimation.to = scrollView.flickableItem.contentHeight; | | |||
526 | closeAnimation.running = true; | | |||
527 | } | | |||
528 | } | | |||
529 | } | | |||
530 | | ||||
531 | Binding { | | |||
532 | target: scrollView.verticalScrollBar | | |||
533 | property: "visible" | | |||
534 | value: scrollView.flickableItem.contentHeight > mainItem.height*0.8 | | |||
535 | } | | |||
536 | Connections { | | |||
537 | target: scrollView.verticalScrollBar | | |||
538 | onActiveChanged: { | | |||
539 | if (!scrollView.verticalScrollBar.active) { | | |||
540 | scrollView.flickableItem.movementEnded(); | | |||
541 | } | | |||
542 | } | | |||
543 | } | | |||
544 | ScrollView { | | |||
545 | id: scrollView | | |||
546 | anchors.fill: parent | | |||
547 | horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff | | |||
548 | rightPadding: 0 | | |||
549 | } | | |||
550 | } | | |||
551 | } | 582 | } |
Is this intentional?