aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/Settings.qml
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/Settings.qml')
-rw-r--r--desktop/Settings.qml259
1 files changed, 224 insertions, 35 deletions
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
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
}
}