// 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 } } } } }