mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-08-03 18:24:46 +00:00
Revamp task switcher
This commit is contained in:
parent
25e0f99f43
commit
90138d5442
6 changed files with 352 additions and 380 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2020 Marco Martin <mart@kde.org>
|
* SPDX-FileCopyrightText: 2020 Marco Martin <mart@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
@ -15,6 +16,7 @@ QtObject {
|
||||||
signal resetHomeScreenPosition()
|
signal resetHomeScreenPosition()
|
||||||
signal snapHomeScreenPosition()
|
signal snapHomeScreenPosition()
|
||||||
signal requestRelativeScroll(point pos)
|
signal requestRelativeScroll(point pos)
|
||||||
|
signal setHomeScreenOpacity(int opacity)
|
||||||
property Item homeScreen
|
property Item homeScreen
|
||||||
property QtObject homeScreenWindow
|
property QtObject homeScreenWindow
|
||||||
property bool homeScreenVisible: true
|
property bool homeScreenVisible: true
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,17 @@ FocusScope {
|
||||||
lastRequestedPosition = pos.y;
|
lastRequestedPosition = pos.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
opacity: 1
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation { duration: PlasmaCore.Units.shortDuration }
|
||||||
|
}
|
||||||
|
Connections {
|
||||||
|
target: MobileShell.HomeScreenControls
|
||||||
|
function onSetHomeScreenOpacity(opacity) {
|
||||||
|
root.opacity = opacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//END API implementation
|
//END API implementation
|
||||||
|
|
||||||
property bool componentComplete: false
|
property bool componentComplete: false
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
|
* SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.0
|
import QtQuick 2.15
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.1
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
import QtQuick.Controls 2.2 as QQC2
|
import QtQuick.Controls 2.2 as QQC2
|
||||||
|
|
@ -15,32 +16,25 @@ import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: delegate
|
id: delegate
|
||||||
width: window.width/2
|
|
||||||
height: window.height/2
|
|
||||||
|
|
||||||
//Workaround
|
|
||||||
required property var model
|
required property var model
|
||||||
property bool active: model.IsActive
|
|
||||||
readonly property point taskScreenPoint: Qt.point(model.ScreenGeometry.x, model.ScreenGeometry.y)
|
|
||||||
onActiveChanged: {
|
|
||||||
//sometimes the task switcher window itself appears, screwing up the state
|
|
||||||
if (model.IsActive) {
|
|
||||||
// window.currentTaskIndex = index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
readonly property point taskScreenPoint: Qt.point(model.ScreenGeometry.x, model.ScreenGeometry.y)
|
||||||
|
readonly property real dragOffset: -control.y
|
||||||
|
|
||||||
|
property bool active: model.IsActive
|
||||||
|
|
||||||
|
required property real previewHeight
|
||||||
|
required property real previewWidth
|
||||||
|
property real scale: 1
|
||||||
|
|
||||||
|
opacity: 1 - dragOffset / window.height
|
||||||
|
|
||||||
|
Component.onCompleted: syncDelegateGeometry();
|
||||||
function syncDelegateGeometry() {
|
function syncDelegateGeometry() {
|
||||||
let pos = pipeWireLoader.mapToItem(tasksView, 0, 0);
|
let pos = pipeWireLoader.mapToItem(tasksView, 0, 0);
|
||||||
if (window.visible) {
|
if (window.visible) {
|
||||||
tasksModel.requestPublishDelegateGeometry(tasksModel.index(model.index, 0), Qt.rect(pos.x, pos.y, pipeWireLoader.width, pipeWireLoader.height), pipeWireLoader);
|
tasksModel.requestPublishDelegateGeometry(tasksModel.index(model.index, 0), Qt.rect(pos.x, pos.y, pipeWireLoader.width, pipeWireLoader.height), pipeWireLoader);
|
||||||
} else {
|
|
||||||
// tasksModel.requestPublishDelegateGeometry(tasksModel.index(model.index, 0), Qt.rect(pos.x, pos.y, delegate.width, delegate.height), dummyWindowTask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Connections {
|
|
||||||
target: tasksView
|
|
||||||
onContentYChanged: {
|
|
||||||
syncDelegateGeometry();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Connections {
|
Connections {
|
||||||
|
|
@ -50,88 +44,74 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: syncDelegateGeometry();
|
function closeApp() {
|
||||||
|
|
||||||
Item {
|
|
||||||
anchors {
|
|
||||||
fill: parent
|
|
||||||
margins: PlasmaCore.Units.smallSpacing
|
|
||||||
}
|
|
||||||
|
|
||||||
SequentialAnimation {
|
|
||||||
id: slideAnim
|
|
||||||
property alias to: internalSlideAnim.to
|
|
||||||
NumberAnimation {
|
|
||||||
id: internalSlideAnim
|
|
||||||
target: background
|
|
||||||
properties: "x"
|
|
||||||
duration: PlasmaCore.Units.longDuration
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
ScriptAction {
|
|
||||||
script: {
|
|
||||||
if (background.x != 0) {
|
|
||||||
tasksModel.requestClose(tasksModel.index(model.index, 0));
|
tasksModel.requestClose(tasksModel.index(model.index, 0));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Rectangle {
|
|
||||||
id: background
|
|
||||||
|
|
||||||
|
QQC2.Control {
|
||||||
|
id: control
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height
|
height: parent.height
|
||||||
radius: PlasmaCore.Units.smallSpacing
|
|
||||||
color: PlasmaCore.Theme.backgroundColor
|
|
||||||
opacity: 1 * (1-Math.abs(x)/width)
|
|
||||||
|
|
||||||
MouseArea {
|
// drag up gesture
|
||||||
anchors.fill: parent
|
DragHandler {
|
||||||
drag {
|
id: dragHandler
|
||||||
target: background
|
target: parent
|
||||||
axis: Drag.XAxis
|
yAxis.enabled: true
|
||||||
}
|
xAxis.enabled: false
|
||||||
onPressed: delegate.z = 10;
|
yAxis.maximum: 0
|
||||||
onClicked: {
|
onActiveChanged: {
|
||||||
window.setSingleActiveWindow(model.index, delegate);
|
yAnimator.stop();
|
||||||
if (!model.IsMinimized) {
|
|
||||||
window.visible = false;
|
if (parent.y < -PlasmaCore.Units.gridUnit * 2) {
|
||||||
}
|
yAnimator.to = -window.height;
|
||||||
}
|
|
||||||
onReleased: {
|
|
||||||
delegate.z = 0;
|
|
||||||
if (Math.abs(background.x) > background.width/2) {
|
|
||||||
slideAnim.to = background.x > 0 ? background.width*2 : -background.width*2;
|
|
||||||
slideAnim.running = true;
|
|
||||||
} else {
|
} else {
|
||||||
slideAnim.to = 0;
|
yAnimator.to = 0;
|
||||||
slideAnim.running = true;
|
}
|
||||||
|
yAnimator.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
NumberAnimation on y {
|
||||||
anchors {
|
id: yAnimator
|
||||||
fill: parent
|
running: !dragHandler.active
|
||||||
margins: PlasmaCore.Units.smallSpacing
|
duration: PlasmaCore.Units.longDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
to: 0
|
||||||
|
onFinished: {
|
||||||
|
if (to != 0) { // close app
|
||||||
|
delegate.closeApp();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// application
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: PlasmaCore.Units.smallSpacing
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
id: appHeader
|
||||||
z: 99
|
z: 99
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.maximumHeight: PlasmaCore.Units.gridUnit
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
PlasmaCore.IconItem {
|
||||||
Layout.fillHeight: true
|
Layout.preferredHeight: PlasmaCore.Units.iconSizes.smallMedium
|
||||||
Layout.preferredWidth: height
|
Layout.preferredWidth: PlasmaCore.Units.iconSizes.smallMedium
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
usesPlasmaTheme: false
|
usesPlasmaTheme: false
|
||||||
source: model.decoration
|
source: model.decoration
|
||||||
}
|
}
|
||||||
|
|
||||||
PlasmaComponents.Label {
|
PlasmaComponents.Label {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
horizontalAlignment: Text.AlignHCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
text: model.AppName
|
text: model.AppName
|
||||||
color: PlasmaCore.Theme.textColor
|
color: "white"
|
||||||
}
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: rep
|
id: rep
|
||||||
model: plasmoid.nativeInterface.outputs
|
model: plasmoid.nativeInterface.outputs
|
||||||
|
|
@ -146,28 +126,57 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PlasmaComponents.ToolButton {
|
PlasmaComponents.ToolButton {
|
||||||
z: 99
|
z: 99
|
||||||
icon.name: "window-close"
|
icon.name: "window-close"
|
||||||
icon.width: PlasmaCore.Units.iconSizes.medium
|
icon.width: PlasmaCore.Units.iconSizes.smallMedium
|
||||||
icon.height: PlasmaCore.Units.iconSizes.medium
|
icon.height: PlasmaCore.Units.iconSizes.smallMedium
|
||||||
onClicked: {
|
onClicked: delegate.closeApp()
|
||||||
slideAnim.to = -background.width*2;
|
|
||||||
slideAnim.running = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QQC2.Control {
|
||||||
|
id: appView
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
|
||||||
|
Layout.preferredWidth: delegate.previewWidth
|
||||||
|
Layout.preferredHeight: delegate.previewHeight // keep same as window resolution
|
||||||
|
|
||||||
|
leftPadding: 0
|
||||||
|
rightPadding: 0
|
||||||
|
topPadding: 0
|
||||||
|
bottomPadding: 0
|
||||||
|
|
||||||
|
transform: Scale {
|
||||||
|
origin.x: item.width / 2
|
||||||
|
origin.y: item.height / 2
|
||||||
|
xScale: delegate.scale
|
||||||
|
yScale: delegate.scale
|
||||||
}
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: PlasmaCore.Theme.backgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
id: item
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: pipeWireLoader
|
id: pipeWireLoader
|
||||||
Layout.fillWidth: true
|
anchors.fill: parent
|
||||||
Layout.fillHeight: true
|
source: /*Qt.resolvedUrl("./TaskIcon.qml");*/ Qt.resolvedUrl("./Thumbnail.qml")
|
||||||
source: Qt.resolvedUrl("./Thumbnail.qml")
|
|
||||||
onStatusChanged: {
|
onStatusChanged: {
|
||||||
if (status === Loader.Error) {
|
if (status === Loader.Error) {
|
||||||
source = Qt.resolvedUrl("./TaskIcon.qml");
|
source = Qt.resolvedUrl("./TaskIcon.qml");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
TapHandler {
|
||||||
|
onTapped: {
|
||||||
|
window.setSingleActiveWindow(model.index, delegate);
|
||||||
|
window.visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
|
||||||
|
|
||||||
PlasmaCore.IconItem {
|
PlasmaCore.IconItem {
|
||||||
|
implicitWidth: PlasmaCore.Units.iconSizes.medium
|
||||||
|
implicitHeight: PlasmaCore.Units.iconSizes.medium
|
||||||
usesPlasmaTheme: false
|
usesPlasmaTheme: false
|
||||||
source: model.decoration
|
source: model.decoration
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
|
* SPDX-FileCopyrightText: 2015 Marco Martin <notmart@gmail.com>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: LGPL-2.0-or-later
|
* SPDX-License-Identifier: LGPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
@ -19,18 +20,31 @@ NanoShell.FullScreenOverlay {
|
||||||
visible: false
|
visible: false
|
||||||
width: Screen.width
|
width: Screen.width
|
||||||
height: Screen.height
|
height: Screen.height
|
||||||
property int offset: 0
|
|
||||||
property int overShoot: PlasmaCore.Units.gridUnit * 2
|
required property bool gestureDragging
|
||||||
|
required property real panelHeight // height of task panel, provided by main.qml
|
||||||
|
|
||||||
property int tasksCount: window.model.count
|
property int tasksCount: window.model.count
|
||||||
property int currentTaskIndex: -1
|
property int currentTaskIndex: tasksView.contentX / (tasksView.width + tasksView.spacing)
|
||||||
property TaskManager.TasksModel model
|
property TaskManager.TasksModel model
|
||||||
|
|
||||||
|
property real offset: 0
|
||||||
|
|
||||||
Component.onCompleted: plasmoid.nativeInterface.panel = window;
|
Component.onCompleted: plasmoid.nativeInterface.panel = window;
|
||||||
|
|
||||||
enum MovementDirection {
|
enum MovementDirection {
|
||||||
None = 0,
|
None = 0,
|
||||||
Up,
|
Left,
|
||||||
Down
|
Right
|
||||||
|
}
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (!visible) {
|
||||||
|
window.contentItem.opacity = 1;
|
||||||
|
}
|
||||||
|
// hide homescreen elements to make use of wallpaper
|
||||||
|
MobileShell.HomeScreenControls.setHomeScreenOpacity(visible ? 0 : 1);
|
||||||
|
MobileShell.HomeScreenControls.taskSwitcherVisible = visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
onTasksCountChanged: {
|
onTasksCountChanged: {
|
||||||
|
|
@ -38,38 +52,35 @@ NanoShell.FullScreenOverlay {
|
||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
// More controllable than the color property
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: backgroundRect
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Qt.rgba(0, 0, 0, 0.6)
|
color: Qt.rgba(0, 0, 0, 0.6)
|
||||||
opacity: Math.min(
|
|
||||||
(Math.min(tasksView.contentY, tasksView.height) / tasksView.height),
|
MouseArea {
|
||||||
((tasksView.contentHeight - tasksView.contentY - window.height) / tasksView.height))
|
anchors.fill: parent
|
||||||
|
onClicked: hide()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
if (window.model.count == 0) {
|
window.offset = 0;
|
||||||
return;
|
// skip to first active task
|
||||||
|
if (window.model.activeTask.row >= 0) {
|
||||||
|
tasksView.contentX = window.model.activeTask.row * (tasksView.width + tasksView.spacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
visible = true;
|
root.minimizeAll();
|
||||||
scrollAnim.from = tasksView.contentY;
|
window.visible = true;
|
||||||
scrollAnim.to = window.height;
|
|
||||||
scrollAnim.restart();
|
|
||||||
}
|
}
|
||||||
function hide() {
|
function hide() {
|
||||||
if (!window.visible) {
|
if (!window.visible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
scrollAnim.from = tasksView.contentY;
|
window.visible = false;
|
||||||
|
|
||||||
if (tasksView.contentY + window.height <= tasksView.contentHeight - tasksView.contentY) {
|
|
||||||
scrollAnim.to = 0;
|
|
||||||
} else {
|
|
||||||
scrollAnim.to = tasksView.contentHeight - window.height;
|
|
||||||
}
|
|
||||||
scrollAnim.restart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSingleActiveWindow(id, delegate) {
|
function setSingleActiveWindow(id, delegate) {
|
||||||
|
|
@ -91,232 +102,179 @@ NanoShell.FullScreenOverlay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
activateAnim.delegate = delegate;
|
|
||||||
activateAnim.restart();
|
|
||||||
}
|
|
||||||
|
|
||||||
onOffsetChanged: tasksView.contentY = offset + grid.y
|
|
||||||
onVisibleChanged: {
|
|
||||||
if (!visible) {
|
|
||||||
tasksView.contentY = 0;
|
|
||||||
moveTransition.enabled = false;
|
|
||||||
scrollAnim.running = false;
|
|
||||||
activateAnim.running = false;
|
|
||||||
window.contentItem.opacity = 1;
|
|
||||||
if (activateAnim.delegate) {
|
|
||||||
activateAnim.delegate.z = 0;
|
|
||||||
activateAnim.delegate.scale = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MobileShell.HomeScreenControls.taskSwitcherVisible = visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
SequentialAnimation {
|
|
||||||
id: scrollAnim
|
|
||||||
property alias to: internalAnim.to
|
|
||||||
property alias from: internalAnim.from
|
|
||||||
ScriptAction {
|
|
||||||
script: window.showFullScreen();
|
|
||||||
}
|
|
||||||
NumberAnimation {
|
|
||||||
id: internalAnim
|
|
||||||
target: tasksView
|
|
||||||
properties: "contentY"
|
|
||||||
duration: PlasmaCore.Units.longDuration
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
ScriptAction {
|
|
||||||
script: {
|
|
||||||
if (tasksView.contentY <= 0 || tasksView.contentY >= tasksView.contentHeight - window.height) {
|
|
||||||
window.visible = false;
|
|
||||||
setSingleActiveWindow(currentTaskIndex);
|
|
||||||
} else {
|
|
||||||
moveTransition.enabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SequentialAnimation {
|
|
||||||
id: activateAnim
|
|
||||||
property Item delegate
|
|
||||||
ScriptAction {
|
|
||||||
script: {
|
|
||||||
activateAnim.delegate.z = 2;
|
|
||||||
let pos = tasksView.mapFromItem(activateAnim.delegate, 0, 0);
|
|
||||||
if (pos.x < tasksView.width / 2 && pos.y < tasksView.height / 2) {
|
|
||||||
activateAnim.delegate.transformOrigin = Item.TopLeft;
|
|
||||||
} else if (pos.x < tasksView.width / 2 && pos.y >= tasksView.height / 2) {
|
|
||||||
activateAnim.delegate.transformOrigin = Item.BottomLeft;
|
|
||||||
} else if (pos.x >= tasksView.width / 2 && pos.y < tasksView.height / 2) {
|
|
||||||
activateAnim.delegate.transformOrigin = Item.TopRight;
|
|
||||||
} else {
|
|
||||||
activateAnim.delegate.transformOrigin = Item.BottomRight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ParallelAnimation {
|
|
||||||
OpacityAnimator {
|
|
||||||
target: window.contentItem
|
|
||||||
from: 1
|
|
||||||
to: 0
|
|
||||||
duration: PlasmaCore.Units.longDuration
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
ScaleAnimator {
|
|
||||||
target: activateAnim.delegate
|
|
||||||
from: 1
|
|
||||||
to: 2
|
|
||||||
// To try tosync up with kwin animation
|
|
||||||
duration: PlasmaCore.Units.longDuration * 0.85
|
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ScriptAction {
|
|
||||||
script: {
|
|
||||||
window.visible = false;
|
window.visible = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Flickable {
|
//Rectangle {
|
||||||
|
//id: liveAppShrink
|
||||||
|
//anchors.fill: parent
|
||||||
|
//anchors.topMargin: MobileShell.TopPanelControls.panelHeight
|
||||||
|
//anchors.bottomMargin: window.panelHeight
|
||||||
|
|
||||||
|
//// visible: window.gestureDragging
|
||||||
|
//z: tasksView.z + 1
|
||||||
|
//color: Qt.rgba(255, 255, 255, 0.1)
|
||||||
|
|
||||||
|
//transform: Scale {
|
||||||
|
//origin.x: window.width / 2
|
||||||
|
//origin.y: MobileShell.TopPanelControls.panelHeight + liveAppShrink.height / 2
|
||||||
|
//xScale: {
|
||||||
|
//let minScale = tasksView.scalingFactor;
|
||||||
|
//let travelDist = window.height - tasksView.height;
|
||||||
|
//let subtract = (1 - minScale) * (window.offset / travelDist);
|
||||||
|
//return Math.min(1, Math.max(minScale, 1 - subtract));
|
||||||
|
//}
|
||||||
|
//yScale: xScale
|
||||||
|
//}
|
||||||
|
|
||||||
|
//Loader {
|
||||||
|
//id: pipeWireLoader
|
||||||
|
//anchors.fill: parent
|
||||||
|
|
||||||
|
//z: tasksView.z + 1
|
||||||
|
//source: Qt.resolvedUrl("./Thumbnail.qml")
|
||||||
|
//onStatusChanged: {
|
||||||
|
//if (status === Loader.Error) {
|
||||||
|
//visible = false;
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
//function syncDelegateGeometry() {
|
||||||
|
//window.model.requestPublishDelegateGeometry(window.model.activeTask, Qt.rect(parent.x, parent.y, pipeWireLoader.width, pipeWireLoader.height), pipeWireLoader);
|
||||||
|
//}
|
||||||
|
//Component.onCompleted: syncDelegateGeometry();
|
||||||
|
//Connections {
|
||||||
|
//target: window.model
|
||||||
|
//function onActiveTaskChanged() {
|
||||||
|
//pipeWireLoader.syncDelegateGeometry();
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
|
||||||
|
ListView {
|
||||||
id: tasksView
|
id: tasksView
|
||||||
width: window.width
|
z: 100
|
||||||
height: window.height
|
|
||||||
contentWidth: width
|
|
||||||
contentHeight: mainArea.implicitHeight
|
|
||||||
// topMargin: flickingVertically ? -height : 0
|
|
||||||
// bottomMargin: flickingVertically ? -height : 0
|
|
||||||
property int flickState: TaskSwitcher.MovementDirection.None
|
|
||||||
|
|
||||||
readonly property int movementDirection: {
|
property real horizontalMargin: PlasmaCore.Units.gridUnit * 3
|
||||||
if (flickState != TaskSwitcher.MovementDirection.None) {
|
anchors.centerIn: parent
|
||||||
return flickState;
|
|
||||||
} else if (verticalVelocity < 0) {
|
width: window.width - horizontalMargin * 2
|
||||||
return TaskSwitcher.MovementDirection.Up;
|
height: window.height - (MobileShell.TopPanelControls.panelHeight + window.panelHeight + footerButtons.height
|
||||||
} else if (verticalVelocity > 0) {
|
+ PlasmaCore.Units.gridUnit * 2 + PlasmaCore.Units.largeSpacing * 2)
|
||||||
return TaskSwitcher.MovementDirection.Down;
|
|
||||||
|
// ensure that window previews are exactly to the scale of the device screen
|
||||||
|
property real windowHeight: window.height - window.panelHeight - MobileShell.TopPanelControls.panelHeight
|
||||||
|
property real scalingFactor: {
|
||||||
|
let candidateWidth = tasksView.width;
|
||||||
|
let candidateHeight = (tasksView.width / window.width) * windowHeight;
|
||||||
|
|
||||||
|
if (candidateHeight > tasksView.height) {
|
||||||
|
return tasksView.height / windowHeight;
|
||||||
} else {
|
} else {
|
||||||
return TaskSwitcher.MovementDirection.None;
|
return tasksView.width / window.width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onFlickingVerticallyChanged: {
|
model: window.model
|
||||||
if (flickingVertically) {
|
snapMode: ListView.SnapToItem
|
||||||
flickState = verticalVelocity < 0 ? TaskSwitcher.MovementDirection.Up : TaskSwitcher.MovementDirection.Down;
|
orientation: ListView.Horizontal
|
||||||
} else if (/*!draggingVertically &&*/ !flickingVertically) {
|
spacing: PlasmaCore.Units.largeSpacing
|
||||||
flickState = TaskSwitcher.MovementDirection.None
|
displayMarginBeginning: 2 * (width + spacing)
|
||||||
}
|
displayMarginEnd: 2 * (width + spacing)
|
||||||
Qt.callLater(function() {
|
|
||||||
tasksView.topMargin = flickingVertically && !scrollAnim.running ? -tasksView.height : 0;
|
displaced: Transition {
|
||||||
tasksView.bottomMargin = tasksView.topMargin;
|
NumberAnimation { properties: "x,y"; duration: PlasmaCore.Units.longDuration; easing.type: Easing.InOutQuad }
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onDraggingVerticallyChanged: {
|
property real offset: 0
|
||||||
if (draggingVertically) {
|
property real currentIndexInView: indexAt(contentX, contentY)
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let beforeBeginning = contentY < window.height;
|
|
||||||
let afterEnd = contentY > contentHeight - window.height * 2;
|
|
||||||
|
|
||||||
let topCloseCondition = contentY < window.height / 10 * 9;
|
|
||||||
let bottomClosecondition = contentY > contentHeight - window.height * 2 - window.height / 10;
|
|
||||||
|
|
||||||
switch (movementDirection) {
|
|
||||||
case TaskSwitcher.MovementDirection.Up: {
|
|
||||||
if (topCloseCondition) {
|
|
||||||
hide();
|
|
||||||
} else if (beforeBeginning) {
|
|
||||||
show();
|
|
||||||
} else if (afterEnd) {
|
|
||||||
scrollAnim.from = tasksView.contentY;
|
|
||||||
scrollAnim.to = tasksView.contentHeight - window.height * 2;
|
|
||||||
scrollAnim.restart();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TaskSwitcher.MovementDirection.Down: {
|
|
||||||
if (bottomClosecondition) {
|
|
||||||
hide();
|
|
||||||
} else if (beforeBeginning) {
|
|
||||||
show();
|
|
||||||
} else if (afterEnd) {
|
|
||||||
scrollAnim.from = tasksView.contentY;
|
|
||||||
scrollAnim.to = tasksView.contentHeight - window.height * 2;
|
|
||||||
scrollAnim.restart();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case TaskSwitcher.MovementDirection.None:
|
|
||||||
default:
|
|
||||||
if (beforeBeginning) {
|
|
||||||
show();
|
|
||||||
} else if (afterEnd) {
|
|
||||||
scrollAnim.from = tasksView.contentY;
|
|
||||||
scrollAnim.to = tasksView.contentHeight - window.height * 2;
|
|
||||||
scrollAnim.restart();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: mainArea
|
z: -1
|
||||||
parent: tasksView.contentItem
|
anchors.fill: parent
|
||||||
|
visible: tasksView.count === 0
|
||||||
|
enabled: visible
|
||||||
|
onClicked: { // close window on tap if there are no delegates
|
||||||
|
if (tasksView.count === 0) {
|
||||||
|
window.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents.Label {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: i18n("No applications are open")
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Task {
|
||||||
|
id: task
|
||||||
|
property int curIndex: model.index
|
||||||
width: tasksView.width
|
width: tasksView.width
|
||||||
implicitHeight: window.height * 2 + Math.max(window.height, grid.implicitHeight)
|
height: tasksView.height
|
||||||
onClicked: window.hide()
|
|
||||||
|
|
||||||
Grid {
|
// ensure that window previews are exactly to the scale of the device screen
|
||||||
id: grid
|
previewWidth: tasksView.scalingFactor * window.width
|
||||||
anchors {
|
previewHeight: tasksView.scalingFactor * tasksView.windowHeight
|
||||||
left: parent.left
|
|
||||||
right: parent.right
|
|
||||||
}
|
|
||||||
columns: 2
|
|
||||||
y: parent.height - height - window.height
|
|
||||||
|
|
||||||
Behavior on y {
|
scale: {
|
||||||
NumberAnimation {
|
if (task.curIndex === window.currentTaskIndex) {
|
||||||
duration: PlasmaCore.Units.longDuration
|
let maxScale = 1 / tasksView.scalingFactor;
|
||||||
easing.type: Easing.InOutQuad
|
let travelDist = window.height - tasksView.height;
|
||||||
|
let subtract = (maxScale - 1) * (window.offset / travelDist);
|
||||||
|
let finalScale = Math.max(1, Math.min(maxScale, maxScale - subtract));
|
||||||
|
|
||||||
|
return finalScale;
|
||||||
}
|
}
|
||||||
}
|
return 1;
|
||||||
Repeater {
|
|
||||||
model: window.model
|
|
||||||
delegate: Task {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
move: Transition {
|
onDragOffsetChanged: tasksView.offset = dragOffset
|
||||||
id: moveTransition
|
|
||||||
enabled: false
|
// TODO slide right animation
|
||||||
NumberAnimation {
|
//transform: Translate {
|
||||||
properties: "x,y"
|
//x: task.curIndex < tasksView.currentIndexInView ? Math.min(task.width + tasksView.spacing, tasksView.offset / 2) : 0
|
||||||
duration: PlasmaCore.Units.longDuration
|
//}
|
||||||
easing.type: Easing.InOutQuad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
RowLayout {
|
||||||
|
id: footerButtons
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.bottomMargin: PlasmaCore.Units.largeSpacing + window.panelHeight
|
||||||
|
anchors.topMargin: PlasmaCore.Units.largeSpacing
|
||||||
|
|
||||||
PlasmaComponents.RoundButton {
|
spacing: PlasmaCore.Units.largeSpacing
|
||||||
anchors {
|
|
||||||
horizontalCenter: parent.horizontalCenter
|
PlasmaComponents.ToolButton {
|
||||||
bottom: parent.bottom
|
Layout.alignment: Qt.AlignRight
|
||||||
}
|
|
||||||
icon.name: "start-here-kde"
|
|
||||||
icon.width: PlasmaCore.Units.iconSizes.medium
|
icon.width: PlasmaCore.Units.iconSizes.medium
|
||||||
icon.height: PlasmaCore.Units.iconSizes.medium
|
icon.height: PlasmaCore.Units.iconSizes.medium
|
||||||
onClicked: {
|
icon.name: "view-list-symbolic" // "view-grid-symbolic"
|
||||||
currentTaskIndex = -1;
|
text: i18n("Switch to list view")
|
||||||
window.hide();
|
display: PlasmaComponents.ToolButton.IconOnly
|
||||||
//plasmoid.nativeInterface.showDesktop = true;
|
}
|
||||||
|
|
||||||
root.minimizeAll();
|
PlasmaComponents.ToolButton {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
icon.width: PlasmaCore.Units.iconSizes.medium
|
||||||
|
icon.height: PlasmaCore.Units.iconSizes.medium
|
||||||
|
icon.name: "trash-empty"
|
||||||
|
text: i18n("Clear All")
|
||||||
|
display: PlasmaComponents.ToolButton.IconOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents.ToolButton {
|
||||||
|
Layout.alignment: Qt.AlignLeft
|
||||||
|
icon.width: PlasmaCore.Units.iconSizes.medium
|
||||||
|
icon.height: PlasmaCore.Units.iconSizes.medium
|
||||||
|
icon.name: "system-search"
|
||||||
|
text: i18n("Search")
|
||||||
|
display: PlasmaComponents.ToolButton.IconOnly
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
* SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
|
||||||
|
* SPDX-FileCopyrightText: 2021 Devin Lin <devin@kde.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
@ -46,7 +47,7 @@ PlasmaCore.ColorScope {
|
||||||
running: true
|
running: true
|
||||||
interval: 200
|
interval: 200
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
taskSwitcherLoader.setSource(Qt.resolvedUrl("TaskSwitcher.qml"), {"model": tasksModel});
|
taskSwitcherLoader.setSource(Qt.resolvedUrl("TaskSwitcher.qml"), {"model": tasksModel, "panelHeight": root.height, "gestureDragging": mainMouseArea.pressed});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,8 +78,6 @@ PlasmaCore.ColorScope {
|
||||||
|
|
||||||
virtualDesktop: virtualDesktopInfo.currentDesktop
|
virtualDesktop: virtualDesktopInfo.currentDesktop
|
||||||
activity: activityInfo.currentActivity
|
activity: activityInfo.currentActivity
|
||||||
//FIXME: workaround
|
|
||||||
Component.onCompleted: tasksModel.countChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskManager.VirtualDesktopInfo {
|
TaskManager.VirtualDesktopInfo {
|
||||||
|
|
@ -104,7 +103,6 @@ PlasmaCore.ColorScope {
|
||||||
onPressed: {
|
onPressed: {
|
||||||
startMouseX = oldMouseX = mouse.y;
|
startMouseX = oldMouseX = mouse.y;
|
||||||
startMouseY = oldMouseY = mouse.y;
|
startMouseY = oldMouseY = mouse.y;
|
||||||
taskSwitcher.offset = -taskSwitcher.height;
|
|
||||||
activeButton = icons.childAt(mouse.x, mouse.y);
|
activeButton = icons.childAt(mouse.x, mouse.y);
|
||||||
}
|
}
|
||||||
onPositionChanged: {
|
onPositionChanged: {
|
||||||
|
|
@ -119,18 +117,18 @@ PlasmaCore.ColorScope {
|
||||||
isDragging = true;
|
isDragging = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
taskSwitcher.offset = taskSwitcher.offset - (mouse.y - oldMouseY);
|
taskSwitcher.offset = Math.max(0, taskSwitcher.offset - (mouse.y - oldMouseY));
|
||||||
opening = oldMouseY > mouse.y;
|
opening = oldMouseY > mouse.y;
|
||||||
|
|
||||||
if (taskSwitcher.visibility == Window.Hidden && taskSwitcher.offset > -taskSwitcher.height + units.gridUnit && taskSwitcher.tasksCount) {
|
if (taskSwitcher.visibility == Window.Hidden && Math.abs(startMouseY - mouse.y) > PlasmaCore.Units.gridUnit && taskSwitcher.tasksCount) {
|
||||||
activeButton = null;
|
activeButton = null;
|
||||||
taskSwitcher.showFullScreen();
|
taskSwitcher.show();
|
||||||
//no tasks, let's scroll up the homescreen instead
|
|
||||||
} else if (taskSwitcher.tasksCount === 0) {
|
} else if (taskSwitcher.tasksCount === 0) {
|
||||||
|
//no tasks, let's scroll up the homescreen instead
|
||||||
MobileShell.HomeScreenControls.requestRelativeScroll(Qt.point(mouse.x - oldMouseX, mouse.y - oldMouseY));
|
MobileShell.HomeScreenControls.requestRelativeScroll(Qt.point(mouse.x - oldMouseX, mouse.y - oldMouseY));
|
||||||
}
|
}
|
||||||
oldMouseY = mouse.y;
|
oldMouseY = mouse.y;
|
||||||
oldMouseY = mouse.y;
|
oldMouseX = mouse.x;
|
||||||
}
|
}
|
||||||
onReleased: {
|
onReleased: {
|
||||||
if (taskSwitcher.visibility == Window.Hidden) {
|
if (taskSwitcher.visibility == Window.Hidden) {
|
||||||
|
|
@ -173,19 +171,11 @@ PlasmaCore.ColorScope {
|
||||||
visible: plasmoid.configuration.PanelButtonsVisible
|
visible: plasmoid.configuration.PanelButtonsVisible
|
||||||
property real buttonLength: 0
|
property real buttonLength: 0
|
||||||
|
|
||||||
|
// background colour
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
gradient: Gradient {
|
|
||||||
GradientStop {
|
|
||||||
position: 0
|
|
||||||
color: showingApp ? root.backgroundColor : "transparent"
|
color: showingApp ? root.backgroundColor : "transparent"
|
||||||
}
|
}
|
||||||
GradientStop {
|
|
||||||
position: 1
|
|
||||||
color: showingApp ? root.backgroundColor : Qt.rgba(0, 0, 0, 0.1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
id: tasksButton
|
id: tasksButton
|
||||||
|
|
@ -213,6 +203,7 @@ PlasmaCore.ColorScope {
|
||||||
}
|
}
|
||||||
root.minimizeAll();
|
root.minimizeAll();
|
||||||
MobileShell.HomeScreenControls.resetHomeScreenPosition();
|
MobileShell.HomeScreenControls.resetHomeScreenPosition();
|
||||||
|
MobileShell.HomeScreenControls.setHomeScreenOpacity(1);
|
||||||
plasmoid.nativeInterface.allMinimizedChanged();
|
plasmoid.nativeInterface.allMinimizedChanged();
|
||||||
}
|
}
|
||||||
iconSizeFactor: 1
|
iconSizeFactor: 1
|
||||||
|
|
@ -253,7 +244,6 @@ PlasmaCore.ColorScope {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Window.window.offset = Qt.binding(() => {
|
Window.window.offset = Qt.binding(() => {
|
||||||
// FIXME: find a more precise way to determine the top panel height
|
|
||||||
return plasmoid.formFactor === PlasmaCore.Types.Vertical ? MobileShell.TopPanelControls.panelHeight : 0
|
return plasmoid.formFactor === PlasmaCore.Types.Vertical ? MobileShell.TopPanelControls.panelHeight : 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue