From a0b2a8bbe7fd1d2faff4c6e726f0e2b577b342f5 Mon Sep 17 00:00:00 2001 From: Devin Lin Date: Wed, 13 Mar 2024 16:14:25 -0400 Subject: [PATCH] Keyboard: Change swipe dismiss gesture to animate opacity instead of position --- qml/Keyboard.qml | 123 ++++++++++++++++++++++++++++------------- qml/keys/CharKey.qml | 8 --- qml/keys/PressArea.qml | 28 ++-------- 3 files changed, 91 insertions(+), 68 deletions(-) diff --git a/qml/Keyboard.qml b/qml/Keyboard.qml index bdcf0d69..c3bb2c4c 100644 --- a/qml/Keyboard.qml +++ b/qml/Keyboard.qml @@ -28,7 +28,7 @@ * */ -import QtQuick 2.4 +import QtQuick 2.12 import QtQuick.Controls 2.4 import MaliitKeyboard 2.0 @@ -91,26 +91,75 @@ Item { MouseArea { id: swipeArea - property int jumpBackThreshold: Device.gu(10) - anchors.fill: parent - drag.target: keyboardSurface - drag.axis: Drag.YAxis; - drag.minimumY: 0 - drag.maximumY: parent.height - //fix for lp:1277186 - //only filter children when wordRibbon visible - drag.filterChildren: wordRibbon.visible // Avoid conflict with extended key swipe selection and cursor swipe mode enabled: !canvas.extendedKeysShown && !fullScreenItem.cursorSwipe - onReleased: { - if (keyboardSurface.y > jumpBackThreshold) { + // The percentage we are open by (0.0 -> 1.0) + // It is reset to 1.0 by state when opened + property real openFactor: 1.0 + + // Whether the swipe is in the closing direction + property bool isClosing: false + + // How far you swipe down to fully close + readonly property real swipeDownDistance: height * 0.8 + + // How many pixels we need to swipe before we can consider closing the keyboard + readonly property int jumpBackThreshold: Device.gu(10) + + property real previousY: 0 + onPositionChanged: (mouse) => { + swipeDelta(mouse.y - previousY); + previousY = mouse.y; + } + onReleased: swipeRelease() + + function swipeDelta(deltaY) { + const progressFactor = deltaY / swipeDownDistance; + const newOpenFactor = Math.max(0.0, Math.min(1.0, openFactor - progressFactor)); + isClosing = newOpenFactor < openFactor; + openFactor = newOpenFactor; + + if (swipeArea.openFactor <= 0.0) { + Keyboard.hide(); MaliitGeometry.shown = false; + } + } + + function swipeRelease() { + previousY = 0; + const pixelsMovedBy = swipeDownDistance * (1.0 - openFactor); + + if (isClosing && pixelsMovedBy > jumpBackThreshold) { + // Close keyboard if we are in closing direction, and surpassed threshold + openFactorAnimation.from = openFactor; + openFactorAnimation.to = 0.0; + openFactorAnimation.restart(); } else { - bounceBackAnimation.from = keyboardSurface.y - bounceBackAnimation.start(); + // Keep keyboard open otherwise + openFactorAnimation.from = openFactor; + openFactorAnimation.to = 1.0; + openFactorAnimation.restart(); + } + + isClosing = false; + } + + PropertyAnimation { + id: openFactorAnimation + duration: 100 + target: swipeArea + properties: "openFactor" + easing.type: Easing.Linear; + + onFinished: { + // close keyboard if it is visually closed + if (swipeArea.openFactor <= 0.0) { + Keyboard.hide(); + MaliitGeometry.shown = false; + } } } @@ -118,15 +167,7 @@ Item { id: keyboardSurface objectName: "keyboardSurface" - x:0 - y:0 - width: parent.width - height: canvas.height - - onXChanged: fullScreenItem.reportKeyboardVisibleRect(); - onYChanged: fullScreenItem.reportKeyboardVisibleRect(); - onWidthChanged: fullScreenItem.reportKeyboardVisibleRect(); - onHeightChanged: fullScreenItem.reportKeyboardVisibleRect(); + anchors.fill: parent WordRibbon { id: wordRibbon @@ -158,10 +199,31 @@ Item { anchors.bottom: wordRibbon.visible ? wordRibbon.top : keyboardComp.top } + // Close chevron that shows as you swipe down the keyboard + Item { + width: closeChevron.width + height: closeChevron.height + anchors.centerIn: parent + + // Slowly show chevron + opacity: Math.max(0.0, 1.0 - swipeArea.openFactor * 1.3) + scale: Math.max(0.0, 1.0 - swipeArea.openFactor * 1.3) + + Label { + id: closeChevron + text: '⌄' + font.pixelSize: keyboardSurface.height * 0.25 + } + } + Item { id: keyboardComp objectName: "keyboardComp" + // Hide as you swipe down the keyboard + // We lower opacity at a faster rate than the openFactor property + opacity: 1.0 - Math.min(1.0, (1.0 - swipeArea.openFactor) * 1.5) + visible: !fullScreenItem.cursorSwipe height: parent.height width: parent.width @@ -188,24 +250,12 @@ Item { } } - PropertyAnimation { - id: bounceBackAnimation - // Animations don't have an "enabled" property, so just set the - // target to null if animation is disabled, which effectively also - // disables the animation. - target: Keyboard.animationEnabled ? keyboardSurface : null - properties: "y" - easing.type: Easing.OutBounce; - easing.overshoot: 2.0 - to: 0 - } - state: "HIDDEN" states: [ State { name: "SHOWN" - PropertyChanges { target: keyboardSurface; y: 0; } + PropertyChanges { target: swipeArea; openFactor: 1.0; } onCompleted: { canvas.firstShow = false; canvas.hidingComplete = false; @@ -215,7 +265,6 @@ Item { State { name: "HIDDEN" - PropertyChanges { target: keyboardSurface; y: canvas.height } onCompleted: { canvas.languageMenu.close(); keypad.closeExtendedKeys(); diff --git a/qml/keys/CharKey.qml b/qml/keys/CharKey.qml index c7e30a38..8c5a15a6 100644 --- a/qml/keys/CharKey.qml +++ b/qml/keys/CharKey.qml @@ -383,12 +383,4 @@ Item { keyMouseArea.evaluateSelectorSwipe(); } } - - Connections { - target: swipeArea.drag - function onActiveChanged() { - if (swipeArea.drag.active) - keyMouseArea.cancelPress(); - } - } } diff --git a/qml/keys/PressArea.qml b/qml/keys/PressArea.qml index f44b4beb..2cf4eb04 100644 --- a/qml/keys/PressArea.qml +++ b/qml/keys/PressArea.qml @@ -86,13 +86,9 @@ MultiPointTouchArea { lastY = point.y; lastYChange = distance; } - // Hide if we get close to the bottom of the screen. - // This works around issues with devices with touch buttons - // below the screen preventing release events when swiped - // over - if(point.sceneY > fullScreenItem.height - Device.gu(4) && point.y > startY + Device.gu(8) && !held) { - Keyboard.hide(); - } + + // send swipe event to keyboard container + swipeArea.swipeDelta(distance); } else { lastY = point.y; } @@ -141,22 +137,8 @@ MultiPointTouchArea { } onReleased: { - // Don't evaluate if the release point is above the start point - // or further away from its start than the height of the whole keyboard. - // This works around touches sometimes being recognized as ending below - // the bottom of the screen. - if (point.y > panel.height) { - console.warn("Touch point released past height of keyboard. Ignoring."); - } else if (!(point.y <= startY)) { - // Handles swiping away the keyboard - // Hide if the end point is more than 8 grid units from the start - if (!held && point.y > startY + Device.gu(8)) { - Keyboard.hide(); - } else { - bounceBackAnimation.from = keyboardSurface.y; - bounceBackAnimation.start(); - } - } + // send release event to keyboard container + swipeArea.swipeRelease(); pressed = false; held = false;