mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-06-11 08:57:21 +00:00
Add virtual desktop pager to convergence dock
Show compact numbered desktop buttons in the dock bar when two or more virtual desktops exist. The buttons are split symmetrically to the left and right of the centred app-icon cluster, matching Plasma Desktop pager behaviour. Click a button to switch desktops. Drag a running-task icon from the dock and release it over a desktop button to move that window to the target desktop. Right-click a task icon for a "Move to …" submenu when the pager is visible. The feature is a no-op on single-desktop sessions.
This commit is contained in:
parent
160bc97621
commit
a0bad0507f
1 changed files with 185 additions and 2 deletions
|
|
@ -86,6 +86,38 @@ MouseArea {
|
|||
property string taskPinStorageId: ""
|
||||
readonly property bool taskPinCanDrop: taskPinTargetIndex !== -1 && taskPinStorageId !== ""
|
||||
|
||||
// Virtual desktop pager (convergence mode, 2+ desktops)
|
||||
readonly property bool showPager: convergenceMode && virtualDesktopInfo.numberOfDesktops > 1
|
||||
readonly property real pagerButtonWidth: showPager ? Math.min(root.height, Kirigami.Units.gridUnit * 2.5) : 0
|
||||
readonly property int pagerLeftCount: showPager ? Math.ceil(virtualDesktopInfo.numberOfDesktops / 2) : 0
|
||||
readonly property int pagerRightCount: showPager ? virtualDesktopInfo.numberOfDesktops - pagerLeftCount : 0
|
||||
|
||||
function pagerDesktopName(index) {
|
||||
let names = virtualDesktopInfo.desktopNames
|
||||
if (names && index < names.length && String(names[index]).length > 0)
|
||||
return String(names[index])
|
||||
return i18n("Desktop %1", index + 1)
|
||||
}
|
||||
|
||||
// Returns the desktop ID of the pager button under screen-space x, or ""
|
||||
function pagerButtonDesktopAt(x) {
|
||||
if (!showPager) return ""
|
||||
let ids = virtualDesktopInfo.desktopIds
|
||||
for (let i = 0; i < pagerLeftCount; ++i) {
|
||||
let bx = navButtonWidth + i * pagerButtonWidth
|
||||
if (x >= bx && x < bx + pagerButtonWidth)
|
||||
return (ids && i < ids.length) ? String(ids[i]) : ""
|
||||
}
|
||||
for (let i = 0; i < pagerRightCount; ++i) {
|
||||
let bx = root.width - navButtonWidth - (pagerRightCount - i) * pagerButtonWidth
|
||||
if (x >= bx && x < bx + pagerButtonWidth) {
|
||||
let di = pagerLeftCount + i
|
||||
return (ids && di < ids.length) ? String(ids[di]) : ""
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
function runningTaskStorageId(taskModel) {
|
||||
var id = taskModel ? taskModel.AppId || "" : ""
|
||||
if (id && !id.endsWith(".desktop"))
|
||||
|
|
@ -256,6 +288,131 @@ MouseArea {
|
|||
}
|
||||
}
|
||||
|
||||
// ---- Virtual desktop pager: left wing (desktops 1 .. ceil(N/2)) ----
|
||||
Repeater {
|
||||
id: leftPagerRepeater
|
||||
model: root.pagerLeftCount
|
||||
|
||||
delegate: Item {
|
||||
id: leftDesktopBtn
|
||||
required property int index
|
||||
|
||||
readonly property string desktopId: {
|
||||
let ids = virtualDesktopInfo.desktopIds
|
||||
return (ids && index < ids.length) ? String(ids[index]) : ""
|
||||
}
|
||||
readonly property bool isCurrent: desktopId !== "" && String(desktopId) === String(virtualDesktopInfo.currentDesktop)
|
||||
readonly property bool isDragTarget: {
|
||||
if (root.taskPinDragIndex < 0) return false
|
||||
let cx = root.taskBaseX(root.taskPinDragIndex) + root.dockCellWidth / 2 + root.taskPinDragOffset
|
||||
return root.pagerButtonDesktopAt(cx) === desktopId
|
||||
}
|
||||
|
||||
x: root.navButtonWidth + index * root.pagerButtonWidth
|
||||
y: 0
|
||||
width: root.pagerButtonWidth
|
||||
height: root.height
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.dockItemInset
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
color: leftDesktopBtn.isCurrent || leftDesktopBtn.isDragTarget
|
||||
? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b,
|
||||
leftPagerHover.containsMouse || leftDesktopBtn.isDragTarget ? 0.25 : 0.18)
|
||||
: root.dockItemColor(leftPagerHover.containsPress, leftPagerHover.containsMouse, false)
|
||||
Behavior on color { ColorAnimation { duration: Kirigami.Units.shortDuration } }
|
||||
}
|
||||
|
||||
PC3.Label {
|
||||
anchors.centerIn: parent
|
||||
text: (leftDesktopBtn.index + 1).toString()
|
||||
color: leftDesktopBtn.isCurrent ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor
|
||||
font.pixelSize: Math.round(parent.height * 0.3)
|
||||
font.bold: leftDesktopBtn.isCurrent
|
||||
}
|
||||
|
||||
PC3.ToolTip {
|
||||
visible: leftPagerHover.containsMouse
|
||||
text: root.pagerDesktopName(leftDesktopBtn.index)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: leftPagerHover
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (leftDesktopBtn.desktopId)
|
||||
root.folio.activateVirtualDesktop(leftDesktopBtn.desktopId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Virtual desktop pager: right wing (desktops ceil(N/2)+1 .. N) ----
|
||||
Repeater {
|
||||
id: rightPagerRepeater
|
||||
model: root.pagerRightCount
|
||||
|
||||
delegate: Item {
|
||||
id: rightDesktopBtn
|
||||
required property int index
|
||||
|
||||
readonly property int desktopIndex: root.pagerLeftCount + index
|
||||
readonly property string desktopId: {
|
||||
let ids = virtualDesktopInfo.desktopIds
|
||||
return (ids && desktopIndex < ids.length) ? String(ids[desktopIndex]) : ""
|
||||
}
|
||||
readonly property bool isCurrent: desktopId !== "" && String(desktopId) === String(virtualDesktopInfo.currentDesktop)
|
||||
readonly property bool isDragTarget: {
|
||||
if (root.taskPinDragIndex < 0) return false
|
||||
let cx = root.taskBaseX(root.taskPinDragIndex) + root.dockCellWidth / 2 + root.taskPinDragOffset
|
||||
return root.pagerButtonDesktopAt(cx) === desktopId
|
||||
}
|
||||
|
||||
x: root.width - root.navButtonWidth - (root.pagerRightCount - index) * root.pagerButtonWidth
|
||||
y: 0
|
||||
width: root.pagerButtonWidth
|
||||
height: root.height
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.dockItemInset
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
color: rightDesktopBtn.isCurrent || rightDesktopBtn.isDragTarget
|
||||
? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b,
|
||||
rightPagerHover.containsMouse || rightDesktopBtn.isDragTarget ? 0.25 : 0.18)
|
||||
: root.dockItemColor(rightPagerHover.containsPress, rightPagerHover.containsMouse, false)
|
||||
Behavior on color { ColorAnimation { duration: Kirigami.Units.shortDuration } }
|
||||
}
|
||||
|
||||
PC3.Label {
|
||||
anchors.centerIn: parent
|
||||
text: (rightDesktopBtn.desktopIndex + 1).toString()
|
||||
color: rightDesktopBtn.isCurrent ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor
|
||||
font.pixelSize: Math.round(parent.height * 0.3)
|
||||
font.bold: rightDesktopBtn.isCurrent
|
||||
}
|
||||
|
||||
PC3.ToolTip {
|
||||
visible: rightPagerHover.containsMouse
|
||||
text: root.pagerDesktopName(rightDesktopBtn.desktopIndex)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: rightPagerHover
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (rightDesktopBtn.desktopId)
|
||||
root.folio.activateVirtualDesktop(rightDesktopBtn.desktopId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TaskManager.VirtualDesktopInfo {
|
||||
id: virtualDesktopInfo
|
||||
}
|
||||
|
|
@ -1037,7 +1194,9 @@ MouseArea {
|
|||
target: null
|
||||
xAxis.enabled: true
|
||||
yAxis.enabled: false
|
||||
enabled: root.convergenceMode && taskDelegate.isLocationBottom && !folio.FolioSettings.lockLayout && taskDelegate.taskStorageId !== "" && !folio.FavouritesModel.containsApplication(taskDelegate.taskStorageId)
|
||||
// Enable for unpinned tasks (pin-to-dock drag) and for ALL tasks
|
||||
// when the pager is showing so windows can be dragged to a desktop button.
|
||||
enabled: root.convergenceMode && taskDelegate.isLocationBottom && !folio.FolioSettings.lockLayout && taskDelegate.taskStorageId !== "" && (root.showPager || !folio.FavouritesModel.containsApplication(taskDelegate.taskStorageId))
|
||||
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
|
|
@ -1050,7 +1209,12 @@ MouseArea {
|
|||
root.taskPinTargetIndex = -1
|
||||
root.taskPinStorageId = taskDelegate.taskStorageId
|
||||
} else if (root.taskPinDragIndex === taskDelegate.index) {
|
||||
if (root.taskPinCanDrop) {
|
||||
// If released over a pager button, move the window to that desktop.
|
||||
let finalCenterX = root.taskBaseX(taskDelegate.index) + root.dockCellWidth / 2 + root.taskPinDragOffset
|
||||
let pagerDesktop = root.pagerButtonDesktopAt(finalCenterX)
|
||||
if (pagerDesktop && taskDelegate.model.IsVirtualDesktopsChangeable === true) {
|
||||
tasksModel.requestVirtualDesktops(tasksModel.makeModelIndex(taskDelegate.index), [pagerDesktop])
|
||||
} else if (root.taskPinCanDrop && !folio.FavouritesModel.containsApplication(root.taskPinStorageId)) {
|
||||
folio.FavouritesModel.addApplicationAt(root.taskPinTargetIndex, root.taskPinStorageId)
|
||||
}
|
||||
root.clearTaskPinDrag()
|
||||
|
|
@ -1172,6 +1336,25 @@ MouseArea {
|
|||
}
|
||||
onClicked: tasksModel.requestClose(tasksModel.makeModelIndex(taskDelegate.index))
|
||||
}
|
||||
|
||||
Controls.MenuSeparator {
|
||||
visible: root.showPager && taskDelegate.model.IsVirtualDesktopsChangeable === true
|
||||
}
|
||||
|
||||
Instantiator {
|
||||
model: root.showPager && taskDelegate.model.IsVirtualDesktopsChangeable === true
|
||||
? virtualDesktopInfo.desktopIds : []
|
||||
delegate: PC3.MenuItem {
|
||||
required property int index
|
||||
required property var modelData
|
||||
text: i18n("Move to %1", root.pagerDesktopName(index))
|
||||
enabled: String(modelData) !== String(virtualDesktopInfo.currentDesktop)
|
||||
onTriggered: tasksModel.requestVirtualDesktops(
|
||||
tasksModel.makeModelIndex(taskDelegate.index), [modelData])
|
||||
}
|
||||
onObjectAdded: (idx, obj) => taskContextMenu.insertItem(taskContextMenu.count, obj)
|
||||
onObjectRemoved: (idx, obj) => taskContextMenu.removeItem(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue