2026-04-29 10:10:31 +00:00
|
|
|
// SPDX-FileCopyrightText: 2026 Marco Allegretti
|
|
|
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
|
|
|
|
|
|
import QtQuick
|
|
|
|
|
import QtQuick.Layouts
|
|
|
|
|
|
|
|
|
|
import org.kde.kirigami as Kirigami
|
|
|
|
|
import org.kde.plasma.components 3.0 as PC3
|
2026-05-21 09:13:36 +00:00
|
|
|
import org.kde.plasma.private.mobileshell as MobileShell
|
2026-05-22 07:39:28 +00:00
|
|
|
import org.kde.plasma.private.mobileshell.shellsettingsplugin as ShellSettings
|
2026-04-29 10:10:31 +00:00
|
|
|
import org.kde.taskmanager as TaskManager
|
|
|
|
|
|
|
|
|
|
import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
|
|
|
|
|
|
|
|
|
|
Item {
|
|
|
|
|
id: root
|
|
|
|
|
|
|
|
|
|
required property var folio
|
|
|
|
|
|
|
|
|
|
readonly property bool hasTasks: allTasksModel.count > 0
|
2026-05-21 09:13:36 +00:00
|
|
|
readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
|
2026-04-29 10:10:31 +00:00
|
|
|
property bool sortByName: false
|
|
|
|
|
property int dragTargetDesktopIndex: -1
|
|
|
|
|
property string pendingMoveTaskKey: ""
|
|
|
|
|
property string pendingMoveTargetName: ""
|
|
|
|
|
|
|
|
|
|
signal taskActivated()
|
|
|
|
|
|
|
|
|
|
function taskStorageId(taskModel) {
|
|
|
|
|
var id = taskModel ? taskModel.AppId || "" : ""
|
|
|
|
|
if (id && !id.endsWith(".desktop")) {
|
|
|
|
|
id += ".desktop"
|
|
|
|
|
}
|
|
|
|
|
return id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function taskKey(taskModel) {
|
|
|
|
|
const winIds = taskModel && taskModel.WinIdList ? taskModel.WinIdList : []
|
|
|
|
|
if (winIds.length > 0) {
|
|
|
|
|
var key = ""
|
|
|
|
|
for (var i = 0; i < winIds.length; ++i) {
|
|
|
|
|
key += String(winIds[i]) + "|"
|
|
|
|
|
}
|
|
|
|
|
return key
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return String(taskModel ? taskModel.AppId || "" : "") + "|" + String(taskModel ? taskModel.display || "" : "")
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-22 07:39:28 +00:00
|
|
|
function taskWindowId(taskModel) {
|
|
|
|
|
const winIds = taskModel && taskModel.WinIdList ? taskModel.WinIdList : []
|
|
|
|
|
return winIds.length === 1 ? String(winIds[0]) : ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function dynamicTilingMoveToDesktopAction(desktopId, desktopIndex) {
|
|
|
|
|
if (String(desktopId).length === 0 || desktopIndex < 0) {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
return "move-to-desktop:" + String(desktopId) + "|" + String(desktopIndex + 1)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-29 10:10:31 +00:00
|
|
|
function markTaskMove(taskKey, desktopIndex) {
|
|
|
|
|
pendingMoveTaskKey = taskKey
|
|
|
|
|
pendingMoveTargetName = desktopName(desktopIndex)
|
|
|
|
|
pendingMoveResetTimer.restart()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function mixColor(base, overlay, ratio) {
|
|
|
|
|
return Qt.rgba(
|
|
|
|
|
base.r + (overlay.r - base.r) * ratio,
|
|
|
|
|
base.g + (overlay.g - base.g) * ratio,
|
|
|
|
|
base.b + (overlay.b - base.b) * ratio,
|
|
|
|
|
base.a + (overlay.a - base.a) * ratio)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function desktopName(index) {
|
|
|
|
|
const names = virtualDesktopInfo.desktopNames
|
|
|
|
|
if (names && names.length > index && String(names[index]).length > 0) {
|
|
|
|
|
return String(names[index])
|
|
|
|
|
}
|
|
|
|
|
return "Desktop " + (index + 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isCurrentDesktop(desktopId) {
|
|
|
|
|
return String(desktopId) === String(virtualDesktopInfo.currentDesktop)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Timer {
|
|
|
|
|
id: pendingMoveResetTimer
|
|
|
|
|
interval: 1200
|
|
|
|
|
onTriggered: {
|
|
|
|
|
root.pendingMoveTaskKey = ""
|
|
|
|
|
root.pendingMoveTargetName = ""
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TaskManager.VirtualDesktopInfo { id: virtualDesktopInfo }
|
|
|
|
|
TaskManager.ActivityInfo { id: activityInfo }
|
|
|
|
|
|
|
|
|
|
TaskManager.TasksModel {
|
|
|
|
|
id: allTasksModel
|
|
|
|
|
filterByVirtualDesktop: false
|
|
|
|
|
filterByActivity: true
|
|
|
|
|
filterNotMaximized: false
|
|
|
|
|
filterByScreen: true
|
|
|
|
|
filterHidden: false
|
|
|
|
|
activity: activityInfo.currentActivity
|
2026-05-23 07:52:51 +00:00
|
|
|
groupMode: TaskManager.TasksModel.GroupDisabled
|
2026-04-29 10:10:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TaskManager.TasksModel {
|
|
|
|
|
id: tasksModel
|
|
|
|
|
filterByVirtualDesktop: true
|
|
|
|
|
filterByActivity: true
|
|
|
|
|
filterNotMaximized: false
|
|
|
|
|
filterByScreen: true
|
|
|
|
|
filterHidden: false
|
|
|
|
|
virtualDesktop: virtualDesktopInfo.currentDesktop
|
|
|
|
|
activity: activityInfo.currentActivity
|
2026-05-23 07:52:51 +00:00
|
|
|
groupMode: TaskManager.TasksModel.GroupDisabled
|
2026-04-29 10:10:31 +00:00
|
|
|
sortMode: root.sortByName ? TaskManager.TasksModel.SortAlpha : TaskManager.TasksModel.SortLastActivated
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
id: panelBackground
|
|
|
|
|
anchors.fill: parent
|
2026-05-23 13:58:56 +00:00
|
|
|
radius: 0
|
2026-05-24 13:49:08 +00:00
|
|
|
color: "transparent"
|
2026-04-29 10:10:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ColumnLayout {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
anchors.margins: Kirigami.Units.smallSpacing
|
|
|
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
PC3.Label {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
text: i18n("Running")
|
|
|
|
|
font.weight: Font.Medium
|
|
|
|
|
elide: Text.ElideRight
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Row {
|
|
|
|
|
spacing: 1
|
|
|
|
|
|
|
|
|
|
Repeater {
|
|
|
|
|
model: [
|
|
|
|
|
{ label: i18n("Recent"), byName: false },
|
|
|
|
|
{ label: i18n("Name"), byName: true }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
delegate: MouseArea {
|
|
|
|
|
id: sortButton
|
|
|
|
|
|
|
|
|
|
required property var modelData
|
|
|
|
|
readonly property bool checked: root.sortByName === modelData.byName
|
|
|
|
|
|
|
|
|
|
width: Math.max(Kirigami.Units.gridUnit * 3.5, label.implicitWidth + Kirigami.Units.smallSpacing * 3)
|
|
|
|
|
height: Kirigami.Units.gridUnit * 1.6
|
|
|
|
|
hoverEnabled: true
|
|
|
|
|
cursorShape: Qt.PointingHandCursor
|
|
|
|
|
onClicked: root.sortByName = modelData.byName
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
radius: Kirigami.Units.cornerRadius
|
|
|
|
|
color: sortButton.checked
|
|
|
|
|
? root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, sortButton.containsMouse ? 0.28 : 0.2)
|
|
|
|
|
: sortButton.containsMouse
|
|
|
|
|
? root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.08)
|
|
|
|
|
: "transparent"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PC3.Label {
|
|
|
|
|
id: label
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
text: sortButton.modelData.label
|
|
|
|
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.85
|
|
|
|
|
color: sortButton.checked ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
|
id: desktopStrip
|
|
|
|
|
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
visible: virtualDesktopInfo.numberOfDesktops > 1
|
|
|
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
PC3.Label {
|
|
|
|
|
text: i18n("Desktops")
|
|
|
|
|
opacity: 0.7
|
|
|
|
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.85
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Item {
|
|
|
|
|
id: desktopDropSurface
|
|
|
|
|
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 2.4
|
|
|
|
|
|
|
|
|
|
function desktopIndexAt(x) {
|
|
|
|
|
if (virtualDesktopInfo.numberOfDesktops <= 0) {
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const localX = desktopRow.mapFromItem(desktopDropSurface, x, 0).x
|
|
|
|
|
var nearestIndex = -1
|
|
|
|
|
var nearestDistance = Number.MAX_VALUE
|
|
|
|
|
for (var i = 0; i < virtualDesktopInfo.numberOfDesktops; ++i) {
|
|
|
|
|
const item = desktopRepeater.itemAt(i)
|
|
|
|
|
if (!item) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (localX >= item.x && localX <= item.x + item.width) {
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const center = item.x + item.width / 2
|
|
|
|
|
const distance = Math.abs(localX - center)
|
|
|
|
|
if (distance < nearestDistance) {
|
|
|
|
|
nearestIndex = i
|
|
|
|
|
nearestDistance = distance
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nearestIndex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Row {
|
|
|
|
|
id: desktopRow
|
|
|
|
|
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
Repeater {
|
|
|
|
|
id: desktopRepeater
|
|
|
|
|
|
|
|
|
|
model: virtualDesktopInfo.desktopIds
|
|
|
|
|
|
|
|
|
|
delegate: MouseArea {
|
|
|
|
|
id: desktopButton
|
|
|
|
|
|
|
|
|
|
required property int index
|
|
|
|
|
required property var modelData
|
|
|
|
|
|
|
|
|
|
readonly property bool checked: root.isCurrentDesktop(modelData)
|
|
|
|
|
readonly property string desktopLabel: root.desktopName(index)
|
|
|
|
|
readonly property bool dragHovered: desktopDropArea.containsDrag && root.dragTargetDesktopIndex === index
|
|
|
|
|
|
|
|
|
|
width: Math.max(Kirigami.Units.gridUnit * 5.5, (desktopRow.width / Math.max(1, virtualDesktopInfo.numberOfDesktops)) - Kirigami.Units.smallSpacing)
|
|
|
|
|
height: desktopRow.height
|
|
|
|
|
hoverEnabled: true
|
|
|
|
|
cursorShape: Qt.PointingHandCursor
|
|
|
|
|
|
|
|
|
|
onClicked: root.folio.activateVirtualDesktop(modelData)
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
radius: Kirigami.Units.cornerRadius
|
|
|
|
|
scale: desktopButton.dragHovered ? 1.03 : 1
|
|
|
|
|
color: desktopButton.checked
|
|
|
|
|
? root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, desktopButton.containsMouse || desktopButton.dragHovered ? 0.32 : 0.24)
|
|
|
|
|
: desktopButton.dragHovered
|
|
|
|
|
? root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.18)
|
|
|
|
|
: desktopButton.containsMouse
|
|
|
|
|
? root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.08)
|
|
|
|
|
: root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.045)
|
|
|
|
|
border.width: 1
|
|
|
|
|
border.pixelAligned: false
|
|
|
|
|
border.color: desktopButton.checked || desktopButton.dragHovered
|
|
|
|
|
? root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.55)
|
|
|
|
|
: root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.14)
|
|
|
|
|
|
|
|
|
|
Behavior on color {
|
2026-05-21 09:13:36 +00:00
|
|
|
MobileShell.MotionColorAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration }
|
2026-04-29 10:10:31 +00:00
|
|
|
}
|
|
|
|
|
Behavior on scale {
|
2026-05-21 09:13:36 +00:00
|
|
|
MobileShell.MotionNumberAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration }
|
2026-04-29 10:10:31 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.left: parent.left
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
anchors.margins: Kirigami.Units.smallSpacing / 2
|
|
|
|
|
height: Math.max(2, Math.round(Kirigami.Units.devicePixelRatio))
|
|
|
|
|
radius: height / 2
|
|
|
|
|
visible: desktopButton.checked
|
|
|
|
|
color: Kirigami.Theme.highlightColor
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PC3.Label {
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
width: parent.width - Kirigami.Units.smallSpacing * 2
|
|
|
|
|
text: desktopButton.desktopLabel
|
|
|
|
|
elide: Text.ElideRight
|
|
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
|
font.weight: desktopButton.checked || desktopButton.dragHovered ? Font.Medium : Font.Normal
|
|
|
|
|
font.pixelSize: Math.min(Kirigami.Theme.defaultFont.pixelSize, parent.height * 0.42)
|
|
|
|
|
color: desktopButton.checked || desktopButton.dragHovered ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DropArea {
|
|
|
|
|
id: desktopDropArea
|
|
|
|
|
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
keys: ["folio-running-task"]
|
|
|
|
|
|
|
|
|
|
onEntered: (drag) => {
|
|
|
|
|
root.dragTargetDesktopIndex = desktopDropSurface.desktopIndexAt(drag.x)
|
|
|
|
|
drag.accept(Qt.MoveAction)
|
|
|
|
|
}
|
|
|
|
|
onPositionChanged: (drag) => {
|
|
|
|
|
root.dragTargetDesktopIndex = desktopDropSurface.desktopIndexAt(drag.x)
|
|
|
|
|
drag.accept(Qt.MoveAction)
|
|
|
|
|
}
|
|
|
|
|
onExited: root.dragTargetDesktopIndex = -1
|
|
|
|
|
onDropped: (drop) => {
|
|
|
|
|
const desktopIndex = desktopDropSurface.desktopIndexAt(drop.x)
|
|
|
|
|
const desktopId = desktopIndex >= 0 ? virtualDesktopInfo.desktopIds[desktopIndex] : ""
|
|
|
|
|
if (!drop.source || !drop.source.moveToDesktop || String(desktopId).length === 0) {
|
|
|
|
|
root.dragTargetDesktopIndex = -1
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drop.source.moveToDesktop(desktopId, desktopIndex)
|
|
|
|
|
root.dragTargetDesktopIndex = -1
|
|
|
|
|
drop.accept(Qt.MoveAction)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GridView {
|
|
|
|
|
id: taskGrid
|
|
|
|
|
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
Layout.fillHeight: true
|
|
|
|
|
clip: true
|
|
|
|
|
model: tasksModel
|
|
|
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
|
|
|
interactive: contentHeight > height
|
|
|
|
|
|
|
|
|
|
readonly property int columns: Math.max(1, Math.floor(width / (Kirigami.Units.gridUnit * 14)))
|
|
|
|
|
cellWidth: Math.floor(width / columns)
|
|
|
|
|
cellHeight: Kirigami.Units.gridUnit * 10
|
|
|
|
|
|
|
|
|
|
delegate: Item {
|
|
|
|
|
id: taskCard
|
|
|
|
|
|
|
|
|
|
required property int index
|
|
|
|
|
required property var model
|
|
|
|
|
|
|
|
|
|
width: taskGrid.cellWidth - Kirigami.Units.smallSpacing
|
|
|
|
|
height: taskGrid.cellHeight - Kirigami.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
readonly property var modelIndex: tasksModel.makeModelIndex(index)
|
|
|
|
|
readonly property var winIds: model.WinIdList ? model.WinIdList : []
|
|
|
|
|
readonly property int previewCount: Math.max(1, Math.min(2, winIds.length))
|
|
|
|
|
readonly property bool activeTask: model.IsActive === true
|
|
|
|
|
readonly property bool minimizedTask: model.IsMinimized === true
|
|
|
|
|
readonly property bool groupTask: model.IsGroupParent === true
|
|
|
|
|
readonly property bool desktopsChangeable: model.IsVirtualDesktopsChangeable === true
|
|
|
|
|
readonly property string storageId: root.taskStorageId(model)
|
|
|
|
|
readonly property string taskKey: root.taskKey(model)
|
2026-05-22 07:39:28 +00:00
|
|
|
readonly property string windowId: root.taskWindowId(model)
|
|
|
|
|
readonly property bool dynamicTilingActive: ShellSettings.Settings.convergenceModeEnabled && ShellSettings.Settings.dynamicTilingEnabled
|
|
|
|
|
readonly property bool canRequestDynamicTiling: dynamicTilingActive && !groupTask && windowId !== ""
|
|
|
|
|
readonly property int dynamicTilingWindowStateSerial: ShellSettings.Settings.dynamicTilingWindowStateSerial
|
|
|
|
|
readonly property bool dynamicTilingMaximized: canRequestDynamicTiling
|
|
|
|
|
&& dynamicTilingWindowStateSerial >= 0
|
|
|
|
|
&& ShellSettings.Settings.isDynamicTilingWindowMaximized(windowId)
|
|
|
|
|
readonly property bool maximizedTask: model.IsMaximized === true || dynamicTilingMaximized
|
2026-04-29 10:10:31 +00:00
|
|
|
readonly property bool pinned: storageId !== "" && root.folio.FavouritesModel.containsApplication(storageId)
|
|
|
|
|
readonly property bool pendingMove: root.pendingMoveTaskKey === taskKey
|
|
|
|
|
|
|
|
|
|
function taskIndexForPreview(previewIndex) {
|
|
|
|
|
return taskCard.groupTask
|
|
|
|
|
? tasksModel.makeModelIndex(taskCard.index, previewIndex)
|
|
|
|
|
: taskCard.modelIndex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function titleForPreview(previewIndex) {
|
|
|
|
|
if (!taskCard.groupTask) {
|
|
|
|
|
return taskCard.model.display || ""
|
|
|
|
|
}
|
|
|
|
|
return tasksModel.data(tasksModel.makeModelIndex(taskCard.index, previewIndex), 0) || taskCard.model.display || ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function activate(previewIndex) {
|
|
|
|
|
tasksModel.requestActivate(taskIndexForPreview(previewIndex || 0))
|
|
|
|
|
root.taskActivated()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function moveToDesktop(desktopId, desktopIndex) {
|
|
|
|
|
if (!taskCard.desktopsChangeable || String(desktopId).length === 0) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
root.markTaskMove(taskCard.taskKey, desktopIndex)
|
2026-05-22 07:39:28 +00:00
|
|
|
if (taskCard.canRequestDynamicTiling) {
|
|
|
|
|
const action = root.dynamicTilingMoveToDesktopAction(desktopId, desktopIndex)
|
|
|
|
|
if (action !== "") {
|
|
|
|
|
ShellSettings.Settings.requestDynamicTilingWindowAction(taskCard.windowId, action)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-29 10:10:31 +00:00
|
|
|
tasksModel.requestVirtualDesktops(taskCard.modelIndex, [desktopId])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Item {
|
|
|
|
|
id: dragProxy
|
|
|
|
|
|
|
|
|
|
parent: root
|
|
|
|
|
width: taskCard.width
|
|
|
|
|
height: taskCard.height
|
|
|
|
|
z: 1000
|
|
|
|
|
visible: cardArea.drag.active
|
|
|
|
|
opacity: 0.9
|
|
|
|
|
|
|
|
|
|
Drag.active: cardArea.drag.active
|
|
|
|
|
Drag.hotSpot.x: cardArea.pressX
|
|
|
|
|
Drag.hotSpot.y: cardArea.pressY
|
|
|
|
|
Drag.keys: ["folio-running-task"]
|
|
|
|
|
Drag.proposedAction: Qt.MoveAction
|
|
|
|
|
Drag.source: taskCard
|
|
|
|
|
Drag.supportedActions: Qt.MoveAction
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
radius: Kirigami.Units.cornerRadius
|
|
|
|
|
color: root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.2)
|
|
|
|
|
border.width: 1
|
|
|
|
|
border.pixelAligned: false
|
|
|
|
|
border.color: root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.6)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
anchors.margins: Kirigami.Units.smallSpacing
|
|
|
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
Kirigami.Icon {
|
|
|
|
|
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
|
|
|
|
|
Layout.preferredHeight: Layout.preferredWidth
|
|
|
|
|
source: taskCard.model.decoration
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PC3.Label {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
text: taskCard.model.display || ""
|
|
|
|
|
elide: Text.ElideRight
|
|
|
|
|
maximumLineCount: 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
|
id: cardArea
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
hoverEnabled: true
|
|
|
|
|
cursorShape: taskCard.desktopsChangeable ? Qt.OpenHandCursor : Qt.PointingHandCursor
|
|
|
|
|
enabled: !taskCard.pendingMove
|
|
|
|
|
property real pressX: width / 2
|
|
|
|
|
property real pressY: height / 2
|
|
|
|
|
property bool wasDragged: false
|
|
|
|
|
drag.target: taskCard.desktopsChangeable ? dragProxy : undefined
|
|
|
|
|
drag.threshold: Math.max(4, Kirigami.Units.smallSpacing)
|
|
|
|
|
drag.smoothed: false
|
|
|
|
|
|
|
|
|
|
onPressed: (mouse) => {
|
|
|
|
|
wasDragged = false
|
|
|
|
|
pressX = mouse.x
|
|
|
|
|
pressY = mouse.y
|
|
|
|
|
const pos = taskCard.mapToItem(root, 0, 0)
|
|
|
|
|
dragProxy.x = pos.x
|
|
|
|
|
dragProxy.y = pos.y
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onPositionChanged: {
|
|
|
|
|
if (drag.active) {
|
|
|
|
|
wasDragged = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onReleased: {
|
|
|
|
|
if (wasDragged) {
|
|
|
|
|
dragProxy.Drag.drop()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onClicked: {
|
|
|
|
|
if (!wasDragged) {
|
|
|
|
|
taskCard.activate(0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
radius: Kirigami.Units.cornerRadius
|
|
|
|
|
color: taskCard.activeTask
|
|
|
|
|
? root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, cardArea.containsMouse ? 0.18 : 0.12)
|
|
|
|
|
: cardArea.containsMouse
|
|
|
|
|
? root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.08)
|
|
|
|
|
: root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.04)
|
|
|
|
|
border.width: 1
|
|
|
|
|
border.pixelAligned: false
|
|
|
|
|
border.color: taskCard.activeTask
|
|
|
|
|
? root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.5)
|
|
|
|
|
: root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.12)
|
|
|
|
|
|
|
|
|
|
Behavior on color {
|
2026-05-21 09:13:36 +00:00
|
|
|
MobileShell.MotionColorAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration }
|
2026-04-29 10:10:31 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ColumnLayout {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
anchors.margins: Kirigami.Units.smallSpacing
|
|
|
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
Row {
|
|
|
|
|
id: previewRow
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
Layout.preferredHeight: Kirigami.Units.gridUnit * 5
|
|
|
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
Repeater {
|
|
|
|
|
model: taskCard.previewCount
|
|
|
|
|
|
|
|
|
|
delegate: MouseArea {
|
|
|
|
|
id: previewArea
|
|
|
|
|
|
|
|
|
|
required property int index
|
|
|
|
|
readonly property string childUuid: taskCard.winIds.length > index ? taskCard.winIds[index] : ""
|
|
|
|
|
|
|
|
|
|
width: (previewRow.width - previewRow.spacing * (taskCard.previewCount - 1)) / taskCard.previewCount
|
|
|
|
|
height: previewRow.height
|
|
|
|
|
hoverEnabled: true
|
|
|
|
|
cursorShape: Qt.PointingHandCursor
|
|
|
|
|
onClicked: taskCard.activate(index)
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
radius: Kirigami.Units.cornerRadius
|
|
|
|
|
color: root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, previewArea.containsMouse ? 0.1 : 0.06)
|
|
|
|
|
border.width: 1
|
|
|
|
|
border.pixelAligned: false
|
|
|
|
|
border.color: root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.14)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Loader {
|
|
|
|
|
id: thumbnailLoader
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
anchors.margins: 1
|
|
|
|
|
active: previewArea.childUuid !== "" && root.visible
|
|
|
|
|
sourceComponent: PipeWireThumbnail {
|
|
|
|
|
windowUuid: previewArea.childUuid
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Kirigami.Icon {
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
width: Kirigami.Units.iconSizes.large
|
|
|
|
|
height: width
|
|
|
|
|
source: taskCard.model.decoration
|
|
|
|
|
visible: !thumbnailLoader.item || !thumbnailLoader.item.hasThumbnail
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.left: parent.left
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
height: titleLabel.implicitHeight + Kirigami.Units.smallSpacing
|
|
|
|
|
radius: Kirigami.Units.cornerRadius
|
|
|
|
|
color: Qt.rgba(0, 0, 0, 0.48)
|
|
|
|
|
visible: taskCard.previewCount > 1
|
|
|
|
|
|
|
|
|
|
PC3.Label {
|
|
|
|
|
id: titleLabel
|
|
|
|
|
anchors.left: parent.left
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
|
anchors.margins: Kirigami.Units.smallSpacing
|
|
|
|
|
text: taskCard.titleForPreview(previewArea.index)
|
|
|
|
|
elide: Text.ElideRight
|
|
|
|
|
maximumLineCount: 1
|
|
|
|
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.75
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
Kirigami.Icon {
|
|
|
|
|
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
|
|
|
|
|
Layout.preferredHeight: Layout.preferredWidth
|
|
|
|
|
source: taskCard.model.decoration
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ColumnLayout {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
spacing: 0
|
|
|
|
|
|
|
|
|
|
PC3.Label {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
text: taskCard.model.display || ""
|
|
|
|
|
font.weight: taskCard.activeTask ? Font.Medium : Font.Normal
|
|
|
|
|
elide: Text.ElideRight
|
|
|
|
|
maximumLineCount: 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Row {
|
|
|
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
PC3.Label {
|
|
|
|
|
text: taskCard.activeTask ? i18n("Active") : taskCard.minimizedTask ? i18n("Minimized") : i18n("Open")
|
|
|
|
|
opacity: 0.65
|
|
|
|
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.75
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PC3.Label {
|
|
|
|
|
visible: taskCard.maximizedTask
|
|
|
|
|
text: i18n("Maximized")
|
|
|
|
|
opacity: 0.65
|
|
|
|
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.75
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PC3.Label {
|
|
|
|
|
visible: taskCard.winIds.length > 1
|
|
|
|
|
text: i18np("%1 window", "%1 windows", taskCard.winIds.length)
|
|
|
|
|
opacity: 0.65
|
|
|
|
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.75
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RowLayout {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
|
|
|
|
|
|
Item { Layout.fillWidth: true }
|
|
|
|
|
|
2026-05-04 18:34:54 +00:00
|
|
|
RunningAppsPanelButton {
|
2026-04-29 10:10:31 +00:00
|
|
|
iconName: taskCard.pinned ? "emblem-favorite" : "window-pin"
|
|
|
|
|
toolTipText: taskCard.pinned ? i18n("Pinned") : i18n("Pin to Dock")
|
|
|
|
|
checked: taskCard.pinned
|
|
|
|
|
enabled: taskCard.storageId !== "" && !taskCard.pinned && !root.folio.FolioSettings.lockLayout
|
|
|
|
|
onTriggered: root.folio.FavouritesModel.addApplication(taskCard.storageId)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-04 18:34:54 +00:00
|
|
|
RunningAppsPanelButton {
|
2026-04-29 10:10:31 +00:00
|
|
|
iconName: taskCard.minimizedTask ? "window-restore" : "window-minimize"
|
|
|
|
|
toolTipText: taskCard.minimizedTask ? i18n("Restore") : i18n("Minimize")
|
|
|
|
|
onTriggered: tasksModel.requestToggleMinimized(taskCard.modelIndex)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-04 18:34:54 +00:00
|
|
|
RunningAppsPanelButton {
|
2026-04-29 10:10:31 +00:00
|
|
|
iconName: taskCard.maximizedTask ? "window-restore" : "window-maximize"
|
|
|
|
|
toolTipText: taskCard.maximizedTask ? i18n("Restore") : i18n("Maximize")
|
|
|
|
|
enabled: !taskCard.groupTask
|
2026-05-22 07:39:28 +00:00
|
|
|
onTriggered: {
|
|
|
|
|
if (taskCard.canRequestDynamicTiling) {
|
|
|
|
|
ShellSettings.Settings.requestDynamicTilingWindowAction(taskCard.windowId, "maximize-toggle")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
tasksModel.requestToggleMaximized(taskCard.modelIndex)
|
|
|
|
|
}
|
2026-04-29 10:10:31 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-04 18:34:54 +00:00
|
|
|
RunningAppsPanelButton {
|
2026-04-29 10:10:31 +00:00
|
|
|
iconName: "window-close"
|
|
|
|
|
toolTipText: taskCard.winIds.length > 1 ? i18n("Close All") : i18n("Close")
|
|
|
|
|
onTriggered: tasksModel.requestClose(taskCard.modelIndex)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
radius: Kirigami.Units.cornerRadius
|
|
|
|
|
visible: taskCard.pendingMove
|
|
|
|
|
color: root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.18)
|
|
|
|
|
border.width: 1
|
|
|
|
|
border.pixelAligned: false
|
|
|
|
|
border.color: root.mixColor(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.55)
|
|
|
|
|
|
|
|
|
|
PC3.Label {
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
width: parent.width - Kirigami.Units.gridUnit
|
|
|
|
|
text: i18n("Moving to %1", root.pendingMoveTargetName)
|
|
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
|
elide: Text.ElideRight
|
|
|
|
|
font.weight: Font.Medium
|
|
|
|
|
color: Kirigami.Theme.highlightColor
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PC3.ScrollBar.vertical: PC3.ScrollBar {
|
|
|
|
|
interactive: true
|
|
|
|
|
enabled: taskGrid.contentHeight > taskGrid.height
|
|
|
|
|
implicitWidth: Kirigami.Units.smallSpacing
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PC3.Label {
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
width: parent.width - Kirigami.Units.gridUnit * 2
|
|
|
|
|
visible: taskGrid.count === 0
|
|
|
|
|
text: i18n("No windows on this desktop")
|
|
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
|
opacity: 0.65
|
|
|
|
|
wrapMode: Text.WordWrap
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|