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

0001 // SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
0002 // SPDX-FileCopyrightText: 2021-2023 Devin Lin <devin@kde.org>
0003 // SPDX-License-Identifier: GPL-2.0-or-later
0004 
0005 import QtQuick
0006 import QtQuick.Layouts
0007 
0008 import org.kde.kirigami 2.20 as Kirigami
0009 import org.kde.plasma.core as PlasmaCore
0010 import org.kde.plasma.components 3.0 as PlasmaComponents
0011 import org.kde.plasma.private.mobileshell as MobileShell
0012 import org.kde.plasma.private.mobileshell.state as MobileShellState
0013 import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
0014 
0015 import org.kde.kwin 3.0 as KWinComponents
0016 import org.kde.kwin.private.effects 1.0
0017 
0018 /**
0019  * Component that provides a task switcher.
0020  */
0021 FocusScope {
0022     id: root
0023     focus: true
0024 
0025     readonly property QtObject effect: KWinComponents.SceneView.effect
0026     readonly property QtObject targetScreen: KWinComponents.SceneView.screen
0027 
0028     readonly property bool inLandscape: width > height;
0029     readonly property bool isInLandscapeNavPanelMode: inLandscape && ShellSettings.Settings.navigationPanelEnabled
0030 
0031     readonly property real topMargin: MobileShell.Constants.topPanelHeight
0032     readonly property real bottomMargin: isInLandscapeNavPanelMode ? 0 : MobileShell.Constants.bottomPanelHeight
0033     readonly property real leftMargin: 0
0034     readonly property real rightMargin: isInLandscapeNavPanelMode ? MobileShell.Constants.bottomPanelHeight : 0
0035 
0036     property var taskSwitcherState: TaskSwitcherState {
0037         taskSwitcher: root
0038     }
0039 
0040     KWinComponents.WindowModel {
0041         id: stackModel
0042     }
0043 
0044     KWinComponents.VirtualDesktopModel {
0045         id: desktopModel
0046     }
0047 
0048     property var tasksModel: KWinComponents.WindowFilterModel {
0049         activity: KWinComponents.Workspace.currentActivity
0050         desktop: KWinComponents.Workspace.currentDesktop
0051         screenName: root.targetScreen.name
0052         windowModel: stackModel
0053         minimizedWindows: true
0054         windowType: ~KWinComponents.WindowFilterModel.Dock &
0055                     ~KWinComponents.WindowFilterModel.Desktop &
0056                     ~KWinComponents.WindowFilterModel.Notification &
0057                     ~KWinComponents.WindowFilterModel.CriticalNotification
0058     }
0059 
0060     readonly property int tasksCount: taskList.count
0061 
0062     // keep track of task list events
0063     property int oldTasksCount: tasksCount
0064     onTasksCountChanged: {
0065         if (tasksCount === 0 && oldTasksCount !== 0) {
0066             hide();
0067         } else if (tasksCount < oldTasksCount && taskSwitcherState.currentTaskIndex >= tasksCount - 1) {
0068             // if the user is on the last task, and it is closed, scroll left
0069             taskSwitcherState.animateGoToTaskIndex(tasksCount - 1, Kirigami.Units.longDuration);
0070         }
0071 
0072         oldTasksCount = tasksCount;
0073     }
0074 
0075     Keys.onEscapePressed: hide();
0076 
0077     Component.onCompleted: {
0078         taskList.jumpToFirstVisibleWindow();
0079         taskList.minimizeAll();
0080 
0081         // fully open the panel (if this is a button press, not gesture)
0082         if (!root.effect.gestureInProgress) {
0083             taskSwitcherState.open();
0084         }
0085     }
0086 
0087     // called by c++ plugin
0088     function hideAnimation() {
0089         closeAnim.restart();
0090     }
0091 
0092     function instantHide() {
0093         root.effect.deactivate(true);
0094     }
0095 
0096     function hide() {
0097         root.effect.deactivate(false);
0098     }
0099 
0100     // scroll to delegate index, and activate it
0101     function activateWindow(index, window) {
0102         KWinComponents.Workspace.activeWindow = window;
0103         taskSwitcherState.openApp(index, window);
0104     }
0105 
0106     Connections {
0107         target: root.effect
0108 
0109         function onPartialActivationFactorChanged() {
0110             taskSwitcherState.yPosition = taskSwitcherState.openedYPosition * root.effect.partialActivationFactor;
0111         }
0112 
0113         function onGestureInProgressChanged() {
0114             if (!root.effect.gestureInProgress) {
0115                 taskSwitcherState.updateState();
0116             }
0117         }
0118     }
0119 
0120     // view of the desktop background
0121     KWinComponents.DesktopBackground {
0122         id: backgroundItem
0123         activity: KWinComponents.Workspace.currentActivity
0124         desktop: KWinComponents.Workspace.currentDesktop
0125         outputName: targetScreen.name
0126     }
0127 
0128     // background colour
0129     Rectangle {
0130         id: backgroundRect
0131         anchors.fill: parent
0132 
0133         opacity: container.opacity
0134         color: {
0135             // animate background colour only if we are *not* opening from the homescreen
0136             if (taskSwitcherState.wasInActiveTask || !taskSwitcherState.currentlyBeingOpened) {
0137                 return Qt.rgba(0, 0, 0, 0.6);
0138             } else {
0139                 return Qt.rgba(0, 0, 0, 0.6 * Math.min(1, taskSwitcherState.yPosition / taskSwitcherState.openedYPosition));
0140             }
0141         }
0142     }
0143 
0144     // status bar
0145     // TODO: improve load times, it is quite slow
0146     // MobileShell.StatusBar {
0147     //     id: statusBar
0148     //     z: 1
0149     //     colorGroup: Kirigami.Theme.ComplementaryColorGroup
0150     //     backgroundColor: "transparent"
0151     //
0152     //     height: root.topMargin
0153     //     anchors.top: parent.top
0154     //     anchors.left: parent.left
0155     //     anchors.right: parent.right
0156     // }
0157 
0158     // navigation panel
0159     MobileShell.NavigationPanel {
0160         id: navigationPanel
0161         z: 1
0162         visible: ShellSettings.Settings.navigationPanelEnabled
0163         backgroundColor: Qt.rgba(0, 0, 0, 0.1)
0164         foregroundColorGroup: Kirigami.Theme.Complementary
0165         shadow: false
0166 
0167         isVertical: MobileShell.Constants.navigationPanelOnSide(root.width, root.height)
0168 
0169         leftAction: MobileShell.NavigationPanelAction {
0170             enabled: true
0171             iconSource: "mobile-task-switcher"
0172             iconSizeFactor: 0.75
0173 
0174             onTriggered: {
0175                 if (taskList.count === 0) {
0176                     root.hide();
0177                 } else {
0178                     const currentIndex = taskSwitcherState.currentTaskIndex;
0179                     taskSwitcherState.openApp(taskSwitcherState.currentTaskIndex, taskList.getTaskAt(currentIndex).window);
0180                 }
0181             }
0182         }
0183 
0184         // home button
0185         middleAction: MobileShell.NavigationPanelAction {
0186             enabled: true
0187             iconSource: "start-here-kde"
0188             iconSizeFactor: 1
0189             onTriggered: root.hide()
0190         }
0191 
0192         // close app/keyboard button
0193         rightAction: MobileShell.NavigationPanelAction {
0194             enabled: true
0195             iconSource: "mobile-close-app"
0196             iconSizeFactor: 0.75
0197 
0198             onTriggered: {
0199                 taskList.getTaskAt(taskSwitcherState.currentTaskIndex).closeApp();
0200             }
0201         }
0202 
0203         rightCornerAction: MobileShell.NavigationPanelAction {
0204             visible: false
0205         }
0206     }
0207 
0208     states: [
0209         State {
0210             name: "landscape"
0211             when: MobileShell.Constants.navigationPanelOnSide(root.width, root.height)
0212             AnchorChanges {
0213                 target: navigationPanel
0214                 anchors {
0215                     right: root.right
0216                     top: root.top
0217                     bottom: root.bottom
0218                 }
0219             }
0220             PropertyChanges {
0221                 target: navigationPanel
0222                 width: root.rightMargin
0223                 anchors.topMargin: root.topMargin
0224             }
0225         },
0226         State {
0227             name: "portrait"
0228             when: !MobileShell.Constants.navigationPanelOnSide(root.width, root.height)
0229             AnchorChanges {
0230                 target: navigationPanel
0231                 anchors {
0232                     right: root.right
0233                     left: root.left
0234                     bottom: root.bottom
0235                 }
0236             }
0237             PropertyChanges {
0238                 target: navigationPanel
0239                 height: root.bottomMargin
0240             }
0241         }
0242     ]
0243 
0244     // task list
0245     Item {
0246         id: container
0247 
0248         // provide shell margins
0249         anchors.fill: parent
0250         anchors.leftMargin: root.leftMargin
0251         anchors.rightMargin: root.rightMargin
0252         anchors.bottomMargin: root.bottomMargin
0253         anchors.topMargin: root.topMargin
0254 
0255         NumberAnimation on opacity {
0256             id: closeAnim
0257             running: false
0258             to: 0
0259             duration: 200
0260             easing.type: Easing.InOutQuad
0261 
0262             onFinished: {
0263                 closeAllButton.closeRequested = false;
0264             }
0265         }
0266 
0267         // placeholder message
0268         ColumnLayout {
0269             id: placeholder
0270             spacing: Kirigami.Units.gridUnit
0271             opacity: (root.tasksCount === 0 && !taskSwitcherState.currentlyBeingClosed) ? 0.9 : 0
0272             Behavior on opacity { NumberAnimation { duration: 500 } }
0273 
0274             anchors.centerIn: parent
0275 
0276             Kirigami.Icon {
0277                 id: icon
0278                 Layout.alignment: Qt.AlignHCenter
0279                 implicitWidth: Kirigami.Units.iconSizes.large
0280                 implicitHeight: Kirigami.Units.iconSizes.large
0281                 source: "window"
0282                 color: "white"
0283             }
0284 
0285             Kirigami.Heading {
0286                 Layout.fillWidth: true
0287                 Layout.maximumWidth: root.width * 0.75
0288                 Layout.alignment: Qt.AlignHCenter
0289                 color: "white"
0290                 level: 3
0291                 wrapMode: Text.Wrap
0292                 horizontalAlignment: Text.AlignHCenter
0293                 text: i18n("No applications are running.")
0294             }
0295         }
0296 
0297         // flicking area for task switcher
0298         FlickContainer {
0299             id: flickable
0300             anchors.fill: parent
0301 
0302             taskSwitcherState: root.taskSwitcherState
0303 
0304             // don't allow FlickContainer to steal from swiping on tasks
0305             interactive: taskList.taskInteractingCount === 0
0306 
0307             // the item is effectively anchored to the flickable bounds
0308             TaskList {
0309                 id: taskList
0310                 taskSwitcher: root
0311                 shellTopMargin: root.topMargin
0312                 shellBottomMargin: root.bottomMargin
0313 
0314                 opacity: {
0315                     // animate opacity only if we are *not* opening from the homescreen
0316                     if (taskSwitcherState.wasInActiveTask || !taskSwitcherState.currentlyBeingOpened) {
0317                         return 1;
0318                     } else {
0319                         return Math.min(1, taskSwitcherState.yPosition / taskSwitcherState.openedYPosition);
0320                     }
0321                 }
0322 
0323                 x: flickable.contentX
0324                 width: flickable.width
0325                 height: flickable.height
0326 
0327                 PlasmaComponents.ToolButton {
0328                     id: closeAllButton
0329                     property bool closeRequested: false
0330                     visible: root.tasksCount !== 0
0331 
0332                     anchors {
0333                         bottom: parent.bottom
0334                         bottomMargin: taskList.taskY / 2
0335                         horizontalCenter: parent.horizontalCenter
0336                     }
0337 
0338                     Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
0339                     Kirigami.Theme.inherit: false
0340 
0341                     opacity: (taskSwitcherState.currentlyBeingOpened || taskSwitcherState.currentlyBeingClosed) ? 0.0 : 1.0
0342                     Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration } }
0343 
0344                     icon.name: "edit-clear-history"
0345                     font.bold: true
0346 
0347                     text: closeRequested ? i18n("Confirm Close All") : i18n("Close All")
0348 
0349                     onClicked: {
0350                         if (closeRequested) {
0351                             taskList.closeAll();
0352                         } else {
0353                             closeRequested = true;
0354                         }
0355                     }
0356                 }
0357             }
0358         }
0359     }
0360 }
0361