diff options
| -rw-r--r-- | desktop/README.md | 7 | ||||
| -rw-r--r-- | desktop/Settings.qml | 259 |
2 files changed, 230 insertions, 36 deletions
diff --git a/desktop/README.md b/desktop/README.md index 5d327a5..aa105a9 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -60,7 +60,12 @@ The sidebar's edge drags to resize it, between 160 and 420px; the width is saved in the preferences file on release. `open` takes a page name; an unknown or empty one opens the landing page, -a short About. A page's back arrow and Escape return there; Escape on the +a short About under the project's name, Keystone, and a system card: the +Slackware facts of conky-theme-udt's card (version, packages, kernel, +install age, and the slackpkg ChangeLog age, coloured green, yellow, red at +a day and a week), plus the machine, then base library and development tool +versions read from the installed package names, in two columns, sampled +once per open. The page scrolls when it outgrows the window. A page's back arrow and Escape return there; Escape on the landing page closes the window. Ctrl+Q closes it from anywhere. The search above the list matches each page's label, its module's 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 + } + } + } + } + } + } + } + } } } } |
