154 lines
4.7 KiB
QML
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|