From 0c08bffaca0b4b3b94d4c5484a86e8856f5e000a Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Sun, 18 Oct 2026 10:35:35 +0200 Subject: settings: system card on the landing page The landing page was a paragraph of About text. It now carries the project's name, Keystone, a short About, and a system card: the Slackware facts of conky-theme-udt's card (version, package count, kernel, install age, and the slackpkg ChangeLog age, green under a day, yellow under a week, red past it), the machine, and base library and development tool versions, in two columns. The page scrolls once it outgrows the window. Everything comes from one sh process per open of the window, so there is no cache file and no dependency on conky's sampler. Library and tool versions are read from the installed package names in /var/log/packages: the version must start with a digit, so gcc does not also match gcc-g++, and stops at an underscore, which drops qt6's snapshot suffix. A package that is not installed shows a dash. The shortcut walkthrough left the About text: the drawer, island and assistant are described in their own READMEs. Co-Authored-By: Claude Opus 5.5 --- desktop/Settings.qml | 259 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 224 insertions(+), 35 deletions(-) (limited to 'desktop/Settings.qml') diff --git a/desktop/Settings.qml b/desktop/Settings.qml index 4905bcd..22af3da 100644 --- a/desktop/Settings.qml +++ b/desktop/Settings.qml @@ -10,7 +10,9 @@ // GNU General Public License for more details. import Quickshell +import Quickshell.Io import QtQuick +import QtQuick.Controls // The settings window: a normal toplevel, not a layer surface, so Hyprland // tiles or floats it like any app. The drawer is the quick area; things set @@ -283,47 +285,234 @@ Scope { // The landing page, where back returns to: what this shell is, // centred in the space beside the sidebar. Item { + id: landing anchors { top: parent.top; right: parent.right; bottom: parent.bottom; left: rule.right; margins: 16 } visible: root.current === null - Column { - anchors.centerIn: parent - width: Math.min(parent.width, 560) - spacing: 12 - - Text { - width: parent.width - horizontalAlignment: Text.AlignHCenter - text: "About" - font { family: Theme.fontFamily; pixelSize: Theme.fontSize + 2; bold: true } - color: Theme.text + // Versions read from the installed package names, as + // [label, package]. A package not installed shows a dash. + readonly property var base: [ + ["glibc", "glibc"], ["Qt", "qt6"], ["Mesa", "mesa"], ["Wayland", "wayland"], + ["PipeWire", "pipewire"], ["D-Bus", "dbus"], ["elogind", "elogind"], + ["OpenSSL", "openssl"], ["FFmpeg", "ffmpeg"], + ] + readonly property var dev: [ + ["GCC", "gcc"], ["LLVM", "llvm"], ["binutils", "binutils"], ["CMake", "cmake"], + ["Meson", "meson"], ["Git", "git"], ["Python", "python3"], ["Perl", "perl"], + ["Rust", "rust"], ["Node.js", "nodejs"], + ] + + // System facts, sampled once per open of the window: the + // Slackware card from conky-theme-udt plus the machine. + // Ages are epochs, turned into text here. + property var sys: ({}) + Process { + running: true + command: ["sh", "-c", + "printf 'version\\t%s\\n' \"$(cat /etc/slackware-version)\"\n" + // The trailing slash: /var/log/packages is a symlink. + + "printf 'packages\\t%s\\n' \"$(ls -1 /var/log/packages/ | wc -l)\"\n" + + "printf 'changelog\\t%s\\n' \"$(stat -c %Y /var/lib/slackpkg/ChangeLog.txt)\"\n" + // Filesystem birth time, 0 where the filesystem keeps none. + + "printf 'birth\\t%s\\n' \"$(stat -c %W /)\"\n" + + "printf 'kernel\\t%s\\n' \"$(uname -r)\"\n" + + "printf 'host\\t%s\\n' \"$(uname -n)\"\n" + + "printf 'uptime\\t%s\\n' \"$(cut -d' ' -f1 /proc/uptime)\"\n" + + "printf 'cpu\\t%s\\n' \"$(sed -n 's/^model name[^:]*: //p;T;q' /proc/cpuinfo)\"\n" + + "printf 'gpu\\t%s\\n' \"$(lspci | sed -n 's/.*\\(VGA\\|3D\\)[^:]*: //p' | head -1)\"\n" + + "printf 'memory\\t%s\\n' \"$(sed -n 's/^MemTotal: *\\([0-9]*\\).*/\\1/p' /proc/meminfo)\"\n" + + "printf 'hyprland\\t%s\\n' \"$(hyprctl version -j | jq -r .version)\"\n" + + "printf 'quickshell\\t%s\\n' \"$(qs --version | cut -d' ' -f2)\"\n" + // name-version-arch-build. The version must start + // with a digit, so gcc does not also match gcc-g++, + // and stops at an underscore, so qt6's snapshot + // suffix is dropped. + + "ls -1 /var/log/packages/ | sed -n 's/^\\(" + + landing.base.concat(landing.dev).map(p => p[1]).join("\\|") + + "\\)-\\([0-9][^-_]*\\)[^-]*-[^-]*-[^-]*$/pkg:\\1\\t\\2/p'\n"] + stdout: StdioCollector { + onStreamFinished: { + const out = {}; + for (const line of text.split("\n")) { + const tab = line.indexOf("\t"); + if (tab > 0 && line.slice(tab + 1) !== "") out[line.slice(0, tab)] = line.slice(tab + 1); + } + landing.sys = out; + } } + } - Text { - width: parent.width - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.Wrap - textFormat: Text.PlainText - lineHeight: 1.25 - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } - color: Theme.text - text: "A desktop shell for Hyprland, built on Quickshell.\n\n" - + "SUPER alone opens the drawer, the quick area: notifications, sound, " - + "network, bluetooth, mail, VMs, performance, the clipboard and the " - + "rest of what changes during the day. The island at the top of the " - + "main screen carries the clock, volume, tray, keyboard layout and " - + "calendar, and SUPER+Home opens the assistant.\n\n" - + "This window holds what is set once and left alone. Pick a page on " - + "the left, or search for a setting by name." - } + // Seconds as hours below two days, then days, as the conky card. + function age(s) { + if (!(s >= 0)) return "-"; + return s < 172800 ? Math.floor(s / 3600) + "h" : Math.floor(s / 86400) + "d"; + } + function pkgs(list) { return list.map(p => [p[0], sys["pkg:" + p[1]] ?? "-"]); } + readonly property real now: Date.now() / 1000 + readonly property real changelogAge: sys.changelog > 0 ? now - sys.changelog : -1 + readonly property var sections: [ + { title: "System", rows: [ + ["Version", (sys.version ?? "-").replace(/^Slackware\s+/, "")], + ["Packages", sys.packages ?? "-"], + ["Kernel", sys.kernel ?? "-"], + ["Installed", sys.birth > 0 ? age(now - sys.birth) + " ago" : "-"], + ["Host", sys.host ?? "-"], + ["Uptime", sys.uptime ? age(+sys.uptime) : "-"], + ["CPU", sys.cpu ?? "-"], + ["GPU", (sys.gpu ?? "-").replace(" Corporation", "")], + ["Memory", sys.memory ? (sys.memory / 1048576).toFixed(1) + " GiB" : "-"], + ["Hyprland", sys.hyprland ?? "-"], + ["Quickshell", sys.quickshell ?? "-"], + ] }, + { title: "Base libraries", rows: pkgs(base) }, + { title: "Development", rows: pkgs(dev) }, + ] + + Flickable { + id: landingFlick + anchors.fill: parent + contentHeight: about.height + clip: true + boundsBehavior: Flickable.StopAtBounds + ScrollBar.vertical: ScrollBar {} - Text { - width: parent.width - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.Wrap - text: "GPLv2. Colours follow the wallpaper through unified-desktop-theme." - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } - color: Theme.subtext + Column { + id: about + // Centred while it fits, top-aligned once it scrolls. + x: (landingFlick.width - width) / 2 + y: Math.max(0, (landingFlick.height - height) / 2) + width: Math.min(landingFlick.width - 16, 640) + spacing: 12 + + Text { + width: parent.width + horizontalAlignment: Text.AlignHCenter + text: "Keystone" + font { family: Theme.fontFamily; pixelSize: Theme.fontSize + 10; bold: true; letterSpacing: 2 } + color: Theme.accent + } + + Text { + width: parent.width + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + textFormat: Text.PlainText + lineHeight: 1.25 + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } + color: Theme.text + text: "A desktop shell for Hyprland, built on Quickshell.\n\n" + + "This window holds what is set once and left alone. Pick a page on " + + "the left, or search for a setting by name." + } + + Text { + width: parent.width + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + text: "GPLv2. Colours follow the wallpaper through unified-desktop-theme." + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + color: Theme.subtext + } + + Rectangle { + width: parent.width + height: facts.implicitHeight + 28 + radius: 10 + color: Theme.surface + + Column { + id: facts + anchors { fill: parent; margins: 14 } + spacing: 4 + + // The headline, as on the conky card: how long + // since slackpkg's ChangeLog changed, green under + // a day, yellow under a week, red past it. + Item { + width: parent.width + height: 36 + Text { + id: slackIcon + anchors.verticalCenter: parent.verticalCenter + // nf-linux-slackware, present in Inconsolata Nerd Font. + text: "" + font { family: Theme.iconFamily; pixelSize: Theme.fontSize + 8 } + color: Theme.accent + } + Column { + anchors { left: slackIcon.right; leftMargin: 10; verticalCenter: parent.verticalCenter } + Text { + text: "SLACKWARE" + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3; bold: true; letterSpacing: 1 } + color: Theme.text + } + Text { + text: "last slackpkg update" + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 5 } + color: Theme.subtext + } + } + Text { + anchors { right: parent.right; verticalCenter: parent.verticalCenter } + text: landing.age(landing.changelogAge) + font { family: Theme.fontFamily; pixelSize: Theme.fontSize + 8; bold: true } + color: landing.changelogAge < 0 ? Theme.subtext + : landing.changelogAge < 86400 ? Theme.green + : landing.changelogAge < 604800 ? Theme.yellow : Theme.red + } + } + + Repeater { + model: landing.sections + Column { + id: section + required property var modelData + width: facts.width + spacing: 4 + + Text { + topPadding: 10 + bottomPadding: 2 + text: section.modelData.title.toUpperCase() + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 5; bold: true; letterSpacing: 1 } + color: Theme.overlay + } + + // Two columns, filled row by row. + Grid { + id: cells + columns: 2 + columnSpacing: 24 + rowSpacing: 4 + readonly property real cell: (facts.width - columnSpacing) / 2 + + Repeater { + model: section.modelData.rows + Item { + required property var modelData + width: cells.cell + height: 20 + Text { + id: key + text: parent.modelData[0] + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + color: Theme.subtext + } + Text { + anchors { right: parent.right; left: key.right; leftMargin: 12 } + horizontalAlignment: Text.AlignRight + elide: Text.ElideLeft + text: parent.modelData[1] + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } + color: Theme.text + } + } + } + } + } + } + } + } } } } -- cgit v1.2.3