Warning, /plasma/plasma-mobile/kwin/mobiletaskswitcher/qml/TaskSwitcherState.qml is written in an unsupported language. File is not indexed.

0001 // SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
0002 // SPDX-License-Identifier: GPL-2.0-or-later
0003 
0004 import QtQuick 2.15
0005 
0006 import org.kde.kirigami 2.20 as Kirigami
0007 
0008 import org.kde.kwin 3.0 as KWinComponents
0009 
0010 /**
0011  * State object for the task switcher.
0012  */
0013 QtObject {
0014     id: root
0015 
0016     // TaskSwitcher item component
0017     // We assume that the taskSwitcher the size of the entire screen.
0018     required property var taskSwitcher
0019 
0020 
0021     // ~~ positioning ~~
0022 
0023     // Position of the list view:
0024     //
0025     // xPosition:
0026     // We start at 0, which is the position at which the first task in the task switcher is centered on the screen.
0027     // Decreasing xPosition results in the task switcher moving forward (to the second task, third task, etc), being the layout direction Right to Left.
0028     //
0029     // yPosition:
0030     // 0 - Start of swipe up gesture, if window was showing, the thumbnail is the size of it
0031     // Increasing yPosition results in the task switcher moving up (and thumbnails shrinking)
0032     property real xPosition: 0
0033     property real yPosition: 0
0034 
0035     // direction of the movement
0036     property bool movingRight: false
0037     property bool movingUp: false
0038 
0039     // used for calculating movement direction
0040     property real oldXPosition: 0
0041     property real oldYPosition: 0
0042     onXPositionChanged: {
0043         movingRight = xPosition < oldXPosition;
0044         oldXPosition = xPosition;
0045     }
0046     onYPositionChanged: {
0047         movingUp = yPosition > oldYPosition;
0048         oldYPosition = yPosition;
0049     }
0050 
0051     // yPosition when the task switcher is completely open
0052     readonly property real openedYPosition: (taskSwitcher.height - taskHeight) / 2
0053 
0054     // ~~ active state ~~
0055 
0056     // whether the user was in an active task before the task switcher was opened
0057     property bool wasInActiveTask: false
0058 
0059     // whether we are in a swipe up gesture to open the task switcher
0060     property bool currentlyBeingOpened: true // assume that task switcher is loaded on open
0061 
0062     // whether the task switcher is being closed: an animation is running
0063     property bool currentlyBeingClosed: false
0064 
0065     // whether we are in a swipe left/right gesture to walk through tasks
0066     property bool scrollingTasks: false
0067 
0068     readonly property int currentTaskIndex: {
0069         let candidateIndex = Math.round(-xPosition / (taskSpacing + taskWidth));
0070         return Math.max(0, Math.min(taskSwitcher.tasksCount - 1, candidateIndex));
0071     }
0072 
0073     // ~~ measurement constants ~~
0074 
0075     // dimensions of a real window on the screen
0076     readonly property real windowHeight: taskSwitcher.height - taskSwitcher.topMargin - taskSwitcher.bottomMargin
0077     readonly property real windowWidth: taskSwitcher.width - taskSwitcher.leftMargin - taskSwitcher.rightMargin
0078 
0079     // dimensions of the task previews
0080     readonly property real previewHeight: windowHeight * scalingFactor
0081     readonly property real previewWidth: windowWidth * scalingFactor
0082     readonly property real taskHeight: previewHeight + taskHeaderHeight
0083     readonly property real taskWidth: previewWidth
0084 
0085     // spacing between each task preview
0086     readonly property real taskSpacing: Kirigami.Units.gridUnit
0087 
0088     // height of the task preview header
0089     readonly property real taskHeaderHeight: Kirigami.Units.gridUnit * 2 + Kirigami.Units.smallSpacing * 2
0090 
0091     // the scaling factor of the window preview compared to the actual window
0092     // we need to ensure that window previews always fit on screen
0093     readonly property real scalingFactor: {
0094         let candidateFactor = 0.6;
0095         let candidateTaskHeight = windowHeight * candidateFactor + taskHeaderHeight;
0096         let candidateTaskWidth = windowWidth * candidateFactor;
0097 
0098         let candidateHeight = (candidateTaskWidth / windowWidth) * windowHeight;
0099         if (candidateHeight > windowHeight) {
0100             return candidateTaskHeight / windowHeight;
0101         } else {
0102             return candidateTaskWidth / windowWidth;
0103         }
0104     }
0105 
0106     // scale of the task list (based on the progress of the swipe up gesture)
0107     readonly property real currentScale: {
0108         let maxScale = 1 / scalingFactor;
0109         let subtract = (maxScale - 1) * (yPosition / openedYPosition);
0110         let finalScale = Math.max(0, Math.min(maxScale, maxScale - subtract));
0111 
0112         // animate scale only if we are *not* opening from the homescreen
0113         if ((wasInActiveTask || !currentlyBeingOpened) && !scrollingTasks) {
0114             return finalScale;
0115         }
0116         return scrollingTasks ? maxScale : 1;
0117     }
0118 
0119     // ~~ signals and functions ~~
0120 
0121     // cancel all animated moving, as another flick source is taking over
0122     signal cancelAnimations()
0123     onCancelAnimations: {
0124         openAnim.stop();
0125         openAppAnim.stop();
0126         closeAnim.stop();
0127         xAnim.stop();
0128     }
0129 
0130     function open() {
0131         openAnim.restart();
0132     }
0133 
0134     function close() {
0135         closeAnim.restart();
0136     }
0137 
0138     function openApp(index, window) {
0139         animateGoToTaskIndex(index, Kirigami.Units.shortDuration);
0140         openAppAnim.restart();
0141         KWinComponents.Workspace.activeWindow = window
0142     }
0143 
0144     // get the xPosition where the task will be centered on the screen
0145     function xPositionFromTaskIndex(index) {
0146         return -index * (taskWidth + taskSpacing);
0147     }
0148 
0149     // instantly go to the task index
0150     function goToTaskIndex(index) {
0151         xPosition = xPositionFromTaskIndex(index);
0152     }
0153 
0154     // go to the task index, animated
0155     function animateGoToTaskIndex(index, duration) {
0156         xAnim.duration = duration;
0157         xAnim.to = xPositionFromTaskIndex(index);
0158         xAnim.restart();
0159     }
0160 
0161     // called after a user finishes an interaction (ex. lets go of the screen)
0162     function updateState() {
0163         cancelAnimations();
0164 
0165         // update vertical state
0166         if ((movingUp || root.yPosition >= openedYPosition) && !scrollingTasks) {
0167             // open task switcher and stay
0168             openAnim.restart();
0169         } else {
0170             // close task switcher and return to app
0171             closeAnim.restart();
0172         }
0173 
0174         // update horizontal state
0175         let duration = Kirigami.Units.longDuration * 2;
0176         if (currentlyBeingOpened) {
0177             animateGoToTaskIndex(currentTaskIndex, duration);
0178         } else {
0179             let currentTaskIndexPosition = xPositionFromTaskIndex(currentTaskIndex);
0180             if (xPosition > currentTaskIndexPosition) {
0181                 if (movingRight) {
0182                     animateGoToTaskIndex(currentTaskIndex, duration);
0183                 } else {
0184                     animateGoToTaskIndex(Math.max(0, currentTaskIndex - 1), duration);
0185                 }
0186             } else {
0187                 if (movingRight) {
0188                     animateGoToTaskIndex(Math.min(taskSwitcher.tasksCount - 1, currentTaskIndex + 1), duration);
0189                 } else {
0190                     animateGoToTaskIndex(currentTaskIndex, duration);
0191                 }
0192             }
0193         }
0194     }
0195 
0196     // ~~ property animators ~~
0197 
0198     property var xAnim: NumberAnimation {
0199         target: root
0200         property: "xPosition"
0201         easing.type: Easing.OutBack
0202     }
0203 
0204     property var openAnim: NumberAnimation {
0205         target: root
0206         property: "yPosition"
0207         to: openedYPosition
0208         duration: 300
0209         easing.type: Easing.OutBack
0210 
0211         onFinished: {
0212             root.currentlyBeingOpened = false;
0213         }
0214     }
0215 
0216     property var closeAnim: NumberAnimation {
0217         target: root
0218         property: "yPosition"
0219         to: 0
0220         duration: Kirigami.Units.longDuration
0221         easing.type: Easing.InOutQuad
0222 
0223         onStarted: root.currentlyBeingClosed = true
0224 
0225         onFinished: {
0226             root.currentlyBeingClosed = false;
0227             root.currentlyBeingOpened = false;
0228             scrollingTasks = false;
0229             taskSwitcher.instantHide();
0230         }
0231     }
0232 
0233     property var openAppAnim: NumberAnimation {
0234         target: root
0235         property: "yPosition"
0236         to: 0
0237         duration: 300
0238         easing.type: Easing.OutQuint
0239 
0240         onStarted: root.currentlyBeingClosed = true
0241 
0242         onFinished: {
0243             root.currentlyBeingClosed = false;
0244             root.currentlyBeingOpened = false;
0245             taskSwitcher.instantHide();
0246         }
0247     }
0248 }