diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-15 12:33:18 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-15 12:33:18 +0200 |
| commit | d41ead57885d5008922fdada2d702dc6488e09d6 (patch) | |
| tree | 4bdbdf54d3b752fd29c9223555500216af10a8e9 /desktop/modules | |
| parent | 36b272482a57f3edd14782171d5979756d8fde9c (diff) | |
| download | quickshell-d41ead57885d5008922fdada2d702dc6488e09d6.tar.gz quickshell-d41ead57885d5008922fdada2d702dc6488e09d6.zip | |
feat(updates): a page that reads at a glance
The first cut was small type in one long list: 471 Slackware upgrades
pushed everything below them out of sight, and the repo tag, the
versions and the name ran together across the row. Type is 2px larger
and rows taller. A section over ten rows starts folded behind a
clickable heading with its count. The version being moved to is bright,
the one being left dim, both in Inconsolata so digits line up. Every
other row is shaded to carry the eye across, and the repo is a pill in
one colour per repo, never red, green, yellow or the accent, which mean
something elsewhere.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'desktop/modules')
| -rw-r--r-- | desktop/modules/updates/UpdatesPage.qml | 136 |
1 files changed, 106 insertions, 30 deletions
diff --git a/desktop/modules/updates/UpdatesPage.qml b/desktop/modules/updates/UpdatesPage.qml index 967b0e0..2cce13c 100644 --- a/desktop/modules/updates/UpdatesPage.qml +++ b/desktop/modules/updates/UpdatesPage.qml @@ -23,23 +23,30 @@ Column { spacing: 16 + // Rows carry `from` and `to` apart so the target can stand out; a row + // with a single value (new, removed) leaves `from` empty. function tracked(list) { - return list.map(r => ({ name: r.name, repo: r.repo, ver: r.from + " → " + r.to })); + return list.map(r => ({ name: r.name, repo: r.repo, from: r.from, to: r.to })); } readonly property var sections: !d ? [] : [ { title: "Slackware upgrades", err: d.errors.mirror || "", - rows: d.slackware.upgrade.map(r => ({ name: r.name, repo: "", ver: r.from + " → " + r.to })) }, + rows: d.slackware.upgrade.map(r => ({ name: r.name, repo: "", from: r.from, to: r.to })) }, { title: "New in Slackware", err: "", - rows: d.slackware.new.map(r => ({ name: r.name, repo: "", ver: r.version })) }, + rows: d.slackware.new.map(r => ({ name: r.name, repo: "", from: "", to: r.version })) }, { title: "Removed from Slackware", err: "", - rows: d.slackware.removed.map(r => ({ name: r.name, repo: "", ver: "removed " + r.date })) }, + rows: d.slackware.removed.map(r => ({ name: r.name, repo: "", from: "", to: "removed " + r.date })) }, { title: "Upgrade here", err: d.errors.repo || "", rows: tracked(d.upgrade) }, { title: "Awaiting build", err: "", rows: tracked(d.build) }, { title: "Upstream newer", err: d.errors.nvchecker || "", rows: tracked(d.upstream) }, { title: "App updates", err: "", rows: tracked(d.apps) }, ] + function repoColor(r) { + return { "personal": Theme.blue, "my-sbo": Theme.mauve, "pentesting": Theme.peach, + "SBo": Theme.teal }[r] || Theme.overlay; + } + function ago(t) { const m = Math.round((Date.now() / 1000 - t) / 60); return m < 1 ? "just now" : m < 60 ? m + " min ago" : Math.round(m / 60) + " h ago"; @@ -50,61 +57,130 @@ Column { && page.d.upstream.length === 0 && page.d.apps.length === 0 text: "Up to date" color: Theme.subtext - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + font { family: Theme.fontFamily; pixelSize: Theme.fontSize } } Repeater { model: page.sections Column { + id: section required property var modelData width: page.width - spacing: 4 + spacing: 2 visible: modelData.rows.length > 0 || modelData.err !== "" - Text { - text: modelData.title + (modelData.rows.length ? " " + modelData.rows.length : "") - color: Theme.text - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1; bold: true } + // A long section starts folded, so 471 Slackware upgrades do not + // bury everything below them. A refresh rebuilds the delegates + // and folds it again. + property bool open: modelData.rows.length <= 10 + + Item { + width: page.width + height: heading.implicitHeight + 6 + + Row { + id: heading + anchors.verticalCenter: parent.verticalCenter + spacing: 8 + + Text { + anchors.verticalCenter: parent.verticalCenter + width: 14 + text: section.open ? "\uf078" : "\uf054" + color: Theme.subtext + font { family: Theme.iconFamily; pixelSize: Theme.fontSize - 3 } + } + Text { + text: section.modelData.title + color: Theme.text + font { family: Theme.fontFamily; pixelSize: Theme.fontSize + 1; bold: true } + } + Text { + anchors.verticalCenter: parent.verticalCenter + visible: section.modelData.rows.length > 0 + text: section.modelData.rows.length + color: Theme.subtext + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1 } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: section.open = !section.open + } } Text { - visible: modelData.err !== "" - text: modelData.err + visible: section.modelData.err !== "" + text: section.modelData.err color: Theme.subtext - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3; italic: true } + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1; italic: true } } Repeater { - model: modelData.rows + model: section.open ? section.modelData.rows : [] - Item { + Rectangle { required property var modelData + required property int index width: page.width - height: 22 + height: 26 + radius: 4 + // Every other row shaded, to carry the eye from the name + // across to its version. + color: index % 2 ? Qt.alpha(Theme.surface, 0.45) : "transparent" Text { - anchors { left: parent.left; right: repo.left; rightMargin: 8; verticalCenter: parent.verticalCenter } + anchors { left: parent.left; leftMargin: 8; right: repo.left; rightMargin: 8; verticalCenter: parent.verticalCenter } elide: Text.ElideRight textFormat: Text.PlainText text: modelData.name color: Theme.text - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1 } } - Text { + // The repo as a pill, one colour per repo. Never red, green, + // yellow or the accent: those carry meaning elsewhere. + Rectangle { id: repo - anchors { right: ver.left; rightMargin: 12; verticalCenter: parent.verticalCenter } - text: modelData.repo - color: Theme.subtext - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + readonly property color tint: page.repoColor(modelData.repo) + anchors { right: ver.left; rightMargin: 16; verticalCenter: parent.verticalCenter } + visible: modelData.repo !== "" + width: visible ? repoText.implicitWidth + 14 : 0 + height: repoText.implicitHeight + 4 + radius: height / 2 + color: Qt.alpha(tint, 0.18) + border { width: 1; color: Qt.alpha(tint, 0.5) } + + Text { + id: repoText + anchors.centerIn: parent + text: modelData.repo + color: repo.tint + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + } } - Text { + // Monospace so digits line up down the column; the + // version being moved to is the bright one. + Row { id: ver - anchors { right: parent.right; verticalCenter: parent.verticalCenter } - textFormat: Text.PlainText - text: modelData.ver - color: Theme.subtext - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + anchors { right: parent.right; rightMargin: 8; verticalCenter: parent.verticalCenter } + spacing: 6 + + Text { + visible: modelData.from !== "" + textFormat: Text.PlainText + text: modelData.from + " →" + color: Theme.subtext + font { family: Theme.iconFamily; pixelSize: Theme.fontSize - 1 } + } + Text { + textFormat: Text.PlainText + text: modelData.to + color: Theme.text + font { family: Theme.iconFamily; pixelSize: Theme.fontSize - 1 } + } } } } @@ -119,7 +195,7 @@ Column { text: page.up.checking ? "checking…" : page.d && page.d.checked ? "checked " + page.ago(page.d.checked) : "" color: Theme.subtext - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 } + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1 } } Button { |
