1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-29 07:52:50 -05:00

dock: track hyprland addresses, fix closing, use ScriptModel

This commit is contained in:
bbedward
2025-11-10 12:26:14 -05:00
parent af95631a1d
commit cc02d09c4d
3 changed files with 135 additions and 205 deletions

View File

@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Widgets import Quickshell.Widgets
import qs.Common import qs.Common
import qs.Services import qs.Services
@@ -91,56 +92,11 @@ Item {
} }
function getToplevelObject() { function getToplevelObject() {
if (!appData) { return appData?.toplevel || null
return null
}
const sortedToplevels = CompositorService.sortedToplevels
if (!sortedToplevels) {
return null
}
if (appData.type === "window") {
if (appData.uniqueId) {
for (var i = 0; i < sortedToplevels.length; i++) {
const toplevel = sortedToplevels[i]
const checkId = toplevel.title + "|" + (toplevel.appId || "") + "|" + i
if (checkId === appData.uniqueId) {
return toplevel
}
}
}
if (appData.windowId !== undefined && appData.windowId !== null && appData.windowId >= 0) {
if (appData.windowId < sortedToplevels.length) {
return sortedToplevels[appData.windowId]
}
}
} else if (appData.type === "grouped") {
if (appData.windowId !== undefined && appData.windowId !== null && appData.windowId >= 0) {
if (appData.windowId < sortedToplevels.length) {
return sortedToplevels[appData.windowId]
}
}
}
return null
} }
function getGroupedToplevels() { function getGroupedToplevels() {
if (!appData || appData.type !== "grouped") { return appData?.allWindows?.map(w => w.toplevel).filter(t => t !== null) || []
return []
}
const toplevels = []
const allToplevels = ToplevelManager.toplevels.values
for (let i = 0; i < allToplevels.length; i++) {
const toplevel = allToplevels[i]
if (toplevel.appId === appData.appId) {
toplevels.push(toplevel)
}
}
return toplevels
} }
onIsHoveredChanged: { onIsHoveredChanged: {
if (mouseArea.pressed) return if (mouseArea.pressed) return
@@ -325,17 +281,9 @@ Item {
} }
} }
} else if (mouse.button === Qt.MiddleButton) { } else if (mouse.button === Qt.MiddleButton) {
if (appData && appData.type === "window") { if (appData?.type === "window") {
const sortedToplevels = CompositorService.sortedToplevels appData?.toplevel?.close()
for (var i = 0; i < sortedToplevels.length; i++) { } else if (appData?.type === "grouped") {
const toplevel = sortedToplevels[i]
const checkId = toplevel.title + "|" + (toplevel.appId || "") + "|" + i
if (checkId === appData.uniqueId) {
toplevel.close()
break
}
}
} else if (appData && appData.type === "grouped") {
if (contextMenu) { if (contextMenu) {
contextMenu.showForButton(root, appData, root.height, false, cachedDesktopEntry, parentDockScreen) contextMenu.showForButton(root, appData, root.height, false, cachedDesktopEntry, parentDockScreen)
} }

View File

@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland
import qs.Common import qs.Common
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
@@ -54,23 +55,24 @@ Item {
Repeater { Repeater {
id: repeater id: repeater
model: ListModel {
id: dockModel property var dockItems: []
model: ScriptModel {
values: repeater.dockItems
objectProp: "uniqueKey"
}
Component.onCompleted: updateModel() Component.onCompleted: updateModel()
function updateModel() { function updateModel() {
clear()
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) {
// Group windows by appId
const appGroups = new Map() const appGroups = new Map()
// Add pinned apps first (even if they have no windows)
pinnedApps.forEach(appId => { pinnedApps.forEach(appId => {
appGroups.set(appId, { appGroups.set(appId, {
appId: appId, appId: appId,
@@ -79,7 +81,6 @@ Item {
}) })
}) })
// Group all running windows by appId
sortedToplevels.forEach((toplevel, index) => { sortedToplevels.forEach((toplevel, index) => {
const appId = toplevel.appId || "unknown" const appId = toplevel.appId || "unknown"
if (!appGroups.has(appId)) { if (!appGroups.has(appId)) {
@@ -89,36 +90,28 @@ Item {
windows: [] windows: []
}) })
} }
const title = toplevel.title || "(Unnamed)"
const truncatedTitle = title.length > 50 ? title.substring(0, 47) + "..." : title
const uniqueId = toplevel.title + "|" + (toplevel.appId || "") + "|" + index
appGroups.get(appId).windows.push({ appGroups.get(appId).windows.push({
windowId: index, toplevel: toplevel,
windowTitle: truncatedTitle, index: index
uniqueId: uniqueId
}) })
}) })
// Sort groups: pinned first, then unpinned
const pinnedGroups = [] const pinnedGroups = []
const unpinnedGroups = [] const unpinnedGroups = []
Array.from(appGroups.entries()).forEach(([appId, group]) => { Array.from(appGroups.entries()).forEach(([appId, group]) => {
// For grouped apps, just show the first window info but track all windows
const firstWindow = group.windows.length > 0 ? group.windows[0] : null const firstWindow = group.windows.length > 0 ? group.windows[0] : null
const item = { const item = {
"type": "grouped", uniqueKey: "grouped_" + appId,
"appId": appId, type: "grouped",
"windowId": firstWindow ? firstWindow.windowId : -1, appId: appId,
"windowTitle": firstWindow ? firstWindow.windowTitle : "", toplevel: firstWindow ? firstWindow.toplevel : null,
"workspaceId": -1, isPinned: group.isPinned,
"isPinned": group.isPinned, isRunning: group.windows.length > 0,
"isRunning": group.windows.length > 0, windowCount: group.windows.length,
"windowCount": group.windows.length, allWindows: group.windows
"uniqueId": firstWindow ? firstWindow.uniqueId : "",
"allWindows": group.windows
} }
if (group.isPinned) { if (group.isPinned) {
@@ -128,19 +121,16 @@ Item {
} }
}) })
// Add items in order
pinnedGroups.forEach(item => items.push(item)) pinnedGroups.forEach(item => items.push(item))
// Add separator if needed
if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) { if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) {
items.push({ items.push({
"type": "separator", uniqueKey: "separator_grouped",
"appId": "__SEPARATOR__", type: "separator",
"windowId": -1, appId: "__SEPARATOR__",
"windowTitle": "", toplevel: null,
"workspaceId": -1, isPinned: false,
"isPinned": false, isRunning: false
"isRunning": false
}) })
} }
@@ -149,13 +139,12 @@ Item {
} else { } else {
pinnedApps.forEach(appId => { pinnedApps.forEach(appId => {
items.push({ items.push({
"type": "pinned", uniqueKey: "pinned_" + appId,
"appId": appId, type: "pinned",
"windowId": -1, appId: appId,
"windowTitle": "", toplevel: null,
"workspaceId": -1, isPinned: true,
"isPinned": true, isRunning: false
"isRunning": false
}) })
}) })
@@ -163,49 +152,52 @@ Item {
if (pinnedApps.length > 0 && sortedToplevels.length > 0) { if (pinnedApps.length > 0 && sortedToplevels.length > 0) {
items.push({ items.push({
"type": "separator", uniqueKey: "separator_ungrouped",
"appId": "__SEPARATOR__", type: "separator",
"windowId": -1, appId: "__SEPARATOR__",
"windowTitle": "", toplevel: null,
"workspaceId": -1, isPinned: false,
"isPinned": false, isRunning: false
"isRunning": false,
"isFocused": false
}) })
} }
sortedToplevels.forEach((toplevel, index) => { sortedToplevels.forEach((toplevel, index) => {
const title = toplevel.title || "(Unnamed)" let uniqueKey = "window_" + index
const truncatedTitle = title.length > 50 ? title.substring(0, 47) + "..." : title if (CompositorService.isHyprland && Hyprland.toplevels) {
const uniqueId = toplevel.title + "|" + (toplevel.appId || "") + "|" + index 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({ items.push({
"type": "window", uniqueKey: uniqueKey,
"appId": toplevel.appId, type: "window",
"windowId": index, appId: toplevel.appId,
"windowTitle": truncatedTitle, toplevel: toplevel,
"workspaceId": -1, isPinned: false,
"isPinned": false, isRunning: true
"isRunning": true,
"uniqueId": uniqueId
}) })
}) })
} }
items.forEach(item => append(item)) dockItems = items
}
} }
delegate: Item { delegate: Item {
id: delegateItem id: delegateItem
property alias dockButton: button property alias dockButton: button
property var itemData: modelData
clip: false clip: false
width: model.type === "separator" ? (root.isVertical ? root.iconSize : 8) : (root.isVertical ? root.iconSize : root.iconSize * 1.2) width: itemData.type === "separator" ? (root.isVertical ? root.iconSize : 8) : (root.isVertical ? root.iconSize : root.iconSize * 1.2)
height: model.type === "separator" ? (root.isVertical ? 8 : root.iconSize) : (root.isVertical ? root.iconSize * 1.2 : root.iconSize) height: itemData.type === "separator" ? (root.isVertical ? 8 : root.iconSize) : (root.isVertical ? root.iconSize * 1.2 : root.iconSize)
Rectangle { Rectangle {
visible: model.type === "separator" visible: itemData.type === "separator"
width: root.isVertical ? root.iconSize * 0.5 : 2 width: root.isVertical ? root.iconSize * 0.5 : 2
height: root.isVertical ? 2 : root.iconSize * 0.5 height: root.isVertical ? 2 : root.iconSize * 0.5
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3) color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
@@ -215,21 +207,24 @@ Item {
DockAppButton { DockAppButton {
id: button id: button
visible: model.type !== "separator" visible: itemData.type !== "separator"
anchors.centerIn: parent anchors.centerIn: parent
width: delegateItem.width width: delegateItem.width
height: delegateItem.height height: delegateItem.height
actualIconSize: root.iconSize actualIconSize: root.iconSize
appData: model appData: itemData
contextMenu: root.contextMenu contextMenu: root.contextMenu
dockApps: root dockApps: root
index: model.index index: model.index
parentDockScreen: root.dockScreen parentDockScreen: root.dockScreen
showWindowTitle: model.type === "window" || model.type === "grouped" showWindowTitle: itemData?.type === "window" || itemData?.type === "grouped"
windowTitle: model.windowTitle || "" windowTitle: {
const title = itemData?.toplevel?.title || "(Unnamed)"
return title.length > 50 ? title.substring(0, 47) + "..." : title
}
} }
} }
} }
@@ -239,18 +234,18 @@ Item {
Connections { Connections {
target: CompositorService target: CompositorService
function onToplevelsChanged() { function onToplevelsChanged() {
dockModel.updateModel() repeater.updateModel()
} }
} }
Connections { Connections {
target: SessionData target: SessionData
function onPinnedAppsChanged() { function onPinnedAppsChanged() {
dockModel.updateModel() repeater.updateModel()
} }
} }
onGroupByAppChanged: { onGroupByAppChanged: {
dockModel.updateModel() repeater.updateModel()
} }
} }

View File

@@ -1,6 +1,7 @@
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Widgets import Quickshell.Widgets
import qs.Common import qs.Common
import qs.Services import qs.Services
@@ -475,24 +476,10 @@ PanelWindow {
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
const sortedToplevels = CompositorService.sortedToplevels if (root.appData?.type === "window") {
if (root.appData && root.appData.type === "window") { root.appData?.toplevel?.close()
for (var i = 0; i < sortedToplevels.length; i++) { } else if (root.appData?.type === "grouped") {
const toplevel = sortedToplevels[i] root.appData?.allWindows?.forEach(window => window.toplevel?.close())
const checkId = toplevel.title + "|" + (toplevel.appId || "") + "|" + i
if (checkId === root.appData.uniqueId) {
toplevel.close()
break
}
}
} else if (root.appData && root.appData.type === "grouped") {
const allToplevels = ToplevelManager.toplevels.values
for (let i = 0; i < allToplevels.length; i++) {
const toplevel = allToplevels[i]
if (toplevel.appId === root.appData.appId) {
toplevel.close()
}
}
} }
root.close() root.close()
} }