diff --git a/Helpers/SystemInfo.qml b/Helpers/SystemInfo.qml index c4e3565..d0af2a9 100644 --- a/Helpers/SystemInfo.qml +++ b/Helpers/SystemInfo.qml @@ -1,17 +1,30 @@ pragma Singleton -import qs.Config +import QtQuick import Quickshell import Quickshell.Io -import QtQuick +import qs.Config +import qs.Paths Singleton { id: root + property string boardName + property string boardVendor + readonly property string device: { + if (!boardName) + return boardVendor; + if (!boardVendor || boardName.toLowerCase().startsWith(boardVendor.toLowerCase())) + return boardName; + return `${boardVendor} ${boardName}`; + } + property string firmware + property string hostname property bool isDefaultLogo: true + property string kernel property string osId property list osIdLike - property string osLogo + property string osLogo: Qt.resolvedUrl(`${Quickshell.shellDir}/assets/logo.svg`) property string osName property string osPrettyName readonly property string shell: Quickshell.env("SHELL").split("/").pop() @@ -19,6 +32,12 @@ Singleton { readonly property string user: Quickshell.env("USER") readonly property string wm: Quickshell.env("XDG_CURRENT_DESKTOP") || Quickshell.env("XDG_SESSION_DESKTOP") + function sanitiseDmi(s: string): string { + const t = s.trim(); + const junk = ["to be filled by o.e.m.", "system product name", "system manufacturer", "system version", "default string", "o.e.m.", "not specified", "not applicable", "unknown", "none", ""]; + return junk.includes(t.toLowerCase()) ? "" : t; + } + FileView { id: osRelease @@ -35,7 +54,10 @@ Singleton { root.osIdLike = fd("ID_LIKE").split(" "); const logo = Quickshell.iconPath(fd("LOGO"), true); - if (Config.general.logo) { + if (Config.general.logo === "zshell") { + root.osLogo = Qt.resolvedUrl(`${Quickshell.shellDir}/assets/logo.svg`); + root.isDefaultLogo = true; + } else if (Config.general.logo) { root.osLogo = Quickshell.iconPath(Config.general.logo, true) || "file://" + Paths.absolutePath(Config.general.logo); root.isDefaultLogo = false; } else if (logo) { @@ -53,6 +75,39 @@ Singleton { target: Config.general } + FileView { + path: "/proc/sys/kernel/osrelease" + + onLoaded: root.kernel = text().trim() + } + + FileView { + path: "/proc/sys/kernel/hostname" + + onLoaded: root.hostname = text().trim() + } + + FileView { + path: "/sys/class/dmi/id/sys_vendor" + printErrors: false + + onLoaded: root.boardVendor = root.sanitiseDmi(text()) + } + + FileView { + path: "/sys/class/dmi/id/product_name" + printErrors: false + + onLoaded: root.boardName = root.sanitiseDmi(text()) + } + + FileView { + path: "/sys/class/dmi/id/bios_version" + printErrors: false + + onLoaded: root.firmware = root.sanitiseDmi(text()) + } + Timer { interval: 15000 repeat: true @@ -69,12 +124,15 @@ Singleton { onLoaded: { const up = parseInt(text().split(" ")[0] ?? 0); - const hours = Math.floor(up / 3600); + const days = Math.floor(up / 86400); + const hours = Math.floor((up % 86400) / 3600); const minutes = Math.floor((up % 3600) / 60); let str = ""; + if (days > 0) + str += `${days} day${days === 1 ? "" : "s"}`; if (hours > 0) - str += `${hours} hour${hours === 1 ? "" : "s"}`; + str += `${str ? ", " : ""}${hours} hour${hours === 1 ? "" : "s"}`; if (minutes > 0 || !str) str += `${str ? ", " : ""}${minutes} minute${minutes === 1 ? "" : "s"}`; root.uptime = str; diff --git a/Modules/SettingsNew/Common/InfoRow.qml b/Modules/SettingsNew/Common/InfoRow.qml new file mode 100644 index 0000000..1a715ff --- /dev/null +++ b/Modules/SettingsNew/Common/InfoRow.qml @@ -0,0 +1,78 @@ +pragma ComponentBehavior: Bound + +import QtQuick +import QtQuick.Layouts +import qs.Components +import qs.Config + +ConnectedRect { + id: root + + property string icon + property color iconColor: DynamicColors.palette.m3onSurfaceVariant + property alias label: label.text + property Component leadingComponent: icon ? iconComp : null + property string subtext + property alias value: value.text + + Layout.fillWidth: true + implicitHeight: rowLayout.implicitHeight + rowLayout.anchors.margins * 2 + + Component { + id: iconComp + + MaterialIcon { + color: root.iconColor + font.pointSize: Appearance.font.size.small + text: root.icon + } + } + + RowLayout { + id: rowLayout + + anchors.fill: parent + anchors.leftMargin: Appearance.padding.largeIncreased + anchors.margins: Appearance.padding.larger + anchors.rightMargin: Appearance.padding.largeIncreased + spacing: Appearance.spacing.small + + Loader { + active: root.leadingComponent + sourceComponent: root.leadingComponent + visible: root.leadingComponent + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 0 + + CustomText { + id: label + + Layout.fillWidth: true + elide: Text.ElideRight + font.pointSize: Appearance.font.size.small + } + + CustomText { + Layout.fillWidth: true + color: DynamicColors.palette.m3outline + elide: Text.ElideRight + font.pointSize: Appearance.font.size.small + text: root.subtext + visible: root.subtext + } + } + + CustomText { + id: value + + Layout.maximumWidth: root.width / 2 + color: DynamicColors.palette.m3onSurfaceVariant + elide: Text.ElideRight + font.pointSize: Appearance.font.size.small + horizontalAlignment: Text.AlignRight + } + } +} diff --git a/Modules/SettingsNew/PageCompRegistry.qml b/Modules/SettingsNew/PageCompRegistry.qml index b40238f..a74e153 100644 --- a/Modules/SettingsNew/PageCompRegistry.qml +++ b/Modules/SettingsNew/PageCompRegistry.qml @@ -150,6 +150,16 @@ QtObject { } } } + }, + + // About + Component { + StackPage { + Component { + AboutPage { + } + } + } } ] readonly property Component placeholderComp: Component { diff --git a/Modules/SettingsNew/PageRegistry.qml b/Modules/SettingsNew/PageRegistry.qml index 84aef7f..3971d36 100644 --- a/Modules/SettingsNew/PageRegistry.qml +++ b/Modules/SettingsNew/PageRegistry.qml @@ -60,5 +60,11 @@ Singleton { description: qsTr("Poll intervals, audio and brightness increments"), category: "shell" }, + { + name: qsTr("About"), + icon: "info", + description: qsTr("System information"), + category: "about" + }, ] } diff --git a/Modules/SettingsNew/Pages/AboutPage.qml b/Modules/SettingsNew/Pages/AboutPage.qml new file mode 100644 index 0000000..cec1e3d --- /dev/null +++ b/Modules/SettingsNew/Pages/AboutPage.qml @@ -0,0 +1,117 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell.Io +import ZShell +import qs.Modules.SettingsNew.Common +import qs.Config +import qs.Helpers +import qs.Components + +PageBase { + id: root + + property string cliVersion + readonly property int pluginCount: 0 + property string quickshellVersion + + title: qsTr("About") + + ColumnLayout { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + spacing: Appearance.spacing.extraSmall / 2 + width: root.cappedWidth + + Process { + command: ["quickshell", "--version"] + running: true + + stdout: StdioCollector { + onStreamFinished: root.quickshellVersion = text.trim().split(" ")[1] ?? "" + } + } + + ConnectedRect { + Layout.fillWidth: true + first: true + implicitHeight: hero.implicitHeight + Appearance.padding.extraLarge * 2 + last: true + + ColumnLayout { + id: hero + + anchors.centerIn: parent + spacing: Appearance.spacing.small + width: parent.width - Appearance.padding.largeIncreased * 2 + + CustomText { + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: Appearance.spacing.small + font.pointSize: Appearance.font.size.large + text: "ZShell" + } + + CustomText { + Layout.alignment: Qt.AlignHCenter + color: DynamicColors.palette.m3onSurfaceVariant + font: Appearance.font.body.medium + text: ZUtils.version ? `v${ZUtils.version}` : "…" + } + } + } + + // System + SectionHeader { + text: qsTr("System") + } + + InfoRow { + first: true + label: qsTr("Hostname") + value: SystemInfo.hostname + } + + InfoRow { + label: qsTr("Device") + value: SystemInfo.device + } + + InfoRow { + label: qsTr("Distro") + value: SystemInfo.osPrettyName || SystemInfo.osName + } + + InfoRow { + label: qsTr("Kernel") + value: SystemInfo.kernel + } + + InfoRow { + label: qsTr("Firmware") + last: true + value: SystemInfo.firmware + } + + // Software + SectionHeader { + text: qsTr("Software") + } + + InfoRow { + first: true + label: qsTr("Shell") + value: ZUtils.version || "…" + } + + InfoRow { + label: qsTr("Quickshell") + value: root.quickshellVersion || "…" + } + + InfoRow { + label: qsTr("Qt") + last: true + value: ZUtils.qtVersion || "…" + } + } +} diff --git a/Plugins/ZShell/zutils.hpp b/Plugins/ZShell/zutils.hpp index 54cfb5f..dfbd42f 100644 --- a/Plugins/ZShell/zutils.hpp +++ b/Plugins/ZShell/zutils.hpp @@ -12,6 +12,9 @@ Q_OBJECT QML_ELEMENT QML_SINGLETON +Q_PROPERTY(QString version READ version CONSTANT) +Q_PROPERTY(QString qtVersion READ qtVersion CONSTANT) + public: // clang-format off Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);