shift-shell/components/mobileshell/qml/taskswitcher/TaskSwitcher.qml

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

331 lines
12 KiB
QML
Raw Normal View History

2015-06-18 23:31:26 +00:00
/*
2021-03-01 20:03:25 +00:00
* SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
2021-10-18 03:50:59 +00:00
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
2015-06-18 23:31:26 +00:00
*
2021-03-01 20:03:25 +00:00
* SPDX-License-Identifier: LGPL-2.0-or-later
2015-06-18 23:31:26 +00:00
*/
2020-07-17 12:13:35 +00:00
import QtQuick 2.12
2015-06-18 23:31:26 +00:00
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
import org.kde.taskmanager 0.1 as TaskManager
2015-06-24 20:47:07 +00:00
import org.kde.plasma.core 2.1 as PlasmaCore
2019-10-11 10:06:12 +00:00
import org.kde.plasma.components 3.0 as PlasmaComponents
2019-09-18 13:31:04 +00:00
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
2015-06-18 23:31:26 +00:00
2019-09-18 14:49:44 +00:00
NanoShell.FullScreenOverlay {
2015-06-18 23:31:26 +00:00
id: window
visible: false
width: Screen.width
height: Screen.height
2021-10-18 03:50:59 +00:00
2021-10-31 04:11:10 +00:00
required property real taskPanelHeight // height of task panel, provided by main.qml
2021-10-18 03:50:59 +00:00
// dimensions of a window on the screen
readonly property real windowHeight: window.height - (navPanel.isPortrait ? window.taskPanelHeight : 0) - MobileShell.TopPanelControls.panelHeight
readonly property real windowWidth: window.width - (navPanel.isPortrait ? 0 : window.taskPanelHeight)
2020-07-17 12:13:35 +00:00
property int tasksCount: window.model.count
property int currentTaskIndex: tasksView.currentIndex
2020-07-17 12:13:35 +00:00
property TaskManager.TasksModel model
2021-10-18 03:50:59 +00:00
2021-10-18 18:47:17 +00:00
// properties controlled from main.qml MouseArea (swipe to open gesture)
2021-10-24 04:16:38 +00:00
property real oldYOffset: 0
property real yOffset: 0
2021-10-18 18:47:17 +00:00
2021-10-24 04:16:38 +00:00
// offset constants
readonly property real targetYOffsetDist: window.height - tasksView.height // offset distance to perfect opening
readonly property real dismissYOffsetDist: window.height
// set from NavigationPanel in main.qml
2021-10-18 18:47:17 +00:00
property bool wasInActiveTask: false // whether we were in an app before opening the task switcher
2021-10-24 04:16:38 +00:00
property bool currentlyDragging: false // whether we are in a swipe up gesture
2015-06-18 23:31:26 +00:00
property var displaysModel: MobileShell.DisplaysModel {}
2020-07-17 12:13:35 +00:00
enum MovementDirection {
None = 0,
2021-10-18 03:50:59 +00:00
Left,
Right
}
onVisibleChanged: {
if (!visible) {
window.contentItem.opacity = 1;
}
// hide homescreen elements to make use of wallpaper
2021-10-24 04:16:38 +00:00
if (visible) {
MobileShell.HomeScreenControls.hideHomeScreen(!window.wasInActiveTask); // only animate if going from homescreen
} else {
MobileShell.HomeScreenControls.showHomeScreen(true);
}
2021-10-18 03:50:59 +00:00
MobileShell.HomeScreenControls.taskSwitcherVisible = visible;
2020-07-17 12:13:35 +00:00
}
onTasksCountChanged: {
if (tasksCount == 0) {
hide();
}
}
2021-10-18 03:50:59 +00:00
2021-10-31 04:11:10 +00:00
// background
2020-07-31 10:06:46 +00:00
color: "transparent"
Rectangle {
2021-10-18 03:50:59 +00:00
id: backgroundRect
2020-07-31 10:06:46 +00:00
anchors.fill: parent
2021-10-24 04:16:38 +00:00
color: Qt.rgba(0, 0, 0, 0.6 * (window.wasInActiveTask ? 1 : Math.min(1, window.yOffset / window.targetYOffsetDist)))
2021-10-18 03:50:59 +00:00
MouseArea {
anchors.fill: parent
onClicked: hide()
}
2020-07-31 10:06:46 +00:00
}
2015-06-18 23:31:26 +00:00
2021-10-18 18:47:17 +00:00
//BEGIN functions
function show(animation) {
2021-10-24 04:16:38 +00:00
window.yOffset = 0;
2021-10-18 18:47:17 +00:00
window.wasInActiveTask = window.model.activeTask.row >= 0;
2021-10-18 03:50:59 +00:00
// skip to first active task
2021-10-18 18:47:17 +00:00
if (window.wasInActiveTask) {
tasksView.currentIndex = window.model.activeTask.row;
2021-10-31 19:40:39 +00:00
tasksView.positionViewAtIndex(window.model.activeTask.row, ListView.SnapPosition);
}
2021-10-18 03:50:59 +00:00
window.visible = true;
root.minimizeAll();
2021-10-18 18:47:17 +00:00
// animate app shrink
if (animation) {
2021-10-24 04:16:38 +00:00
offsetAnimator.to = window.targetYOffsetDist;
2021-10-18 18:47:17 +00:00
offsetAnimator.restart();
}
2015-06-18 23:31:26 +00:00
}
function hide() {
2021-10-18 18:47:17 +00:00
if (!window.visible) return;
2021-10-18 03:50:59 +00:00
window.visible = false;
2015-06-18 23:31:26 +00:00
}
2021-10-18 18:47:17 +00:00
function snapOffset() {
2021-10-24 04:16:38 +00:00
let movingUp = window.yOffset > window.oldYOffset;
if (movingUp || window.yOffset >= window.targetYOffsetDist) { // open task switcher and stay
offsetAnimator.to = window.targetYOffsetDist;
2021-10-18 18:47:17 +00:00
offsetAnimator.restart();
2021-10-24 04:16:38 +00:00
} else { // close task switcher and return to app
2021-10-18 18:47:17 +00:00
if (!window.wasInActiveTask) { // if pulled up from homescreen, don't activate app
offsetAnimator.activateApp = false;
}
offsetAnimator.to = 0;
offsetAnimator.restart();
}
}
// scroll to delegate index, and activate it
function activateWindow(id) {
offsetAnimator.to = 0;
offsetAnimator.restart();
}
2020-07-30 15:53:55 +00:00
function setSingleActiveWindow(id, delegate) {
if (id < 0) {
return;
}
var newActiveIdx = window.model.index(id, 0)
var newActiveGeo = tasksModel.data(newActiveIdx, TaskManager.AbstractTasksModel.ScreenGeometry)
for (var i = 0 ; i < tasksModel.count; i++) {
var idx = window.model.index(i, 0)
if (i == id) {
window.model.requestActivate(idx);
} else if (!tasksModel.data(idx, TaskManager.AbstractTasksModel.IsMinimized)) {
var geo = tasksModel.data(idx, TaskManager.AbstractTasksModel.ScreenGeometry)
// Only minimize the other windows in the same screen
if (geo === newActiveGeo) {
tasksModel.requestToggleMinimized(idx);
}
}
}
2021-10-18 03:50:59 +00:00
window.visible = false;
2020-07-30 15:53:55 +00:00
}
2021-10-18 18:47:17 +00:00
//END functions
2020-07-30 15:53:55 +00:00
2021-10-18 18:47:17 +00:00
// animate app grow and shrink
2021-10-24 04:16:38 +00:00
NumberAnimation on yOffset {
2021-10-18 18:47:17 +00:00
id: offsetAnimator
duration: PlasmaCore.Units.longDuration
easing.type: Easing.InOutQuad
property bool activateApp: true
// states of to:
// 0 - open/resume app (zoom up the thumbnail)
2021-10-24 04:16:38 +00:00
// window.targetYOffsetDist - animate shrinking of thumbnail, to listview (open task switcher)
2021-10-18 18:47:17 +00:00
to: 0
onFinished: {
if (to === 0) { // close task switcher, and switch to current app
if (!window.visible) return;
window.visible = false;
if (activateApp) {
setSingleActiveWindow(window.currentTaskIndex);
}
activateApp = true;
2021-10-24 04:16:38 +00:00
} else if (to == window.dismissYOffsetDist) {
window.hide();
2021-10-18 18:47:17 +00:00
}
}
}
2021-10-18 03:50:59 +00:00
2021-10-31 19:21:02 +00:00
Item {
id: container
2021-10-18 03:50:59 +00:00
2021-10-31 19:21:02 +00:00
// provide shell margins
anchors.fill: parent
anchors.rightMargin: navPanel.isPortrait ? 0 : window.taskPanelHeight
anchors.bottomMargin: navPanel.isPortrait ? window.taskPanelHeight : 0
anchors.topMargin: MobileShell.TopPanelControls.panelHeight
2021-10-18 03:50:59 +00:00
2021-10-31 19:21:02 +00:00
// applications list
ListView {
id: tasksView
opacity: window.wasInActiveTask ? 1 : Math.min(1, window.yOffset / window.targetYOffsetDist)
anchors.centerIn: parent
readonly property real sizeFactor: 0.75
readonly property real taskHeaderHeight: PlasmaCore.Units.gridUnit * 2 + PlasmaCore.Units.smallSpacing * 2
width: window.windowWidth * sizeFactor
height: window.windowHeight * sizeFactor + taskHeaderHeight
model: window.model
orientation: ListView.Horizontal
2021-10-18 03:50:59 +00:00
2021-10-31 19:21:02 +00:00
highlightRangeMode: ListView.StrictlyEnforceRange // ensures currentIndex is updated
snapMode: ListView.SnapToItem
spacing: PlasmaCore.Units.largeSpacing
displayMarginBeginning: 2 * (width + spacing)
displayMarginEnd: 2 * (width + spacing)
displaced: Transition {
NumberAnimation { properties: "x,y"; duration: PlasmaCore.Units.longDuration; easing.type: Easing.InOutQuad }
2015-06-21 19:26:14 +00:00
}
2021-10-31 04:11:10 +00:00
2021-10-31 19:21:02 +00:00
// ensure that window previews are exactly to the scale of the device screen
property real scalingFactor: {
let candidateHeight = (tasksView.width / window.windowWidth) * window.windowHeight;
if (candidateHeight > tasksView.height) {
return tasksView.height / window.windowHeight;
} else {
return tasksView.width / window.windowWidth;
}
}
2021-10-31 04:11:10 +00:00
2021-10-31 19:21:02 +00:00
delegate: Task {
id: task
property int curIndex: model.index
z: window.currentTaskIndex === curIndex ? 1 : 0
width: tasksView.width
height: tasksView.height
displaysModel: window.displaysModel
2021-10-31 19:21:02 +00:00
// account for header offset (center the preview)
y: -tasksView.taskHeaderHeight / 2
2021-10-31 04:11:10 +00:00
2021-10-31 19:21:02 +00:00
// scale gesture
scale: {
let maxScale = 1 / tasksView.scalingFactor;
let subtract = (maxScale - 1) * (window.yOffset / window.targetYOffsetDist);
let finalScale = Math.max(0, Math.min(maxScale, maxScale - subtract));
if ((window.wasInActiveTask || !taskSwitcher.currentlyDragging) && window.currentTaskIndex === task.curIndex) {
return finalScale;
}
return 1;
2021-10-31 04:11:10 +00:00
}
2021-10-31 19:21:02 +00:00
// ensure that window previews are exactly to the scale of the device screen
previewWidth: tasksView.scalingFactor * window.windowWidth
previewHeight: tasksView.scalingFactor * window.windowHeight
2021-10-31 04:11:10 +00:00
}
2020-02-04 19:10:01 +00:00
}
2015-06-18 23:31:26 +00:00
}
2021-10-18 03:50:59 +00:00
2021-10-31 04:11:10 +00:00
// top panel swipe down gesture
MouseArea {
anchors.top: parent.top
2021-10-18 03:50:59 +00:00
anchors.left: parent.left
anchors.right: parent.right
2021-10-31 04:11:10 +00:00
height: MobileShell.TopPanelControls.panelHeight
property int oldMouseY: 0
onPositionChanged: {
MobileShell.TopPanelControls.requestRelativeScroll(mouse.y - oldMouseY);
oldMouseY = mouse.y;
}
onPressed: {
oldMouseY = mouse.y;
MobileShell.TopPanelControls.startSwipe();
}
onReleased: MobileShell.TopPanelControls.endSwipe();
}
// task panel
MobileShell.NavigationPanel {
2021-10-31 04:11:10 +00:00
id: navPanel
property bool isPortrait: Screen.width <= Screen.height
width: isPortrait ? implicitWidth : window.taskPanelHeight
height: isPortrait ? window.taskPanelHeight : implicitWidth
anchors.left: isPortrait ? parent.left : undefined
anchors.right: parent.right
anchors.top: isPortrait ? undefined: parent.top
2021-10-18 03:50:59 +00:00
anchors.bottom: parent.bottom
2021-10-31 04:11:10 +00:00
taskSwitcher: window
backgroundColor: window.visible ? Qt.rgba(0, 0, 0, 0.1) : "transparent"
foregroundColorGroup: PlasmaCore.Theme.ComplementaryColorGroup
dragGestureEnabled: false
Behavior on backgroundColor { ColorAnimation {} }
2021-10-18 03:50:59 +00:00
leftAction: MobileShell.NavigationPanelAction {
2021-10-31 04:11:10 +00:00
enabled: true
iconSource: "mobile-task-switcher"
iconSizeFactor: 0.75
onTriggered: {
if (window.wasInActiveTask) {
window.activateWindow(window.currentTaskIndex);
} else {
window.hide();
}
}
}
2021-10-18 03:50:59 +00:00
middleAction: MobileShell.NavigationPanelAction {
2021-10-31 04:11:10 +00:00
enabled: true
iconSource: "start-here-kde"
iconSizeFactor: 1
onTriggered: {
window.hide();
root.triggerHomescreen();
}
2021-10-18 03:50:59 +00:00
}
rightAction: MobileShell.NavigationPanelAction {
2021-10-31 04:11:10 +00:00
enabled: true
iconSource: "mobile-close-app"
iconSizeFactor: 0.75
onTriggered: {
tasksModel.requestClose(tasksModel.index(window.currentTaskIndex, 0));
}
}
}
2015-06-18 23:31:26 +00:00
}