shift-shell/kwin/mobiletaskswitcher/qml/Task.qml
Micah Stanley fdc8958ce5 taskswitcher: Gesture Navigation: Quality of Life Improvements
Most Notable Changes Here Include:
1. If the user moves and lifts their finger up halfway up the screen, the navigation gesture will now go home.
2. The task drawer will now move in and out of view depending on the gesture navigation state.
3. The app window will now continue to shrink with resistance as the window moves further up.
4. When in the task drawer, if the user drags up from the bottom, the current task will now follow the users finger like the task does when dragging up from within an app.
5. The task drawer will now slide in from the side when it is not within an app.

I would upload a video here to showcase these changes. However, I was unable to get OBS to record my screen while in my plasma mobile session.
2024-10-15 00:55:29 +00:00

243 lines
7.3 KiB
QML

// SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
// SPDX-FileCopyrightText: 2021-2023 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
import QtQuick
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.kwin 3.0 as KWinComponents
Item {
id: delegate
required property var taskSwitcher
required property QtObject window
required property var model
required property real previewHeight
required property real previewWidth
readonly property real dragOffset: -control.y
readonly property int currentIndex: model.index
// whether this task is being interacted with
readonly property bool interactingActive: control.pressed && control.passedDragThreshold
// whether to show the text header
property bool showHeader: true
// the amount to darken the task preview by
property real darken: 0
opacity: 1 - dragOffset / taskSwitcher.height
//BEGIN functions
function closeApp() {
delegate.window.closeWindow();
}
function activateApp() {
taskSwitcherHelpers.openApp(model.index, delegate.window);
}
function minimizeApp() {
delegate.window.minimized = true;
}
//END functions
MouseArea {
id: control
width: parent.width
height: parent.height
// set cursor shape here, since taphandler seems to not be able to do it
cursorShape: Qt.PointingHandCursor
property bool movingUp: false
property real oldY: y
onYChanged: {
movingUp = y < oldY;
oldY = y;
}
onClicked: {
if (!passedDragThreshold) {
delegate.activateApp();
}
}
// pixels before we start treating it as drag event
readonly property real dragThreshold: 5
property real startPosition: 0
property bool hasStartPosition: false
property bool passedDragThreshold: false
onPositionChanged: (mouse) => {
// map it to the root area, so that it doesn't jitter (since this item is moving)
const yPos = control.mapToItem(delegate, mouse.x, mouse.y).y
// reset start position
if (!hasStartPosition) {
startPosition = yPos;
hasStartPosition = true;
}
// set threshold
if (!passedDragThreshold && Math.abs(y) > dragThreshold) {
passedDragThreshold = true;
}
// update position
// y < 0 - dragging up (dismissing the app)
y = Math.min(0, yPos - startPosition);
}
onPressedChanged: {
yAnimator.stop();
// reset values
if (pressed) {
hasStartPosition = false;
passedDragThreshold = false;
}
// run animation when finger lets go
if (!pressed) {
if (control.movingUp && control.y < -Kirigami.Units.gridUnit * 2) {
yAnimator.to = -root.height;
} else {
yAnimator.to = 0;
}
yAnimator.start();
}
}
// if the app doesn't close within a certain time, drag it back
Timer {
id: uncloseTimer
interval: 3000
onTriggered: {
yAnimator.to = 0;
yAnimator.restart();
}
}
NumberAnimation on y {
id: yAnimator
running: !control.pressed
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
to: 0
onFinished: {
if (to != 0) { // close app
taskSwitcherHelpers.lastClosedTask = currentIndex;
delegate.closeApp();
uncloseTimer.start();
}
}
}
// application
ColumnLayout {
id: column
anchors.fill: parent
spacing: 0
// header
RowLayout {
id: appHeader
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: column.height - appView.height
spacing: Kirigami.Units.smallSpacing * 2
opacity: delegate.showHeader ? 1 : 0
Behavior on opacity {
NumberAnimation { duration: Kirigami.Units.shortDuration }
}
Kirigami.Icon {
Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
Layout.alignment: Qt.AlignVCenter
source: delegate.window.icon
}
PlasmaComponents.Label {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
elide: Text.ElideRight
text: delegate.window.caption
color: "white"
}
PlasmaComponents.ToolButton {
Layout.alignment: Qt.AlignVCenter
z: 99
icon.name: "window-close"
icon.width: Kirigami.Units.iconSizes.smallMedium
icon.height: Kirigami.Units.iconSizes.smallMedium
onClicked: {
taskSwitcherHelpers.lastClosedTask = currentIndex;
delegate.closeApp()
}
}
}
// app preview
Rectangle {
id: appView
Layout.preferredWidth: taskSwitcherHelpers.previewWidth
Layout.preferredHeight: taskSwitcherHelpers.previewHeight
Layout.maximumWidth: taskSwitcherHelpers.previewWidth
Layout.maximumHeight: taskSwitcherHelpers.previewHeight
radius: Kirigami.Units.largeSpacing
color: Qt.rgba(0, 0, 0, 0.2)
clip: true
// scale animation on press
property real zoomScale: control.pressed ? 0.95 : 1
Behavior on zoomScale {
NumberAnimation {
duration: 200
easing.type: Easing.OutExpo
}
}
transform: Scale {
origin.x: appView.width / 2;
origin.y: appView.height / 2;
xScale: appView.zoomScale
yScale: appView.zoomScale
}
Item {
id: item
anchors.fill: parent
KWinComponents.WindowThumbnail {
id: thumbSource
wId: delegate.window.internalId
anchors.fill: parent
layer.enabled: true
layer.effect: ColorOverlay {
color: Qt.rgba(0, 0, 0, delegate.darken)
}
}
}
}
}
}
}