pragma Singleton import QtQuick import Quickshell import ZShell.Config Singleton { id: root readonly property var columnDefs: [ { id: "command", title: qsTr("Command"), width: 200, field: 3 }, { id: "pid", title: qsTr("PID"), width: 60, field: 1 }, { id: "cpuUsage", title: qsTr("CPU %"), width: 70, field: 7 }, { id: "memoryUsage", title: qsTr("Memory"), width: 90, field: 8 }, { id: "elapsed", title: qsTr("Elapsed"), width: 80, field: 9 }, { id: "ppid", title: qsTr("PPID"), width: 60, field: 2 }, { id: "user", title: qsTr("User"), width: 100, field: 11 }, { id: "uid", title: qsTr("UID"), width: 60, field: 10 }, { id: "state", title: qsTr("State"), width: 60, field: 5 }, { id: "sleeping", title: qsTr("Sleeping"), width: 80, field: 6 }, { id: "commandLine", title: qsTr("Command line"), width: 240, field: 4 }, { id: "ioReadBytes", title: qsTr("IO read"), width: 90, field: 12 }, { id: "ioWriteBytes", title: qsTr("IO write"), width: 90, field: 13 } ] readonly property string pinnedColumnId: "command" readonly property var hiddenColumns: { const visible = Config.dashboard.processColumns.values.map(e => e.id); return root.columnDefs.filter(d => !visible.includes(d.id)); } function pinnedIndex(): int { const values = Config.dashboard.processColumns.values; for (let i = 0; i < values.length; i++) if (values[i].id === root.pinnedColumnId) return i; return -1; } function ensurePinnedFirst() { const i = root.pinnedIndex(); if (i > 0) Qt.callLater(() => { const idx = root.pinnedIndex(); if (idx > 0) Config.dashboard.processColumns.move(idx, 0); }); } Component.onCompleted: Qt.callLater(ensurePinnedFirst) function defFor(id: string): var { for (const def of root.columnDefs) if (def.id === id) return def; return null; } function addColumn(id: string) { const def = root.defFor(id); Config.dashboard.processColumns.insert({ id: id, width: def ? def.width : 100 }); } function removeColumn(index: int) { const entry = Config.dashboard.processColumns.at(index); if (entry && entry.id === root.pinnedColumnId) return; Config.dashboard.processColumns.remove(index); } function moveColumns(from: int, to: int) { const list = Config.dashboard.processColumns; const entry = list.at(from); if (!entry) return; if (entry.id === root.pinnedColumnId) return; if (to === 0 && list.at(0)?.id === root.pinnedColumnId) return; list.move(from, to); const i = root.pinnedIndex(); if (i > 0) list.move(i, 0); } function resizeColumn(index: int, width: real) { const entry = Config.dashboard.processColumns.at(index); if (entry) entry.width = Math.max(50, width); } function dampOvershoot(overshoot: real, maxOvershoot: real): real { return overshoot / (1 + overshoot / maxOvershoot); } function formatCell(id: string, value: var): string { switch (id) { case "cpuUsage": return value.toFixed(1) + "%"; case "memoryUsage": return root.formatBytes(value * 1024); case "ioReadBytes": case "ioWriteBytes": return root.formatBytes(value); case "elapsed": return root.formatElapsed(value); case "sleeping": return value ? qsTr("Yes") : qsTr("No"); default: return String(value); } } function formatBytes(bytes: var): string { if (bytes === undefined || bytes === null || bytes <= 0) return "0 B"; const units = ["B", "KiB", "MiB", "GiB", "TiB", "PiB"]; let v = Number(bytes); let i = 0; while (v >= 1024 && i < units.length - 1) { v /= 1024; ++i; } return (i === 0 ? v.toFixed(0) : v.toFixed(1)) + " " + units[i]; } function formatElapsed(secs: var): string { const s = Math.max(0, Math.floor(Number(secs))); const h = Math.floor(s / 3600); const m = Math.floor((s % 3600) / 60); const ss = s % 60; if (h > 0) return h + "h " + m + "m"; if (m > 0) return m + "m " + ss + "s"; return ss + "s"; } }