mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-30 00:12:50 -05:00
dock: improve pinned app re-ordering feedback, fix vertical dock
ordering fixes #1046 fixes #938
This commit is contained in:
@@ -19,9 +19,10 @@ Item {
|
|||||||
property bool longPressing: false
|
property bool longPressing: false
|
||||||
property bool dragging: false
|
property bool dragging: false
|
||||||
property point dragStartPos: Qt.point(0, 0)
|
property point dragStartPos: Qt.point(0, 0)
|
||||||
property point dragOffset: Qt.point(0, 0)
|
property real dragAxisOffset: 0
|
||||||
property int targetIndex: -1
|
property int targetIndex: -1
|
||||||
property int originalIndex: -1
|
property int originalIndex: -1
|
||||||
|
property bool isVertical: SettingsData.dockPosition === SettingsData.Position.Left || SettingsData.dockPosition === SettingsData.Position.Right
|
||||||
property bool showWindowTitle: false
|
property bool showWindowTitle: false
|
||||||
property string windowTitle: ""
|
property string windowTitle: ""
|
||||||
property bool isHovered: mouseArea.containsMouse && !dragging
|
property bool isHovered: mouseArea.containsMouse && !dragging
|
||||||
@@ -95,7 +96,7 @@ Item {
|
|||||||
return appData?.allWindows?.map(w => w.toplevel).filter(t => t !== null) || [];
|
return appData?.allWindows?.map(w => w.toplevel).filter(t => t !== null) || [];
|
||||||
}
|
}
|
||||||
onIsHoveredChanged: {
|
onIsHoveredChanged: {
|
||||||
if (mouseArea.pressed)
|
if (mouseArea.pressed || dragging)
|
||||||
return;
|
return;
|
||||||
if (isHovered) {
|
if (isHovered) {
|
||||||
exitAnimation.stop();
|
exitAnimation.stop();
|
||||||
@@ -128,8 +129,8 @@ Item {
|
|||||||
running: false
|
running: false
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: iconTransform
|
target: root
|
||||||
property: animateX ? "x" : "y"
|
property: "hoverAnimOffset"
|
||||||
to: animationDirection * animationDistance * 0.25
|
to: animationDirection * animationDistance * 0.25
|
||||||
duration: Anims.durShort
|
duration: Anims.durShort
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
@@ -137,8 +138,8 @@ Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
target: iconTransform
|
target: root
|
||||||
property: animateX ? "x" : "y"
|
property: "hoverAnimOffset"
|
||||||
to: animationDirection * animationDistance * 0.2
|
to: animationDirection * animationDistance * 0.2
|
||||||
duration: Anims.durShort
|
duration: Anims.durShort
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
@@ -150,8 +151,8 @@ Item {
|
|||||||
id: exitAnimation
|
id: exitAnimation
|
||||||
|
|
||||||
running: false
|
running: false
|
||||||
target: iconTransform
|
target: root
|
||||||
property: animateX ? "x" : "y"
|
property: "hoverAnimOffset"
|
||||||
to: 0
|
to: 0
|
||||||
duration: Anims.durShort
|
duration: Anims.durShort
|
||||||
easing.type: Easing.BezierSpline
|
easing.type: Easing.BezierSpline
|
||||||
@@ -187,16 +188,79 @@ Item {
|
|||||||
}
|
}
|
||||||
onReleased: mouse => {
|
onReleased: mouse => {
|
||||||
longPressTimer.stop();
|
longPressTimer.stop();
|
||||||
if (longPressing) {
|
|
||||||
if (dragging && targetIndex >= 0 && targetIndex !== originalIndex && dockApps) {
|
|
||||||
dockApps.movePinnedApp(originalIndex, targetIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
longPressing = false;
|
const wasDragging = dragging;
|
||||||
dragging = false;
|
const didReorder = wasDragging && targetIndex >= 0 && targetIndex !== originalIndex && dockApps;
|
||||||
dragOffset = Qt.point(0, 0);
|
|
||||||
targetIndex = -1;
|
if (didReorder)
|
||||||
originalIndex = -1;
|
dockApps.movePinnedApp(originalIndex, targetIndex);
|
||||||
|
|
||||||
|
longPressing = false;
|
||||||
|
dragging = false;
|
||||||
|
dragAxisOffset = 0;
|
||||||
|
targetIndex = -1;
|
||||||
|
originalIndex = -1;
|
||||||
|
|
||||||
|
if (dockApps && !didReorder) {
|
||||||
|
dockApps.draggedIndex = -1;
|
||||||
|
dockApps.dropTargetIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasDragging || mouse.button !== Qt.LeftButton)
|
||||||
|
return;
|
||||||
|
|
||||||
|
handleLeftClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLeftClick() {
|
||||||
|
if (!appData)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (appData.type) {
|
||||||
|
case "pinned":
|
||||||
|
if (!appData.appId)
|
||||||
|
return;
|
||||||
|
const pinnedEntry = cachedDesktopEntry;
|
||||||
|
if (pinnedEntry) {
|
||||||
|
AppUsageHistoryData.addAppUsage({
|
||||||
|
"id": appData.appId,
|
||||||
|
"name": pinnedEntry.name || appData.appId,
|
||||||
|
"icon": pinnedEntry.icon || "",
|
||||||
|
"exec": pinnedEntry.exec || "",
|
||||||
|
"comment": pinnedEntry.comment || ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
SessionService.launchDesktopEntry(pinnedEntry);
|
||||||
|
break;
|
||||||
|
case "window":
|
||||||
|
const windowToplevel = getToplevelObject();
|
||||||
|
if (windowToplevel)
|
||||||
|
windowToplevel.activate();
|
||||||
|
break;
|
||||||
|
case "grouped":
|
||||||
|
if (appData.windowCount === 0) {
|
||||||
|
if (!appData.appId)
|
||||||
|
return;
|
||||||
|
const groupedEntry = cachedDesktopEntry;
|
||||||
|
if (groupedEntry) {
|
||||||
|
AppUsageHistoryData.addAppUsage({
|
||||||
|
"id": appData.appId,
|
||||||
|
"name": groupedEntry.name || appData.appId,
|
||||||
|
"icon": groupedEntry.icon || "",
|
||||||
|
"exec": groupedEntry.exec || "",
|
||||||
|
"comment": groupedEntry.comment || ""
|
||||||
|
});
|
||||||
|
}
|
||||||
|
SessionService.launchDesktopEntry(groupedEntry);
|
||||||
|
} else if (appData.windowCount === 1) {
|
||||||
|
const groupedToplevel = getToplevelObject();
|
||||||
|
if (groupedToplevel)
|
||||||
|
groupedToplevel.activate();
|
||||||
|
} else if (contextMenu) {
|
||||||
|
const shouldHidePin = appData.appId === "org.quickshell";
|
||||||
|
contextMenu.showForButton(root, appData, root.height + 25, shouldHidePin, cachedDesktopEntry, parentDockScreen);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onPositionChanged: mouse => {
|
onPositionChanged: mouse => {
|
||||||
@@ -206,90 +270,47 @@ Item {
|
|||||||
dragging = true;
|
dragging = true;
|
||||||
targetIndex = index;
|
targetIndex = index;
|
||||||
originalIndex = index;
|
originalIndex = index;
|
||||||
|
if (dockApps) {
|
||||||
|
dockApps.draggedIndex = index;
|
||||||
|
dockApps.dropTargetIndex = index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dragging) {
|
|
||||||
dragOffset = Qt.point(mouse.x - dragStartPos.x, mouse.y - dragStartPos.y);
|
if (!dragging || !dockApps)
|
||||||
if (dockApps) {
|
return;
|
||||||
const threshold = actualIconSize;
|
|
||||||
let newTargetIndex = targetIndex;
|
const axisOffset = isVertical ? (mouse.y - dragStartPos.y) : (mouse.x - dragStartPos.x);
|
||||||
if (dragOffset.x > threshold && targetIndex < dockApps.pinnedAppCount - 1) {
|
dragAxisOffset = axisOffset;
|
||||||
newTargetIndex = targetIndex + 1;
|
|
||||||
} else if (dragOffset.x < -threshold && targetIndex > 0) {
|
const spacing = Math.min(8, Math.max(4, actualIconSize * 0.08));
|
||||||
newTargetIndex = targetIndex - 1;
|
const itemSize = actualIconSize * 1.2 + spacing;
|
||||||
}
|
const slotOffset = Math.round(axisOffset / itemSize);
|
||||||
if (newTargetIndex !== targetIndex) {
|
const newTargetIndex = Math.max(0, Math.min(dockApps.pinnedAppCount - 1, originalIndex + slotOffset));
|
||||||
targetIndex = newTargetIndex;
|
|
||||||
dragStartPos = Qt.point(mouse.x, mouse.y);
|
if (newTargetIndex !== targetIndex) {
|
||||||
}
|
targetIndex = newTargetIndex;
|
||||||
}
|
dockApps.dropTargetIndex = newTargetIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onClicked: mouse => {
|
onClicked: mouse => {
|
||||||
if (!appData || longPressing) {
|
if (!appData)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (mouse.button === Qt.LeftButton) {
|
if (mouse.button === Qt.MiddleButton) {
|
||||||
if (appData.type === "pinned") {
|
switch (appData.type) {
|
||||||
if (appData && appData.appId) {
|
case "window":
|
||||||
const desktopEntry = cachedDesktopEntry;
|
appData.toplevel?.close();
|
||||||
if (desktopEntry) {
|
break;
|
||||||
AppUsageHistoryData.addAppUsage({
|
case "grouped":
|
||||||
"id": appData.appId,
|
|
||||||
"name": desktopEntry.name || appData.appId,
|
|
||||||
"icon": desktopEntry.icon || "",
|
|
||||||
"exec": desktopEntry.exec || "",
|
|
||||||
"comment": desktopEntry.comment || ""
|
|
||||||
});
|
|
||||||
}
|
|
||||||
SessionService.launchDesktopEntry(desktopEntry);
|
|
||||||
}
|
|
||||||
} else if (appData.type === "window") {
|
|
||||||
const toplevel = getToplevelObject();
|
|
||||||
if (toplevel) {
|
|
||||||
toplevel.activate();
|
|
||||||
}
|
|
||||||
} else if (appData.type === "grouped") {
|
|
||||||
if (appData.windowCount === 0) {
|
|
||||||
if (appData && appData.appId) {
|
|
||||||
const desktopEntry = cachedDesktopEntry;
|
|
||||||
if (desktopEntry) {
|
|
||||||
AppUsageHistoryData.addAppUsage({
|
|
||||||
"id": appData.appId,
|
|
||||||
"name": desktopEntry.name || appData.appId,
|
|
||||||
"icon": desktopEntry.icon || "",
|
|
||||||
"exec": desktopEntry.exec || "",
|
|
||||||
"comment": desktopEntry.comment || ""
|
|
||||||
});
|
|
||||||
}
|
|
||||||
SessionService.launchDesktopEntry(desktopEntry);
|
|
||||||
}
|
|
||||||
} else if (appData.windowCount === 1) {
|
|
||||||
// For single window, activate directly
|
|
||||||
const toplevel = getToplevelObject();
|
|
||||||
if (toplevel) {
|
|
||||||
console.log("Activating grouped app window:", appData.windowTitle);
|
|
||||||
toplevel.activate();
|
|
||||||
} else {
|
|
||||||
console.warn("No toplevel found for grouped app");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (contextMenu) {
|
|
||||||
const shouldHidePin = appData.appId === "org.quickshell";
|
|
||||||
contextMenu.showForButton(root, appData, root.height + 25, shouldHidePin, cachedDesktopEntry, parentDockScreen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (mouse.button === Qt.MiddleButton) {
|
|
||||||
if (appData?.type === "window") {
|
|
||||||
appData?.toplevel?.close();
|
|
||||||
} else if (appData?.type === "grouped") {
|
|
||||||
if (contextMenu) {
|
if (contextMenu) {
|
||||||
const shouldHidePin = appData.appId === "org.quickshell";
|
const shouldHidePin = appData.appId === "org.quickshell";
|
||||||
contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen);
|
contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen);
|
||||||
}
|
}
|
||||||
} else if (appData && appData.appId) {
|
break;
|
||||||
|
default:
|
||||||
|
if (!appData.appId)
|
||||||
|
return;
|
||||||
const desktopEntry = cachedDesktopEntry;
|
const desktopEntry = cachedDesktopEntry;
|
||||||
if (desktopEntry) {
|
if (desktopEntry) {
|
||||||
AppUsageHistoryData.addAppUsage({
|
AppUsageHistoryData.addAppUsage({
|
||||||
@@ -301,26 +322,39 @@ Item {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
SessionService.launchDesktopEntry(desktopEntry);
|
SessionService.launchDesktopEntry(desktopEntry);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else if (mouse.button === Qt.RightButton) {
|
} else if (mouse.button === Qt.RightButton) {
|
||||||
if (contextMenu && appData) {
|
if (!contextMenu)
|
||||||
const shouldHidePin = appData.appId === "org.quickshell";
|
return;
|
||||||
contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen);
|
const shouldHidePin = appData.appId === "org.quickshell";
|
||||||
} else {
|
contextMenu.showForButton(root, appData, root.height, shouldHidePin, cachedDesktopEntry, parentDockScreen);
|
||||||
console.warn("No context menu or appData available");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property real hoverAnimOffset: 0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: visualContent
|
id: visualContent
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
transform: Translate {
|
transform: Translate {
|
||||||
id: iconTransform
|
id: iconTransform
|
||||||
x: 0
|
x: {
|
||||||
y: 0
|
if (dragging && !isVertical)
|
||||||
|
return dragAxisOffset;
|
||||||
|
if (!dragging && isVertical)
|
||||||
|
return hoverAnimOffset;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
y: {
|
||||||
|
if (dragging && isVertical)
|
||||||
|
return dragAxisOffset;
|
||||||
|
if (!dragging && !isVertical)
|
||||||
|
return hoverAnimOffset;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Wayland
|
|
||||||
import Quickshell.Hyprland
|
import Quickshell.Hyprland
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
@@ -17,6 +14,9 @@ Item {
|
|||||||
property bool isVertical: false
|
property bool isVertical: false
|
||||||
property var dockScreen: null
|
property var dockScreen: null
|
||||||
property real iconSize: 40
|
property real iconSize: 40
|
||||||
|
property int draggedIndex: -1
|
||||||
|
property int dropTargetIndex: -1
|
||||||
|
property bool suppressShiftAnimation: false
|
||||||
|
|
||||||
clip: false
|
clip: false
|
||||||
implicitWidth: isVertical ? appLayout.height : appLayout.width
|
implicitWidth: isVertical ? appLayout.height : appLayout.width
|
||||||
@@ -24,18 +24,18 @@ Item {
|
|||||||
|
|
||||||
function movePinnedApp(fromIndex, toIndex) {
|
function movePinnedApp(fromIndex, toIndex) {
|
||||||
if (fromIndex === toIndex) {
|
if (fromIndex === toIndex) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentPinned = [...(SessionData.pinnedApps || [])]
|
const currentPinned = [...(SessionData.pinnedApps || [])];
|
||||||
if (fromIndex < 0 || fromIndex >= currentPinned.length || toIndex < 0 || toIndex >= currentPinned.length) {
|
if (fromIndex < 0 || fromIndex >= currentPinned.length || toIndex < 0 || toIndex >= currentPinned.length) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const movedApp = currentPinned.splice(fromIndex, 1)[0]
|
const movedApp = currentPinned.splice(fromIndex, 1)[0];
|
||||||
currentPinned.splice(toIndex, 0, movedApp)
|
currentPinned.splice(toIndex, 0, movedApp);
|
||||||
|
|
||||||
SessionData.setPinnedApps(currentPinned)
|
SessionData.setPinnedApps(currentPinned);
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
@@ -53,202 +53,250 @@ Item {
|
|||||||
flow: root.isVertical ? Flow.TopToBottom : Flow.LeftToRight
|
flow: root.isVertical ? Flow.TopToBottom : Flow.LeftToRight
|
||||||
spacing: Math.min(8, Math.max(4, root.iconSize * 0.08))
|
spacing: Math.min(8, Math.max(4, root.iconSize * 0.08))
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: repeater
|
id: repeater
|
||||||
|
|
||||||
property var dockItems: []
|
property var dockItems: []
|
||||||
|
|
||||||
model: ScriptModel {
|
model: ScriptModel {
|
||||||
values: repeater.dockItems
|
values: repeater.dockItems
|
||||||
objectProp: "uniqueKey"
|
objectProp: "uniqueKey"
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: updateModel()
|
Component.onCompleted: updateModel()
|
||||||
|
|
||||||
function updateModel() {
|
function updateModel() {
|
||||||
const items = []
|
const items = [];
|
||||||
const pinnedApps = [...(SessionData.pinnedApps || [])]
|
const pinnedApps = [...(SessionData.pinnedApps || [])];
|
||||||
const sortedToplevels = CompositorService.sortedToplevels
|
const sortedToplevels = CompositorService.sortedToplevels;
|
||||||
|
|
||||||
if (root.groupByApp) {
|
if (root.groupByApp) {
|
||||||
const appGroups = new Map()
|
const appGroups = new Map();
|
||||||
|
|
||||||
pinnedApps.forEach(rawAppId => {
|
pinnedApps.forEach(rawAppId => {
|
||||||
const appId = Paths.moddedAppId(rawAppId)
|
const appId = Paths.moddedAppId(rawAppId);
|
||||||
appGroups.set(appId, {
|
|
||||||
appId: appId,
|
|
||||||
isPinned: true,
|
|
||||||
windows: []
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
sortedToplevels.forEach((toplevel, index) => {
|
|
||||||
const rawAppId = toplevel.appId || "unknown"
|
|
||||||
const appId = Paths.moddedAppId(rawAppId)
|
|
||||||
if (!appGroups.has(appId)) {
|
|
||||||
appGroups.set(appId, {
|
appGroups.set(appId, {
|
||||||
appId: appId,
|
appId: appId,
|
||||||
isPinned: false,
|
isPinned: true,
|
||||||
windows: []
|
windows: []
|
||||||
})
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sortedToplevels.forEach((toplevel, index) => {
|
||||||
|
const rawAppId = toplevel.appId || "unknown";
|
||||||
|
const appId = Paths.moddedAppId(rawAppId);
|
||||||
|
if (!appGroups.has(appId)) {
|
||||||
|
appGroups.set(appId, {
|
||||||
|
appId: appId,
|
||||||
|
isPinned: false,
|
||||||
|
windows: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
appGroups.get(appId).windows.push({
|
||||||
|
toplevel: toplevel,
|
||||||
|
index: index
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const pinnedGroups = [];
|
||||||
|
const unpinnedGroups = [];
|
||||||
|
|
||||||
|
Array.from(appGroups.entries()).forEach(([appId, group]) => {
|
||||||
|
const firstWindow = group.windows.length > 0 ? group.windows[0] : null;
|
||||||
|
|
||||||
|
const item = {
|
||||||
|
uniqueKey: "grouped_" + appId,
|
||||||
|
type: "grouped",
|
||||||
|
appId: appId,
|
||||||
|
toplevel: firstWindow ? firstWindow.toplevel : null,
|
||||||
|
isPinned: group.isPinned,
|
||||||
|
isRunning: group.windows.length > 0,
|
||||||
|
windowCount: group.windows.length,
|
||||||
|
allWindows: group.windows
|
||||||
|
};
|
||||||
|
|
||||||
|
if (group.isPinned) {
|
||||||
|
pinnedGroups.push(item);
|
||||||
|
} else {
|
||||||
|
unpinnedGroups.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pinnedGroups.forEach(item => items.push(item));
|
||||||
|
|
||||||
|
if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) {
|
||||||
|
items.push({
|
||||||
|
uniqueKey: "separator_grouped",
|
||||||
|
type: "separator",
|
||||||
|
appId: "__SEPARATOR__",
|
||||||
|
toplevel: null,
|
||||||
|
isPinned: false,
|
||||||
|
isRunning: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
appGroups.get(appId).windows.push({
|
unpinnedGroups.forEach(item => items.push(item));
|
||||||
toplevel: toplevel,
|
root.pinnedAppCount = pinnedGroups.length;
|
||||||
index: index
|
} else {
|
||||||
})
|
pinnedApps.forEach(rawAppId => {
|
||||||
})
|
const appId = Paths.moddedAppId(rawAppId);
|
||||||
|
items.push({
|
||||||
|
uniqueKey: "pinned_" + appId,
|
||||||
|
type: "pinned",
|
||||||
|
appId: appId,
|
||||||
|
toplevel: null,
|
||||||
|
isPinned: true,
|
||||||
|
isRunning: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const pinnedGroups = []
|
root.pinnedAppCount = pinnedApps.length;
|
||||||
const unpinnedGroups = []
|
|
||||||
|
|
||||||
Array.from(appGroups.entries()).forEach(([appId, group]) => {
|
if (pinnedApps.length > 0 && sortedToplevels.length > 0) {
|
||||||
const firstWindow = group.windows.length > 0 ? group.windows[0] : null
|
items.push({
|
||||||
|
uniqueKey: "separator_ungrouped",
|
||||||
const item = {
|
type: "separator",
|
||||||
uniqueKey: "grouped_" + appId,
|
appId: "__SEPARATOR__",
|
||||||
type: "grouped",
|
toplevel: null,
|
||||||
appId: appId,
|
isPinned: false,
|
||||||
toplevel: firstWindow ? firstWindow.toplevel : null,
|
isRunning: false
|
||||||
isPinned: group.isPinned,
|
});
|
||||||
isRunning: group.windows.length > 0,
|
|
||||||
windowCount: group.windows.length,
|
|
||||||
allWindows: group.windows
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (group.isPinned) {
|
sortedToplevels.forEach((toplevel, index) => {
|
||||||
pinnedGroups.push(item)
|
let uniqueKey = "window_" + index;
|
||||||
} else {
|
if (CompositorService.isHyprland && Hyprland.toplevels) {
|
||||||
unpinnedGroups.push(item)
|
const hyprlandToplevels = Array.from(Hyprland.toplevels.values);
|
||||||
}
|
for (let i = 0; i < hyprlandToplevels.length; i++) {
|
||||||
})
|
if (hyprlandToplevels[i].wayland === toplevel) {
|
||||||
|
uniqueKey = "window_" + hyprlandToplevels[i].address;
|
||||||
pinnedGroups.forEach(item => items.push(item))
|
break;
|
||||||
|
}
|
||||||
if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) {
|
|
||||||
items.push({
|
|
||||||
uniqueKey: "separator_grouped",
|
|
||||||
type: "separator",
|
|
||||||
appId: "__SEPARATOR__",
|
|
||||||
toplevel: null,
|
|
||||||
isPinned: false,
|
|
||||||
isRunning: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
unpinnedGroups.forEach(item => items.push(item))
|
|
||||||
root.pinnedAppCount = pinnedGroups.length
|
|
||||||
} else {
|
|
||||||
pinnedApps.forEach(rawAppId => {
|
|
||||||
const appId = Paths.moddedAppId(rawAppId)
|
|
||||||
items.push({
|
|
||||||
uniqueKey: "pinned_" + appId,
|
|
||||||
type: "pinned",
|
|
||||||
appId: appId,
|
|
||||||
toplevel: null,
|
|
||||||
isPinned: true,
|
|
||||||
isRunning: false
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
root.pinnedAppCount = pinnedApps.length
|
|
||||||
|
|
||||||
if (pinnedApps.length > 0 && sortedToplevels.length > 0) {
|
|
||||||
items.push({
|
|
||||||
uniqueKey: "separator_ungrouped",
|
|
||||||
type: "separator",
|
|
||||||
appId: "__SEPARATOR__",
|
|
||||||
toplevel: null,
|
|
||||||
isPinned: false,
|
|
||||||
isRunning: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
sortedToplevels.forEach((toplevel, index) => {
|
|
||||||
let uniqueKey = "window_" + index
|
|
||||||
if (CompositorService.isHyprland && Hyprland.toplevels) {
|
|
||||||
const hyprlandToplevels = Array.from(Hyprland.toplevels.values)
|
|
||||||
for (let i = 0; i < hyprlandToplevels.length; i++) {
|
|
||||||
if (hyprlandToplevels[i].wayland === toplevel) {
|
|
||||||
uniqueKey = "window_" + hyprlandToplevels[i].address
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
uniqueKey: uniqueKey,
|
||||||
|
type: "window",
|
||||||
|
appId: Paths.moddedAppId(toplevel.appId),
|
||||||
|
toplevel: toplevel,
|
||||||
|
isPinned: false,
|
||||||
|
isRunning: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
dockItems = items;
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
id: delegateItem
|
||||||
|
property alias dockButton: button
|
||||||
|
property var itemData: modelData
|
||||||
|
clip: false
|
||||||
|
z: button.dragging ? 100 : 0
|
||||||
|
|
||||||
|
width: itemData.type === "separator" ? (root.isVertical ? root.iconSize : 8) : (root.isVertical ? root.iconSize : root.iconSize * 1.2)
|
||||||
|
height: itemData.type === "separator" ? (root.isVertical ? 8 : root.iconSize) : (root.isVertical ? root.iconSize * 1.2 : root.iconSize)
|
||||||
|
|
||||||
|
property real shiftOffset: {
|
||||||
|
if (root.draggedIndex < 0 || !itemData.isPinned || itemData.type === "separator")
|
||||||
|
return 0;
|
||||||
|
if (model.index === root.draggedIndex)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const dragIdx = root.draggedIndex;
|
||||||
|
const dropIdx = root.dropTargetIndex;
|
||||||
|
const myIdx = model.index;
|
||||||
|
const shiftAmount = root.iconSize * 1.2 + layoutFlow.spacing;
|
||||||
|
|
||||||
|
if (dropIdx < 0)
|
||||||
|
return 0;
|
||||||
|
if (dragIdx < dropIdx && myIdx > dragIdx && myIdx <= dropIdx)
|
||||||
|
return -shiftAmount;
|
||||||
|
if (dragIdx > dropIdx && myIdx >= dropIdx && myIdx < dragIdx)
|
||||||
|
return shiftAmount;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform: Translate {
|
||||||
|
x: root.isVertical ? 0 : delegateItem.shiftOffset
|
||||||
|
y: root.isVertical ? delegateItem.shiftOffset : 0
|
||||||
|
|
||||||
|
Behavior on x {
|
||||||
|
enabled: !root.suppressShiftAnimation
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push({
|
Behavior on y {
|
||||||
uniqueKey: uniqueKey,
|
enabled: !root.suppressShiftAnimation
|
||||||
type: "window",
|
NumberAnimation {
|
||||||
appId: Paths.moddedAppId(toplevel.appId),
|
duration: 150
|
||||||
toplevel: toplevel,
|
easing.type: Easing.OutCubic
|
||||||
isPinned: false,
|
}
|
||||||
isRunning: true
|
}
|
||||||
})
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
dockItems = items
|
Rectangle {
|
||||||
}
|
visible: itemData.type === "separator"
|
||||||
|
width: root.isVertical ? root.iconSize * 0.5 : 2
|
||||||
|
height: root.isVertical ? 2 : root.iconSize * 0.5
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
|
||||||
|
radius: 1
|
||||||
|
anchors.centerIn: parent
|
||||||
|
}
|
||||||
|
|
||||||
delegate: Item {
|
DockAppButton {
|
||||||
id: delegateItem
|
id: button
|
||||||
property alias dockButton: button
|
visible: itemData.type !== "separator"
|
||||||
property var itemData: modelData
|
anchors.centerIn: parent
|
||||||
clip: false
|
|
||||||
|
|
||||||
width: itemData.type === "separator" ? (root.isVertical ? root.iconSize : 8) : (root.isVertical ? root.iconSize : root.iconSize * 1.2)
|
width: delegateItem.width
|
||||||
height: itemData.type === "separator" ? (root.isVertical ? 8 : root.iconSize) : (root.isVertical ? root.iconSize * 1.2 : root.iconSize)
|
height: delegateItem.height
|
||||||
|
actualIconSize: root.iconSize
|
||||||
|
|
||||||
Rectangle {
|
appData: itemData
|
||||||
visible: itemData.type === "separator"
|
contextMenu: root.contextMenu
|
||||||
width: root.isVertical ? root.iconSize * 0.5 : 2
|
dockApps: root
|
||||||
height: root.isVertical ? 2 : root.iconSize * 0.5
|
index: model.index
|
||||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
|
parentDockScreen: root.dockScreen
|
||||||
radius: 1
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
DockAppButton {
|
showWindowTitle: itemData?.type === "window" || itemData?.type === "grouped"
|
||||||
id: button
|
windowTitle: {
|
||||||
visible: itemData.type !== "separator"
|
const title = itemData?.toplevel?.title || "(Unnamed)";
|
||||||
anchors.centerIn: parent
|
return title.length > 50 ? title.substring(0, 47) + "..." : title;
|
||||||
|
}
|
||||||
width: delegateItem.width
|
|
||||||
height: delegateItem.height
|
|
||||||
actualIconSize: root.iconSize
|
|
||||||
|
|
||||||
appData: itemData
|
|
||||||
contextMenu: root.contextMenu
|
|
||||||
dockApps: root
|
|
||||||
index: model.index
|
|
||||||
parentDockScreen: root.dockScreen
|
|
||||||
|
|
||||||
showWindowTitle: itemData?.type === "window" || itemData?.type === "grouped"
|
|
||||||
windowTitle: {
|
|
||||||
const title = itemData?.toplevel?.title || "(Unnamed)"
|
|
||||||
return title.length > 50 ? title.substring(0, 47) + "..." : title
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: CompositorService
|
target: CompositorService
|
||||||
function onToplevelsChanged() {
|
function onToplevelsChanged() {
|
||||||
repeater.updateModel()
|
repeater.updateModel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: SessionData
|
target: SessionData
|
||||||
function onPinnedAppsChanged() {
|
function onPinnedAppsChanged() {
|
||||||
repeater.updateModel()
|
root.suppressShiftAnimation = true;
|
||||||
|
root.draggedIndex = -1;
|
||||||
|
root.dropTargetIndex = -1;
|
||||||
|
repeater.updateModel();
|
||||||
|
Qt.callLater(() => {
|
||||||
|
root.suppressShiftAnimation = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onGroupByAppChanged: {
|
onGroupByAppChanged: {
|
||||||
repeater.updateModel()
|
repeater.updateModel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user