Files
Panama/config/dot/quickshell/modules/settings/StorageBreakdownBar.qml
T

154 lines
4.7 KiB
QML

// What is on the disk, drawn as the disk.
//
// One bar whose segments add up to the whole filesystem, because the question
// people bring to this page is "what is taking my space" and the only answer
// that cannot mislead is one where the parts sum to the total. Four of the five
// segments are measured; the fifth is what is left over, and it is labelled as
// exactly that rather than being called "System" and quietly absorbing every
// measurement error in the other four.
//
// The legend carries the numbers. A bar this wide can hold a 2% segment but it
// cannot label one, and a segment nobody can name is decoration.
import QtQuick
import qs.config
import qs.services
Column {
id: root
readonly property var breakdown: Disks.breakdown ?? null
readonly property var segments: {
const source = root.breakdown?.segments ?? null;
if (!source)
return [];
return [
{
key: "home",
label: "Home",
detail: "Your files, not counting caches",
bytes: Number(source.home ?? 0),
color: Theme.accent
},
{
key: "applications",
label: "Applications",
detail: "Flatpak applications and the runtimes they share",
bytes: Number(source.applications ?? 0),
color: Theme.teal
},
{
key: "caches",
label: "Caches",
detail: "~/.cache, rebuilt as applications run",
bytes: Number(source.caches ?? 0),
color: Theme.warn
},
{
key: "system",
label: "System & everything else",
detail: "Whatever the measured segments do not account for",
bytes: Number(source.system ?? 0),
color: Theme.magenta
},
{
key: "free",
label: "Free",
detail: "Reported by the filesystem",
bytes: Number(source.free ?? 0),
color: Theme.alpha(Theme.fg, 0.1)
}
];
}
readonly property real total: root.segments.reduce(
(sum, segment) => sum + Math.max(0, segment.bytes), 0)
width: parent ? parent.width : 620
spacing: 10
Text {
width: parent.width
visible: root.total <= 0
text: Disks.measuringBreakdown
? "Measuring what is on this disk…"
: (Disks.lastError !== "" ? Disks.lastError : "Not measured yet.")
color: Theme.fgDim
font.family: Theme.fontFamily
font.pixelSize: Theme.fontSize
wrapMode: Text.WordWrap
}
Rectangle {
width: parent.width
visible: root.total > 0
height: 26
radius: 8
clip: true
color: Theme.alpha(Theme.fg, 0.06)
border.width: 1
border.color: Theme.alpha(Theme.fg, 0.1)
Row {
anchors.fill: parent
anchors.margins: 1
spacing: 0
Repeater {
model: root.segments
delegate: Rectangle {
required property var modelData
// A measured segment always gets a sliver, so "1 GB of
// caches" is visible as a fact rather than rounded away.
width: root.total > 0 && modelData.bytes > 0
? Math.max(parent.width * (modelData.bytes / root.total), 2)
: 0
height: parent.height
color: modelData.color
border.width: 0
}
}
}
}
Flow {
width: parent.width
visible: root.total > 0
spacing: 14
Repeater {
model: root.segments
delegate: Row {
required property var modelData
spacing: 6
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: 9
height: 9
radius: 3
color: modelData.key === "free"
? Theme.alpha(Theme.fg, 0.25)
: modelData.color
border.width: 0
}
Text {
anchors.verticalCenter: parent.verticalCenter
text: modelData.label + " " + Disks.formatBytes(modelData.bytes)
color: Theme.fgDim
font.family: Theme.fontFamily
font.features: Theme.tabularFigures
font.pixelSize: Theme.fontSizeSmall
}
}
}
}
}