2023-03-06 06:38:43 +00:00
|
|
|
// 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
|
|
|
|
|
|
2023-03-19 06:46:05 +00:00
|
|
|
import QtQuick
|
|
|
|
|
import QtQuick.Layouts
|
2023-03-06 06:38:43 +00:00
|
|
|
|
2023-07-25 01:13:52 +00:00
|
|
|
import org.kde.kirigami 2.20 as Kirigami
|
2023-03-19 06:46:05 +00:00
|
|
|
import org.kde.plasma.core as PlasmaCore
|
2023-03-06 06:38:43 +00:00
|
|
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
2023-03-19 06:46:05 +00:00
|
|
|
import org.kde.plasma.private.mobileshell as MobileShell
|
2023-03-25 06:29:40 +00:00
|
|
|
import org.kde.plasma.private.mobileshell.state as MobileShellState
|
2023-04-01 05:10:02 +00:00
|
|
|
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
2023-03-06 06:38:43 +00:00
|
|
|
|
|
|
|
|
import org.kde.kwin 3.0 as KWinComponents
|
|
|
|
|
import org.kde.kwin.private.effects 1.0
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Component that provides a task switcher.
|
|
|
|
|
*/
|
|
|
|
|
FocusScope {
|
|
|
|
|
id: root
|
|
|
|
|
focus: true
|
|
|
|
|
|
|
|
|
|
readonly property QtObject effect: KWinComponents.SceneView.effect
|
|
|
|
|
readonly property QtObject targetScreen: KWinComponents.SceneView.screen
|
|
|
|
|
|
2023-04-01 05:10:02 +00:00
|
|
|
readonly property bool inLandscape: width > height;
|
|
|
|
|
readonly property bool isInLandscapeNavPanelMode: inLandscape && ShellSettings.Settings.navigationPanelEnabled
|
|
|
|
|
|
2023-03-19 06:46:05 +00:00
|
|
|
readonly property real topMargin: MobileShell.Constants.topPanelHeight
|
2023-04-01 05:10:02 +00:00
|
|
|
readonly property real bottomMargin: isInLandscapeNavPanelMode ? 0 : MobileShell.Constants.bottomPanelHeight
|
2023-03-06 06:38:43 +00:00
|
|
|
readonly property real leftMargin: 0
|
2023-04-01 05:10:02 +00:00
|
|
|
readonly property real rightMargin: isInLandscapeNavPanelMode ? MobileShell.Constants.bottomPanelHeight : 0
|
2023-03-06 06:38:43 +00:00
|
|
|
|
|
|
|
|
property var taskSwitcherState: TaskSwitcherState {
|
|
|
|
|
taskSwitcher: root
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KWinComponents.WindowModel {
|
|
|
|
|
id: stackModel
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KWinComponents.VirtualDesktopModel {
|
|
|
|
|
id: desktopModel
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
property var tasksModel: KWinComponents.WindowFilterModel {
|
|
|
|
|
activity: KWinComponents.Workspace.currentActivity
|
|
|
|
|
desktop: KWinComponents.Workspace.currentDesktop
|
|
|
|
|
screenName: root.targetScreen.name
|
|
|
|
|
windowModel: stackModel
|
|
|
|
|
minimizedWindows: true
|
|
|
|
|
windowType: ~KWinComponents.WindowFilterModel.Dock &
|
|
|
|
|
~KWinComponents.WindowFilterModel.Desktop &
|
|
|
|
|
~KWinComponents.WindowFilterModel.Notification &
|
|
|
|
|
~KWinComponents.WindowFilterModel.CriticalNotification
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly property int tasksCount: taskList.count
|
|
|
|
|
|
|
|
|
|
// keep track of task list events
|
|
|
|
|
property int oldTasksCount: tasksCount
|
|
|
|
|
onTasksCountChanged: {
|
|
|
|
|
if (tasksCount === 0 && oldTasksCount !== 0) {
|
|
|
|
|
hide();
|
|
|
|
|
} else if (tasksCount < oldTasksCount && taskSwitcherState.currentTaskIndex >= tasksCount - 1) {
|
|
|
|
|
// if the user is on the last task, and it is closed, scroll left
|
2023-07-25 01:13:52 +00:00
|
|
|
taskSwitcherState.animateGoToTaskIndex(tasksCount - 1, Kirigami.Units.longDuration);
|
2023-03-06 06:38:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
oldTasksCount = tasksCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Keys.onEscapePressed: hide();
|
|
|
|
|
|
|
|
|
|
Component.onCompleted: {
|
|
|
|
|
taskList.jumpToFirstVisibleWindow();
|
|
|
|
|
taskList.minimizeAll();
|
|
|
|
|
|
|
|
|
|
// fully open the panel (if this is a button press, not gesture)
|
|
|
|
|
if (!root.effect.gestureInProgress) {
|
|
|
|
|
taskSwitcherState.open();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// called by c++ plugin
|
|
|
|
|
function hideAnimation() {
|
|
|
|
|
closeAnim.restart();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function instantHide() {
|
|
|
|
|
root.effect.deactivate(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function hide() {
|
|
|
|
|
root.effect.deactivate(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// scroll to delegate index, and activate it
|
|
|
|
|
function activateWindow(index, window) {
|
|
|
|
|
KWinComponents.Workspace.activeClient = window;
|
|
|
|
|
taskSwitcherState.openApp(index, window);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Connections {
|
|
|
|
|
target: root.effect
|
|
|
|
|
|
|
|
|
|
function onPartialActivationFactorChanged() {
|
|
|
|
|
taskSwitcherState.positionY = taskSwitcherState.openedYPosition * root.effect.partialActivationFactor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function onGestureInProgressChanged() {
|
|
|
|
|
if (!root.effect.gestureInProgress) {
|
|
|
|
|
taskSwitcherState.updateState();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-01 05:10:02 +00:00
|
|
|
// view of the desktop background
|
2023-03-06 06:38:43 +00:00
|
|
|
KWinComponents.DesktopBackground {
|
|
|
|
|
id: backgroundItem
|
|
|
|
|
activity: KWinComponents.Workspace.currentActivity
|
|
|
|
|
desktop: KWinComponents.Workspace.currentDesktop
|
|
|
|
|
outputName: targetScreen.name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// background colour
|
|
|
|
|
Rectangle {
|
|
|
|
|
id: backgroundRect
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
|
|
|
|
|
opacity: container.opacity
|
|
|
|
|
color: {
|
|
|
|
|
// animate background colour only if we are *not* opening from the homescreen
|
|
|
|
|
if (taskSwitcherState.wasInActiveTask || !taskSwitcherState.currentlyBeingOpened) {
|
|
|
|
|
return Qt.rgba(0, 0, 0, 0.6);
|
|
|
|
|
} else {
|
|
|
|
|
return Qt.rgba(0, 0, 0, 0.6 * Math.min(1, taskSwitcherState.yPosition / taskSwitcherState.openedYPosition));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-01 05:10:02 +00:00
|
|
|
// status bar
|
2023-04-11 04:12:31 +00:00
|
|
|
// TODO: improve load times, it is quite slow
|
|
|
|
|
// MobileShell.StatusBar {
|
|
|
|
|
// id: statusBar
|
|
|
|
|
// z: 1
|
2023-07-25 01:13:52 +00:00
|
|
|
// colorGroup: Kirigami.Theme.ComplementaryColorGroup
|
2023-04-11 04:12:31 +00:00
|
|
|
// backgroundColor: "transparent"
|
|
|
|
|
//
|
|
|
|
|
// height: root.topMargin
|
|
|
|
|
// anchors.top: parent.top
|
|
|
|
|
// anchors.left: parent.left
|
|
|
|
|
// anchors.right: parent.right
|
|
|
|
|
// }
|
2023-04-01 05:10:02 +00:00
|
|
|
|
|
|
|
|
// navigation panel
|
|
|
|
|
MobileShell.NavigationPanel {
|
|
|
|
|
id: navigationPanel
|
|
|
|
|
z: 1
|
|
|
|
|
visible: ShellSettings.Settings.navigationPanelEnabled
|
|
|
|
|
backgroundColor: Qt.rgba(0, 0, 0, 0.1)
|
2023-07-25 02:24:10 +00:00
|
|
|
foregroundColorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
2023-04-01 05:10:02 +00:00
|
|
|
shadow: false
|
|
|
|
|
|
|
|
|
|
leftAction: MobileShell.NavigationPanelAction {
|
|
|
|
|
enabled: true
|
|
|
|
|
iconSource: "mobile-task-switcher"
|
|
|
|
|
iconSizeFactor: 0.75
|
|
|
|
|
|
|
|
|
|
onTriggered: {
|
|
|
|
|
if (taskList.count === 0) {
|
|
|
|
|
root.hide();
|
|
|
|
|
} else {
|
|
|
|
|
const currentIndex = taskSwitcherState.currentTaskIndex;
|
|
|
|
|
taskSwitcherState.openApp(taskSwitcherState.currentTaskIndex, taskList.getTaskAt(currentIndex).window);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// home button
|
|
|
|
|
middleAction: MobileShell.NavigationPanelAction {
|
|
|
|
|
enabled: true
|
|
|
|
|
iconSource: "start-here-kde"
|
|
|
|
|
iconSizeFactor: 1
|
|
|
|
|
onTriggered: root.hide()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// close app/keyboard button
|
|
|
|
|
rightAction: MobileShell.NavigationPanelAction {
|
|
|
|
|
enabled: true
|
|
|
|
|
iconSource: "mobile-close-app"
|
|
|
|
|
iconSizeFactor: 0.75
|
|
|
|
|
|
|
|
|
|
onTriggered: {
|
|
|
|
|
taskList.getTaskAt(taskSwitcherState.currentTaskIndex).closeApp();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rightCornerAction: MobileShell.NavigationPanelAction {
|
|
|
|
|
visible: false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
states: [
|
|
|
|
|
State {
|
|
|
|
|
name: "landscape"
|
|
|
|
|
when: root.width > root.height
|
|
|
|
|
AnchorChanges {
|
|
|
|
|
target: navigationPanel
|
|
|
|
|
anchors {
|
|
|
|
|
right: root.right
|
|
|
|
|
top: root.top
|
|
|
|
|
bottom: root.bottom
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
PropertyChanges {
|
|
|
|
|
target: navigationPanel
|
|
|
|
|
width: root.rightMargin
|
|
|
|
|
anchors.topMargin: root.topMargin
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
State {
|
|
|
|
|
name: "portrait"
|
|
|
|
|
when: root.width <= root.height
|
|
|
|
|
AnchorChanges {
|
|
|
|
|
target: navigationPanel
|
|
|
|
|
anchors {
|
|
|
|
|
right: root.right
|
|
|
|
|
left: root.left
|
|
|
|
|
bottom: root.bottom
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
PropertyChanges {
|
|
|
|
|
target: navigationPanel
|
|
|
|
|
height: root.bottomMargin
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
// task list
|
2023-03-06 06:38:43 +00:00
|
|
|
Item {
|
|
|
|
|
id: container
|
|
|
|
|
|
|
|
|
|
// provide shell margins
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
anchors.leftMargin: root.leftMargin
|
|
|
|
|
anchors.rightMargin: root.rightMargin
|
|
|
|
|
anchors.bottomMargin: root.bottomMargin
|
|
|
|
|
anchors.topMargin: root.topMargin
|
|
|
|
|
|
|
|
|
|
NumberAnimation on opacity {
|
|
|
|
|
id: closeAnim
|
|
|
|
|
running: false
|
|
|
|
|
to: 0
|
|
|
|
|
duration: 200
|
|
|
|
|
easing.type: Easing.InOutQuad
|
|
|
|
|
|
|
|
|
|
onFinished: {
|
|
|
|
|
closeAllButton.closeRequested = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// placeholder message
|
|
|
|
|
ColumnLayout {
|
|
|
|
|
id: placeholder
|
2023-07-25 01:13:52 +00:00
|
|
|
spacing: Kirigami.Units.gridUnit
|
2023-03-06 06:38:43 +00:00
|
|
|
opacity: (root.tasksCount === 0 && !taskSwitcherState.currentlyBeingClosed) ? 0.9 : 0
|
|
|
|
|
Behavior on opacity { NumberAnimation { duration: 500 } }
|
|
|
|
|
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
|
2023-03-19 06:46:05 +00:00
|
|
|
PlasmaCore.IconItem {
|
2023-03-06 06:38:43 +00:00
|
|
|
id: icon
|
|
|
|
|
Layout.alignment: Qt.AlignHCenter
|
2023-07-25 01:13:52 +00:00
|
|
|
implicitWidth: Kirigami.Units.iconSizes.large
|
|
|
|
|
implicitHeight: Kirigami.Units.iconSizes.large
|
2023-03-06 06:38:43 +00:00
|
|
|
source: "window"
|
2023-03-19 06:46:05 +00:00
|
|
|
// color: "white"
|
2023-03-06 06:38:43 +00:00
|
|
|
}
|
|
|
|
|
|
2023-07-25 01:13:52 +00:00
|
|
|
Kirigami.Heading {
|
2023-03-06 06:38:43 +00:00
|
|
|
Layout.fillWidth: true
|
|
|
|
|
Layout.maximumWidth: root.width * 0.75
|
|
|
|
|
Layout.alignment: Qt.AlignHCenter
|
|
|
|
|
color: "white"
|
|
|
|
|
level: 3
|
|
|
|
|
wrapMode: Text.Wrap
|
|
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
|
text: i18n("No applications are running.")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// flicking area for task switcher
|
|
|
|
|
FlickContainer {
|
|
|
|
|
id: flickable
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
|
|
|
|
|
taskSwitcherState: root.taskSwitcherState
|
|
|
|
|
|
2023-04-01 03:32:56 +00:00
|
|
|
// don't allow FlickContainer to steal from swiping on tasks
|
|
|
|
|
interactive: taskList.taskInteractingCount === 0
|
|
|
|
|
|
2023-03-06 06:38:43 +00:00
|
|
|
// the item is effectively anchored to the flickable bounds
|
|
|
|
|
TaskList {
|
|
|
|
|
id: taskList
|
|
|
|
|
taskSwitcher: root
|
|
|
|
|
shellTopMargin: root.topMargin
|
|
|
|
|
shellBottomMargin: root.bottomMargin
|
|
|
|
|
|
|
|
|
|
opacity: {
|
|
|
|
|
// animate opacity only if we are *not* opening from the homescreen
|
|
|
|
|
if (taskSwitcherState.wasInActiveTask || !taskSwitcherState.currentlyBeingOpened) {
|
|
|
|
|
return 1;
|
|
|
|
|
} else {
|
|
|
|
|
return Math.min(1, taskSwitcherState.yPosition / taskSwitcherState.openedYPosition);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x: flickable.contentX
|
|
|
|
|
width: flickable.width
|
|
|
|
|
height: flickable.height
|
|
|
|
|
|
|
|
|
|
PlasmaComponents.ToolButton {
|
|
|
|
|
id: closeAllButton
|
|
|
|
|
property bool closeRequested: false
|
|
|
|
|
visible: root.tasksCount !== 0
|
|
|
|
|
|
|
|
|
|
anchors {
|
|
|
|
|
bottom: parent.bottom
|
|
|
|
|
bottomMargin: taskList.taskY / 2
|
|
|
|
|
horizontalCenter: parent.horizontalCenter
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-25 01:13:52 +00:00
|
|
|
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
|
|
|
|
|
Kirigami.Theme.inherit: false
|
2023-03-06 06:38:43 +00:00
|
|
|
|
|
|
|
|
opacity: (taskSwitcherState.currentlyBeingOpened || taskSwitcherState.currentlyBeingClosed) ? 0.0 : 1.0
|
2023-07-25 01:13:52 +00:00
|
|
|
Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration } }
|
2023-03-06 06:38:43 +00:00
|
|
|
|
|
|
|
|
icon.name: "edit-clear-history"
|
|
|
|
|
font.bold: true
|
|
|
|
|
|
|
|
|
|
text: closeRequested ? i18n("Confirm Close All") : i18n("Close All")
|
|
|
|
|
|
|
|
|
|
onClicked: {
|
|
|
|
|
if (closeRequested) {
|
|
|
|
|
taskList.closeAll();
|
|
|
|
|
} else {
|
|
|
|
|
closeRequested = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|