mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-06 05:25:41 -05:00
252 lines
9.1 KiB
QML
252 lines
9.1 KiB
QML
import QtQuick
|
|
import QtQuick.Controls
|
|
import Quickshell
|
|
import Quickshell.Wayland
|
|
import Quickshell.Hyprland
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Widgets
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property var contextMenu: null
|
|
property bool requestDockShow: false
|
|
property int pinnedAppCount: 0
|
|
property bool groupByApp: false
|
|
property bool isVertical: false
|
|
property var dockScreen: null
|
|
property real iconSize: 40
|
|
|
|
clip: false
|
|
implicitWidth: isVertical ? appLayout.height : appLayout.width
|
|
implicitHeight: isVertical ? appLayout.width : appLayout.height
|
|
|
|
function movePinnedApp(fromIndex, toIndex) {
|
|
if (fromIndex === toIndex) {
|
|
return
|
|
}
|
|
|
|
const currentPinned = [...(SessionData.pinnedApps || [])]
|
|
if (fromIndex < 0 || fromIndex >= currentPinned.length || toIndex < 0 || toIndex >= currentPinned.length) {
|
|
return
|
|
}
|
|
|
|
const movedApp = currentPinned.splice(fromIndex, 1)[0]
|
|
currentPinned.splice(toIndex, 0, movedApp)
|
|
|
|
SessionData.setPinnedApps(currentPinned)
|
|
}
|
|
|
|
Item {
|
|
id: appLayout
|
|
width: layoutFlow.width
|
|
height: layoutFlow.height
|
|
anchors.horizontalCenter: root.isVertical ? undefined : parent.horizontalCenter
|
|
anchors.verticalCenter: root.isVertical ? parent.verticalCenter : undefined
|
|
anchors.left: root.isVertical && SettingsData.dockPosition === SettingsData.Position.Left ? parent.left : undefined
|
|
anchors.right: root.isVertical && SettingsData.dockPosition === SettingsData.Position.Right ? parent.right : undefined
|
|
anchors.top: root.isVertical ? undefined : parent.top
|
|
|
|
Flow {
|
|
id: layoutFlow
|
|
flow: root.isVertical ? Flow.TopToBottom : Flow.LeftToRight
|
|
spacing: Math.min(8, Math.max(4, root.iconSize * 0.08))
|
|
|
|
Repeater {
|
|
id: repeater
|
|
|
|
property var dockItems: []
|
|
|
|
model: ScriptModel {
|
|
values: repeater.dockItems
|
|
objectProp: "uniqueKey"
|
|
}
|
|
|
|
Component.onCompleted: updateModel()
|
|
|
|
function updateModel() {
|
|
const items = []
|
|
const pinnedApps = [...(SessionData.pinnedApps || [])]
|
|
const sortedToplevels = CompositorService.sortedToplevels
|
|
|
|
if (root.groupByApp) {
|
|
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, {
|
|
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
|
|
})
|
|
}
|
|
|
|
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 {
|
|
id: delegateItem
|
|
property alias dockButton: button
|
|
property var itemData: modelData
|
|
clip: false
|
|
|
|
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)
|
|
|
|
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
|
|
}
|
|
|
|
DockAppButton {
|
|
id: button
|
|
visible: itemData.type !== "separator"
|
|
anchors.centerIn: parent
|
|
|
|
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 {
|
|
target: CompositorService
|
|
function onToplevelsChanged() {
|
|
repeater.updateModel()
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: SessionData
|
|
function onPinnedAppsChanged() {
|
|
repeater.updateModel()
|
|
}
|
|
}
|
|
|
|
onGroupByAppChanged: {
|
|
repeater.updateModel()
|
|
}
|
|
}
|