mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-27 23:12:49 -05:00
dock: track hyprland addresses, fix closing, use ScriptModel
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,158 +55,149 @@ Item {
|
|||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: repeater
|
id: repeater
|
||||||
model: ListModel {
|
|
||||||
id: dockModel
|
|
||||||
|
|
||||||
Component.onCompleted: updateModel()
|
property var dockItems: []
|
||||||
|
|
||||||
function updateModel() {
|
model: ScriptModel {
|
||||||
clear()
|
values: repeater.dockItems
|
||||||
|
objectProp: "uniqueKey"
|
||||||
|
}
|
||||||
|
|
||||||
const items = []
|
Component.onCompleted: updateModel()
|
||||||
const pinnedApps = [...(SessionData.pinnedApps || [])]
|
|
||||||
const sortedToplevels = CompositorService.sortedToplevels
|
|
||||||
|
|
||||||
if (root.groupByApp) {
|
function updateModel() {
|
||||||
// Group windows by appId
|
const items = []
|
||||||
const appGroups = new Map()
|
const pinnedApps = [...(SessionData.pinnedApps || [])]
|
||||||
|
const sortedToplevels = CompositorService.sortedToplevels
|
||||||
|
|
||||||
// Add pinned apps first (even if they have no windows)
|
if (root.groupByApp) {
|
||||||
pinnedApps.forEach(appId => {
|
const appGroups = new Map()
|
||||||
|
|
||||||
|
pinnedApps.forEach(appId => {
|
||||||
|
appGroups.set(appId, {
|
||||||
|
appId: appId,
|
||||||
|
isPinned: true,
|
||||||
|
windows: []
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
sortedToplevels.forEach((toplevel, index) => {
|
||||||
|
const appId = toplevel.appId || "unknown"
|
||||||
|
if (!appGroups.has(appId)) {
|
||||||
appGroups.set(appId, {
|
appGroups.set(appId, {
|
||||||
appId: appId,
|
appId: appId,
|
||||||
isPinned: true,
|
isPinned: false,
|
||||||
windows: []
|
windows: []
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
// Group all running windows by appId
|
|
||||||
sortedToplevels.forEach((toplevel, index) => {
|
|
||||||
const appId = toplevel.appId || "unknown"
|
|
||||||
if (!appGroups.has(appId)) {
|
|
||||||
appGroups.set(appId, {
|
|
||||||
appId: appId,
|
|
||||||
isPinned: false,
|
|
||||||
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({
|
|
||||||
windowId: index,
|
|
||||||
windowTitle: truncatedTitle,
|
|
||||||
uniqueId: uniqueId
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Sort groups: pinned first, then unpinned
|
|
||||||
const pinnedGroups = []
|
|
||||||
const unpinnedGroups = []
|
|
||||||
|
|
||||||
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 item = {
|
|
||||||
"type": "grouped",
|
|
||||||
"appId": appId,
|
|
||||||
"windowId": firstWindow ? firstWindow.windowId : -1,
|
|
||||||
"windowTitle": firstWindow ? firstWindow.windowTitle : "",
|
|
||||||
"workspaceId": -1,
|
|
||||||
"isPinned": group.isPinned,
|
|
||||||
"isRunning": group.windows.length > 0,
|
|
||||||
"windowCount": group.windows.length,
|
|
||||||
"uniqueId": firstWindow ? firstWindow.uniqueId : "",
|
|
||||||
"allWindows": group.windows
|
|
||||||
}
|
|
||||||
|
|
||||||
if (group.isPinned) {
|
|
||||||
pinnedGroups.push(item)
|
|
||||||
} else {
|
|
||||||
unpinnedGroups.push(item)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add items in order
|
|
||||||
pinnedGroups.forEach(item => items.push(item))
|
|
||||||
|
|
||||||
// Add separator if needed
|
|
||||||
if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) {
|
|
||||||
items.push({
|
|
||||||
"type": "separator",
|
|
||||||
"appId": "__SEPARATOR__",
|
|
||||||
"windowId": -1,
|
|
||||||
"windowTitle": "",
|
|
||||||
"workspaceId": -1,
|
|
||||||
"isPinned": false,
|
|
||||||
"isRunning": false
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unpinnedGroups.forEach(item => items.push(item))
|
appGroups.get(appId).windows.push({
|
||||||
root.pinnedAppCount = pinnedGroups.length
|
toplevel: toplevel,
|
||||||
} else {
|
index: index
|
||||||
pinnedApps.forEach(appId => {
|
|
||||||
items.push({
|
|
||||||
"type": "pinned",
|
|
||||||
"appId": appId,
|
|
||||||
"windowId": -1,
|
|
||||||
"windowTitle": "",
|
|
||||||
"workspaceId": -1,
|
|
||||||
"isPinned": true,
|
|
||||||
"isRunning": false
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
root.pinnedAppCount = pinnedApps.length
|
const pinnedGroups = []
|
||||||
|
const unpinnedGroups = []
|
||||||
|
|
||||||
if (pinnedApps.length > 0 && sortedToplevels.length > 0) {
|
Array.from(appGroups.entries()).forEach(([appId, group]) => {
|
||||||
items.push({
|
const firstWindow = group.windows.length > 0 ? group.windows[0] : null
|
||||||
"type": "separator",
|
|
||||||
"appId": "__SEPARATOR__",
|
const item = {
|
||||||
"windowId": -1,
|
uniqueKey: "grouped_" + appId,
|
||||||
"windowTitle": "",
|
type: "grouped",
|
||||||
"workspaceId": -1,
|
appId: appId,
|
||||||
"isPinned": false,
|
toplevel: firstWindow ? firstWindow.toplevel : null,
|
||||||
"isRunning": false,
|
isPinned: group.isPinned,
|
||||||
"isFocused": false
|
isRunning: group.windows.length > 0,
|
||||||
})
|
windowCount: group.windows.length,
|
||||||
|
allWindows: group.windows
|
||||||
}
|
}
|
||||||
|
|
||||||
sortedToplevels.forEach((toplevel, index) => {
|
if (group.isPinned) {
|
||||||
const title = toplevel.title || "(Unnamed)"
|
pinnedGroups.push(item)
|
||||||
const truncatedTitle = title.length > 50 ? title.substring(0, 47) + "..." : title
|
} else {
|
||||||
const uniqueId = toplevel.title + "|" + (toplevel.appId || "") + "|" + index
|
unpinnedGroups.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
items.push({
|
pinnedGroups.forEach(item => items.push(item))
|
||||||
"type": "window",
|
|
||||||
"appId": toplevel.appId,
|
if (pinnedGroups.length > 0 && unpinnedGroups.length > 0) {
|
||||||
"windowId": index,
|
items.push({
|
||||||
"windowTitle": truncatedTitle,
|
uniqueKey: "separator_grouped",
|
||||||
"workspaceId": -1,
|
type: "separator",
|
||||||
"isPinned": false,
|
appId: "__SEPARATOR__",
|
||||||
"isRunning": true,
|
toplevel: null,
|
||||||
"uniqueId": uniqueId
|
isPinned: false,
|
||||||
})
|
isRunning: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
items.forEach(item => append(item))
|
unpinnedGroups.forEach(item => items.push(item))
|
||||||
|
root.pinnedAppCount = pinnedGroups.length
|
||||||
|
} else {
|
||||||
|
pinnedApps.forEach(appId => {
|
||||||
|
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: toplevel.appId,
|
||||||
|
toplevel: toplevel,
|
||||||
|
isPinned: false,
|
||||||
|
isRunning: true
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user