From b53cc0466d10600686f3ad393d4f3df0f80a61d5 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 13 Aug 2025 08:04:26 -0400 Subject: [PATCH 1/8] Bar: SysMon --- Bin/system-stats.sh | 15 +++---- Modules/Bar/Bar.qml | 13 +++--- Modules/Bar/SystemMonitor.qml | 84 +++++++++++++++++++++++++++++++++++ Services/SystemStats.qml | 2 + 4 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 Modules/Bar/SystemMonitor.qml diff --git a/Bin/system-stats.sh b/Bin/system-stats.sh index 7bd1170..da966df 100755 --- a/Bin/system-stats.sh +++ b/Bin/system-stats.sh @@ -28,7 +28,7 @@ TEMP_SENSOR_TYPE="" # --- Data Collection Functions --- # -# Gets memory usage in GB and as a percentage. +# Gets memory usage in GB, MB, and as a percentage. # get_memory_info() { awk ' @@ -39,11 +39,10 @@ get_memory_info() { usage_kb = total - available usage_gb = usage_kb / 1000000 usage_percent = (usage_kb / total) * 100 - # MODIFIED: Round the memory percentage to the nearest integer. printf "%.1f %.0f\n", usage_gb, usage_percent } else { # Fallback if /proc/meminfo is unreadable or empty. - print "0.0 0.0" + print "0.0 0 0" } } ' /proc/meminfo @@ -95,10 +94,8 @@ get_cpu_usage() { if (total > 0) { # Formula: 100 * (Total - Idle) / Total usage = 100 * (total - idle) / total - # MODIFIED: Changed format from "%.2f" back to "%.1f" for one decimal place. printf "%.1f\n", usage } else { - # MODIFIED: Changed output back to "0.0" to match the precision. print "0.0" } }' @@ -171,7 +168,7 @@ get_cpu_temp() { # This loop runs indefinitely, gathering and printing stats. while true; do # Call the functions to gather all the data. - # 'read' is used to capture the two output values from get_memory_info. + # get_memory_info read -r mem_gb mem_per <<< "$(get_memory_info)" # Command substitution captures the single output from the other functions. @@ -179,11 +176,11 @@ while true; do cpu_usage=$(get_cpu_usage) cpu_temp=$(get_cpu_temp) - # Use printf to format the final JSON output string, matching the Zig program. - printf '{"mem":"%s", "cpu": "%s", "cputemp": "%s", "memper": "%s", "diskper": "%s"}\n' \ - "$mem_gb" \ + # Use printf to format the final JSON output string, adding the mem_mb key. + printf '{"cpu": "%s", "cputemp": "%s", "memgb":"%s", "memper": "%s", "diskper": "%s"}\n' \ "$cpu_usage" \ "$cpu_temp" \ + "$mem_gb" \ "$mem_per" \ "$disk_per" diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index a494664..3d4c79f 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -50,11 +50,14 @@ Variants { anchors.verticalCenter: parent.verticalCenter spacing: Style.marginSmall * scaling - NText { - text: screen.name - anchors.verticalCenter: parent.verticalCenter - font.weight: Style.fontWeightBold - } + // Debug show monitor name + // NText { + // text: screen.name + // anchors.verticalCenter: parent.verticalCenter + // font.weight: Style.fontWeightBold + // } + + SystemMonitor {} } // Center diff --git a/Modules/Bar/SystemMonitor.qml b/Modules/Bar/SystemMonitor.qml new file mode 100644 index 0000000..ccac84b --- /dev/null +++ b/Modules/Bar/SystemMonitor.qml @@ -0,0 +1,84 @@ +import QtQuick +import Quickshell +import qs.Services +import qs.Widgets + +Row { + id: layout + anchors.verticalCenter: parent.verticalCenter + spacing: Style.marginSmall * scaling + visible: Settings.data.bar.showSystemInfo + + // Ensure our width is an integer + width: Math.floor(cpuUsageLayout.width + cpuTempLayout.width + memoryUsageLayout.width + (2 * 10)) + + Row { + id: cpuUsageLayout + spacing: Style.marginTiny * scaling + + NText { + id: cpuUsageIcon + text: "speed" + font.family: "Material Symbols Outlined" + font.pointSize: Style.fontSizeLarge * scaling + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + color: Colors.accentPrimary + } + + NText { + id: cpuUsageText + text: `${SystemStats.cpuUsage}%` + font.pointSize: Style.fontSizeSmall * scaling + font.weight: Style.fontWeightBold + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + } + } + + // CPU Temperature Component + Row { + id: cpuTempLayout + spacing: Style.marginTiny * scaling + + NText { + text: "thermometer" + font.family: "Material Symbols Outlined" + font.pointSize: Style.fontSizeLarge * scaling + color: Colors.accentPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + } + + NText { + text: `${SystemStats.cpuTemp}°C` + font.pointSize: Style.fontSizeSmall * scaling + font.weight: Style.fontWeightBold + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + } + } + + // Memory Usage Component + Row { + id: memoryUsageLayout + spacing: Style.marginTiny * scaling + + NText { + text: "memory" + font.family: "Material Symbols Outlined" + font.pointSize: Style.fontSizeLarge * scaling + color: Colors.accentPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + } + + NText { + text: `${SystemStats.memoryUsageGb}G` + font.pointSize: Style.fontSizeSmall * scaling + font.weight: Style.fontWeightBold + anchors.verticalCenter: parent.verticalCenter + verticalAlignment: Text.AlignVCenter + } + } +} diff --git a/Services/SystemStats.qml b/Services/SystemStats.qml index add1e38..4c9d3b0 100644 --- a/Services/SystemStats.qml +++ b/Services/SystemStats.qml @@ -11,6 +11,7 @@ Singleton { // Public values property real cpuUsage: 0 property real cpuTemp: 0 + property real memoryUsageGb: 0 property real memoryUsagePer: 0 property real diskUsage: 0 @@ -25,6 +26,7 @@ Singleton { const data = JSON.parse(line) root.cpuUsage = data.cpu root.cpuTemp = data.cputemp + root.memoryUsageGb = data.memgb root.memoryUsagePer = data.memper root.diskUsage = data.diskper } catch (e) { From a57f2f5d683ebc2d026d1fd9dbe58855529cdea2 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 13 Aug 2025 08:05:29 -0400 Subject: [PATCH 2/8] Formatting --- Modules/Background/Overview.qml | 88 ++++++++--------- Modules/Bar/Bar.qml | 1 - Modules/Bar/NotificationHistory.qml | 2 +- Modules/Bar/NotificationHistoryPanel.qml | 2 +- Modules/Settings/Tabs/Audio.qml | 118 +++++++++++------------ Modules/Settings/Tabs/Misc.qml | 2 - Services/NotificationService.qml | 37 +++---- Services/Settings.qml | 12 ++- 8 files changed, 132 insertions(+), 130 deletions(-) diff --git a/Modules/Background/Overview.qml b/Modules/Background/Overview.qml index 3a6f05c..b44f05e 100644 --- a/Modules/Background/Overview.qml +++ b/Modules/Background/Overview.qml @@ -7,7 +7,7 @@ import qs.Widgets NLoader { active: Workspaces.isNiri - + Component.onCompleted: { if (Workspaces.isNiri) { console.log("[Overview] Loading Overview component (Niri detected)") @@ -15,55 +15,55 @@ NLoader { console.log("[Overview] Skipping Overview component (Niri not detected)") } } - + sourceComponent: Variants { model: Quickshell.screens delegate: PanelWindow { - required property ShellScreen modelData - property string wallpaperSource: Wallpapers.currentWallpaper !== "" - && !Settings.data.wallpaper.swww.enabled ? Wallpapers.currentWallpaper : "" + required property ShellScreen modelData + property string wallpaperSource: Wallpapers.currentWallpaper !== "" + && !Settings.data.wallpaper.swww.enabled ? Wallpapers.currentWallpaper : "" - visible: wallpaperSource !== "" && !Settings.data.wallpaper.swww.enabled - color: "transparent" - screen: modelData - WlrLayershell.layer: WlrLayer.Background - WlrLayershell.exclusionMode: ExclusionMode.Ignore - WlrLayershell.namespace: "quickshell-overview" + visible: wallpaperSource !== "" && !Settings.data.wallpaper.swww.enabled + color: "transparent" + screen: modelData + WlrLayershell.layer: WlrLayer.Background + WlrLayershell.exclusionMode: ExclusionMode.Ignore + WlrLayershell.namespace: "quickshell-overview" - anchors { - top: true - bottom: true - right: true - left: true + anchors { + top: true + bottom: true + right: true + left: true + } + + Image { + id: bgImage + + anchors.fill: parent + fillMode: Image.PreserveAspectCrop + source: wallpaperSource + cache: true + smooth: true + mipmap: false + visible: wallpaperSource !== "" + } + + MultiEffect { + id: overviewBgBlur + + anchors.fill: parent + source: bgImage + blurEnabled: true + blur: 0.48 + blurMax: 128 + } + + Rectangle { + anchors.fill: parent + color: Qt.rgba(Colors.backgroundPrimary.r, Colors.backgroundPrimary.g, Colors.backgroundPrimary.b, 0.5) + } } - - Image { - id: bgImage - - anchors.fill: parent - fillMode: Image.PreserveAspectCrop - source: wallpaperSource - cache: true - smooth: true - mipmap: false - visible: wallpaperSource !== "" - } - - MultiEffect { - id: overviewBgBlur - - anchors.fill: parent - source: bgImage - blurEnabled: true - blur: 0.48 - blurMax: 128 - } - - Rectangle { - anchors.fill: parent - color: Qt.rgba(Colors.backgroundPrimary.r, Colors.backgroundPrimary.g, Colors.backgroundPrimary.b, 0.5) - } - } } } diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 821464b..3a0a1ab 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -58,7 +58,6 @@ Variants { // anchors.verticalCenter: parent.verticalCenter // font.weight: Style.fontWeightBold // } - SystemMonitor {} } diff --git a/Modules/Bar/NotificationHistory.qml b/Modules/Bar/NotificationHistory.qml index 2d0b638..ccbc019 100644 --- a/Modules/Bar/NotificationHistory.qml +++ b/Modules/Bar/NotificationHistory.qml @@ -26,4 +26,4 @@ NIconButton { NotificationHistoryPanel { id: notificationHistoryPanelLoader } -} \ No newline at end of file +} diff --git a/Modules/Bar/NotificationHistoryPanel.qml b/Modules/Bar/NotificationHistoryPanel.qml index db1fd42..44f791e 100644 --- a/Modules/Bar/NotificationHistoryPanel.qml +++ b/Modules/Bar/NotificationHistoryPanel.qml @@ -159,4 +159,4 @@ NLoader { } } } -} \ No newline at end of file +} diff --git a/Modules/Settings/Tabs/Audio.qml b/Modules/Settings/Tabs/Audio.qml index 1874607..3f783bb 100644 --- a/Modules/Settings/Tabs/Audio.qml +++ b/Modules/Settings/Tabs/Audio.qml @@ -88,14 +88,14 @@ ColumnLayout { } } - NToggle { - id: allowOverdrive - label: "Allow Volume Overdrive" - description: "Enable volume levels above 100% (up to 200%)" - value: Settings.data.audio ? Settings.data.audio.volumeOverdrive : false - onToggled: function (checked) { - Settings.data.audio.volumeOverdrive = checked - + NToggle { + id: allowOverdrive + label: "Allow Volume Overdrive" + description: "Enable volume levels above 100% (up to 200%)" + value: Settings.data.audio ? Settings.data.audio.volumeOverdrive : false + onToggled: function (checked) { + Settings.data.audio.volumeOverdrive = checked + // If overdrive is disabled and current volume is above 100%, cap it if (!checked && Audio.volume > 1.0) { Audio.volumeSet(1.0) @@ -140,58 +140,56 @@ ColumnLayout { font.weight: Style.fontWeightBold color: Colors.textPrimary Layout.bottomMargin: Style.marginSmall * scaling - } - - // Output Device - NComboBox { - id: outputDeviceCombo - label: "Output Device" - description: "Default audio output device" - optionsKeys: outputDeviceKeys - optionsLabels: outputDeviceLabels - currentKey: Audio.sink ? Audio.sink.id.toString() : "" - onSelected: function (key) { - // Find the node by ID and set it as preferred - for (let i = 0; i < Pipewire.nodes.count; i++) { - let node = Pipewire.nodes.get(i) - if (node.id.toString() === key && node.isSink) { - Pipewire.preferredDefaultAudioSink = node - break - } - } - } - } - - // Input Device - NComboBox { - id: inputDeviceCombo - label: "Input Device" - description: "Default audio input device" - optionsKeys: inputDeviceKeys - optionsLabels: inputDeviceLabels - currentKey: Audio.source ? Audio.source.id.toString() : "" - onSelected: function (key) { - // Find the node by ID and set it as preferred - for (let i = 0; i < Pipewire.nodes.count; i++) { - let node = Pipewire.nodes.get(i) - if (node.id.toString() === key && !node.isSink) { - Pipewire.preferredDefaultAudioSource = node - break - } - } - } - } - - - } - - // Divider - NDivider { - Layout.fillWidth: true - Layout.topMargin: Style.marginLarge * scaling - Layout.bottomMargin: Style.marginMedium * scaling } + // Output Device + NComboBox { + id: outputDeviceCombo + label: "Output Device" + description: "Default audio output device" + optionsKeys: outputDeviceKeys + optionsLabels: outputDeviceLabels + currentKey: Audio.sink ? Audio.sink.id.toString() : "" + onSelected: function (key) { + // Find the node by ID and set it as preferred + for (var i = 0; i < Pipewire.nodes.count; i++) { + let node = Pipewire.nodes.get(i) + if (node.id.toString() === key && node.isSink) { + Pipewire.preferredDefaultAudioSink = node + break + } + } + } + } + + // Input Device + NComboBox { + id: inputDeviceCombo + label: "Input Device" + description: "Default audio input device" + optionsKeys: inputDeviceKeys + optionsLabels: inputDeviceLabels + currentKey: Audio.source ? Audio.source.id.toString() : "" + onSelected: function (key) { + // Find the node by ID and set it as preferred + for (var i = 0; i < Pipewire.nodes.count; i++) { + let node = Pipewire.nodes.get(i) + if (node.id.toString() === key && !node.isSink) { + Pipewire.preferredDefaultAudioSource = node + break + } + } + } + } + } + + // Divider + NDivider { + Layout.fillWidth: true + Layout.topMargin: Style.marginLarge * scaling + Layout.bottomMargin: Style.marginMedium * scaling + } + // Audio Visualizer Category ColumnLayout { spacing: Style.marginSmall * scaling @@ -298,11 +296,11 @@ ColumnLayout { outputDeviceCombo.optionsKeys = outputDeviceKeys outputDeviceCombo.optionsLabels = outputDeviceLabels } - + if (inputDeviceCombo) { inputDeviceCombo.optionsKeys = inputDeviceKeys inputDeviceCombo.optionsLabels = inputDeviceLabels } } } -} \ No newline at end of file +} diff --git a/Modules/Settings/Tabs/Misc.qml b/Modules/Settings/Tabs/Misc.qml index 6ffa0a3..22958a8 100644 --- a/Modules/Settings/Tabs/Misc.qml +++ b/Modules/Settings/Tabs/Misc.qml @@ -39,8 +39,6 @@ ColumnLayout { color: Colors.textPrimary Layout.bottomMargin: Style.marginSmall * scaling } - - } } } diff --git a/Services/NotificationService.qml b/Services/NotificationService.qml index 16ddb38..f771ef3 100644 --- a/Services/NotificationService.qml +++ b/Services/NotificationService.qml @@ -57,7 +57,8 @@ QtObject { property int maxHistory: 100 // Cached history file path - property string historyFile: Quickshell.env("NOCTALIA_NOTIF_HISTORY_FILE") || (Settings.cacheDir + "notifications.json") + property string historyFile: Quickshell.env("NOCTALIA_NOTIF_HISTORY_FILE") + || (Settings.cacheDir + "notifications.json") // Persisted storage for history property FileView historyFileView: FileView { @@ -130,12 +131,12 @@ QtObject { // Add a simplified copy into persistent history function addToHistory(notification) { historyModel.insert(0, { - "summary": notification.summary, - "body": notification.body, - "appName": notification.appName, - "urgency": notification.urgency, - "timestamp": new Date() - }) + "summary": notification.summary, + "body": notification.body, + "appName": notification.appName, + "urgency": notification.urgency, + "timestamp": new Date() + }) while (historyModel.count > maxHistory) { historyModel.remove(historyModel.count - 1) } @@ -155,12 +156,12 @@ QtObject { for (var i = 0; i < items.length; i++) { const it = items[i] historyModel.append({ - "summary": it.summary || "", - "body": it.body || "", - "appName": it.appName || "", - "urgency": it.urgency, - "timestamp": it.timestamp ? new Date(it.timestamp) : new Date() - }) + "summary": it.summary || "", + "body": it.body || "", + "appName": it.appName || "", + "urgency": it.urgency, + "timestamp": it.timestamp ? new Date(it.timestamp) : new Date() + }) } } catch (e) { console.error("[Notifications] Failed to load history:", e) @@ -174,11 +175,11 @@ QtObject { for (var i = 0; i < historyModel.count; i++) { const n = historyModel.get(i) arr.push({ - summary: n.summary, - body: n.body, - appName: n.appName, - urgency: n.urgency, - timestamp: (n.timestamp instanceof Date) ? n.timestamp.getTime() : n.timestamp + "summary": n.summary, + "body": n.body, + "appName": n.appName, + "urgency": n.urgency, + "timestamp": (n.timestamp instanceof Date) ? n.timestamp.getTime() : n.timestamp }) } historyAdapter.history = arr diff --git a/Services/Settings.qml b/Services/Settings.qml index 7305ef0..392f42e 100644 --- a/Services/Settings.qml +++ b/Services/Settings.qml @@ -193,8 +193,14 @@ Singleton { Connections { target: adapter.wallpaper - function onIsRandomChanged() { Wallpapers.toggleRandomWallpaper() } - function onRandomIntervalChanged() { Wallpapers.restartRandomWallpaperTimer() } - function onDirectoryChanged() { Wallpapers.loadWallpapers() } + function onIsRandomChanged() { + Wallpapers.toggleRandomWallpaper() + } + function onRandomIntervalChanged() { + Wallpapers.restartRandomWallpaperTimer() + } + function onDirectoryChanged() { + Wallpapers.loadWallpapers() + } } } From a01d730f3dcf1424ac9d68af850f4fe95db89469 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 13 Aug 2025 08:21:47 -0400 Subject: [PATCH 3/8] NComBox: added safe guards if not options or invalid option index --- Widgets/NComboBox.qml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Widgets/NComboBox.qml b/Widgets/NComboBox.qml index 7219391..1950f0e 100644 --- a/Widgets/NComboBox.qml +++ b/Widgets/NComboBox.qml @@ -69,9 +69,7 @@ ColumnLayout { font.pointSize: Style.fontSizeMedium * scaling verticalAlignment: Text.AlignVCenter elide: Text.ElideRight - text: { - return root.optionsLabels[combo.currentIndex] - } + text: (combo.currentIndex < root.optionsLabels.length) ? root.optionsLabels[combo.currentIndex] : ""; } // Drop down indicator @@ -110,9 +108,7 @@ ColumnLayout { highlighted: combo.highlightedIndex === index contentItem: NText { - text: { - return root.optionsLabels[combo.model.indexOf(modelData)] - } + text: (combo.model.indexOf(modelData)< root.optionsLabels.length) ? root.optionsLabels[combo.model.indexOf(modelData)] : "" font.pointSize: Style.fontSizeMedium * scaling color: highlighted ? Colors.backgroundPrimary : Colors.textPrimary verticalAlignment: Text.AlignVCenter From 07b29ee8730650b5acacdca319218173ad1a34ff Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 13 Aug 2025 08:30:52 -0400 Subject: [PATCH 4/8] NComboBox: better safe guards --- Widgets/NComboBox.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Widgets/NComboBox.qml b/Widgets/NComboBox.qml index 1950f0e..e34e9f0 100644 --- a/Widgets/NComboBox.qml +++ b/Widgets/NComboBox.qml @@ -69,7 +69,8 @@ ColumnLayout { font.pointSize: Style.fontSizeMedium * scaling verticalAlignment: Text.AlignVCenter elide: Text.ElideRight - text: (combo.currentIndex < root.optionsLabels.length) ? root.optionsLabels[combo.currentIndex] : ""; + text: (combo.currentIndex >= 0 + && combo.currentIndex < root.optionsLabels.length) ? root.optionsLabels[combo.currentIndex] : "" } // Drop down indicator @@ -108,7 +109,8 @@ ColumnLayout { highlighted: combo.highlightedIndex === index contentItem: NText { - text: (combo.model.indexOf(modelData)< root.optionsLabels.length) ? root.optionsLabels[combo.model.indexOf(modelData)] : "" + text: (combo.model.indexOf(modelData) >= 0 && combo.model.indexOf( + modelData) < root.optionsLabels.length) ? root.optionsLabels[combo.model.indexOf(modelData)] : "" font.pointSize: Style.fontSizeMedium * scaling color: highlighted ? Colors.backgroundPrimary : Colors.textPrimary verticalAlignment: Text.AlignVCenter From 2fe3ad48a79258211b3690ea37da8511289fedb9 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 13 Aug 2025 08:34:30 -0400 Subject: [PATCH 5/8] Bar Volume: OnClick open SettingsPanel with audio tab focused --- Modules/Bar/Bar.qml | 2 -- Modules/Bar/Volume.qml | 14 ++------------ Modules/Settings/SettingsPanel.qml | 16 ++++++---------- 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 3a0a1ab..bd14629 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -15,8 +15,6 @@ Variants { required property ShellScreen modelData readonly property real scaling: Scaling.scale(screen) - property var settingsPanel: null - screen: modelData implicitHeight: Style.barHeight * scaling color: "transparent" diff --git a/Modules/Bar/Volume.qml b/Modules/Bar/Volume.qml index 5e02908..7d17e8f 100644 --- a/Modules/Bar/Volume.qml +++ b/Modules/Bar/Volume.qml @@ -10,9 +10,6 @@ Item { width: pill.width height: pill.height - // Reference to settings panel - property var settingsPanel: null - Component.onCompleted: { console.log("[Volume] settingsPanel received:", !!settingsPanel) } @@ -87,15 +84,8 @@ Item { } } onClicked: { - // Open settings panel and navigate to Audio tab - console.log("[Volume] Attempting to open settings panel...") - try { - settingsPanel.isLoaded = true - settingsPanel.content.currentTabIndex = 5 // Audio tab index - console.log("[Volume] Settings panel opened successfully") - } catch (error) { - console.log("[Volume] Error opening settings panel:", error) - } + settingsPanel.currentTabIndex = 5 // Audio tab index + settingsPanel.isLoaded = true } } } diff --git a/Modules/Settings/SettingsPanel.qml b/Modules/Settings/SettingsPanel.qml index 189e969..a073432 100644 --- a/Modules/Settings/SettingsPanel.qml +++ b/Modules/Settings/SettingsPanel.qml @@ -10,6 +10,8 @@ import qs.Widgets NLoader { id: root + property int currentTabIndex: 0 + content: Component { NPanel { id: panel @@ -18,7 +20,6 @@ NLoader { WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - property int currentTabIndex: 0 property var tabsModel: [{ "label": "General", "icon": "tune", @@ -65,11 +66,6 @@ NLoader { "source": "Tabs/About.qml" }] - onVisibleChanged: { - if (visible) - currentTabIndex = 0 - } - Component.onCompleted: show() Rectangle { @@ -113,7 +109,7 @@ NLoader { delegate: Rectangle { id: tabItem - readonly property bool selected: index === panel.currentTabIndex + readonly property bool selected: index === currentTabIndex width: parent.width height: 32 * scaling // Back to original height radius: Style.radiusSmall * scaling @@ -154,7 +150,7 @@ NLoader { onEntered: tabItem.hovering = true onExited: tabItem.hovering = false onCanceled: tabItem.hovering = false - onClicked: panel.currentTabIndex = index + onClicked: currentTabIndex = index } } } @@ -185,7 +181,7 @@ NLoader { // Tab label on the main right NText { - text: panel.tabsModel[panel.currentTabIndex].label + text: panel.tabsModel[currentTabIndex].label font.pointSize: Style.fontSizeLarge * scaling font.weight: Style.fontWeightBold color: Colors.accentPrimary @@ -209,7 +205,7 @@ NLoader { id: stack Layout.fillWidth: true Layout.fillHeight: true - currentIndex: panel.currentTabIndex + currentIndex: currentTabIndex Tabs.General {} Tabs.Bar {} From c4264f2f097596dcc9e9c46468c6807441530abe Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 13 Aug 2025 08:37:45 -0400 Subject: [PATCH 6/8] Settings: hiding misc tabs for now, as its empty --- Modules/Settings/SettingsPanel.qml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Modules/Settings/SettingsPanel.qml b/Modules/Settings/SettingsPanel.qml index a073432..7625af4 100644 --- a/Modules/Settings/SettingsPanel.qml +++ b/Modules/Settings/SettingsPanel.qml @@ -20,7 +20,8 @@ NLoader { WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - property var tabsModel: [{ + property var tabsModel: [ + { "label": "General", "icon": "tune", "source": "Tabs/General.qml" @@ -56,11 +57,13 @@ NLoader { "label": "Wallpaper Selector", "icon": "wallpaper_slideshow", "source": "Tabs/WallpaperSelector.qml" - }, { - "label": "Misc", - "icon": "more_horiz", - "source": "Tabs/Misc.qml" - }, { + }, + // { + // "label": "Misc", + // "icon": "more_horiz", + // "source": "Tabs/Misc.qml" + // }, + { "label": "About", "icon": "info", "source": "Tabs/About.qml" @@ -109,7 +112,7 @@ NLoader { delegate: Rectangle { id: tabItem - readonly property bool selected: index === currentTabIndex + width: parent.width height: 32 * scaling // Back to original height radius: Style.radiusSmall * scaling @@ -117,6 +120,8 @@ NLoader { border.color: "transparent" border.width: 0 + readonly property bool selected: index === currentTabIndex + // Subtle hover effect: only icon/text color tint on hover property bool hovering: false From 94b4a096e794c2a5d9bf4419834c5346ee25e0de Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 13 Aug 2025 08:39:12 -0400 Subject: [PATCH 7/8] SidePanel - Utilities: rewired the wallpaper selector shortcut --- Modules/SidePanel/Cards/UtilitiesCard.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/SidePanel/Cards/UtilitiesCard.qml b/Modules/SidePanel/Cards/UtilitiesCard.qml index 8040e06..4f6a17b 100644 --- a/Modules/SidePanel/Cards/UtilitiesCard.qml +++ b/Modules/SidePanel/Cards/UtilitiesCard.qml @@ -30,6 +30,10 @@ NBox { // Wallpaper NIconButton { icon: "image" + onClicked: { + settingsPanel.currentTabIndex = 8 // Audio tab index + settingsPanel.isLoaded = true + } } Item { From 8663b36ecbcb076895be60bf5a16c06aef180c40 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 13 Aug 2025 09:03:09 -0400 Subject: [PATCH 8/8] PowerMenu wip --- Modules/Settings/SettingsPanel.qml | 10 +- Modules/SidePanel/Cards/ProfileCard.qml | 381 ++++++++++++++++++++++++ Services/Settings.qml | 2 +- 3 files changed, 386 insertions(+), 7 deletions(-) diff --git a/Modules/Settings/SettingsPanel.qml b/Modules/Settings/SettingsPanel.qml index 7625af4..e237c6a 100644 --- a/Modules/Settings/SettingsPanel.qml +++ b/Modules/Settings/SettingsPanel.qml @@ -20,8 +20,7 @@ NLoader { WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - property var tabsModel: [ - { + property var tabsModel: [{ "label": "General", "icon": "tune", "source": "Tabs/General.qml" @@ -57,8 +56,7 @@ NLoader { "label": "Wallpaper Selector", "icon": "wallpaper_slideshow", "source": "Tabs/WallpaperSelector.qml" - }, - // { + }, // { // "label": "Misc", // "icon": "more_horiz", // "source": "Tabs/Misc.qml" @@ -112,7 +110,7 @@ NLoader { delegate: Rectangle { id: tabItem - + width: parent.width height: 32 * scaling // Back to original height radius: Style.radiusSmall * scaling @@ -120,7 +118,7 @@ NLoader { border.color: "transparent" border.width: 0 - readonly property bool selected: index === currentTabIndex + readonly property bool selected: index === currentTabIndex // Subtle hover effect: only icon/text color tint on hover property bool hovering: false diff --git a/Modules/SidePanel/Cards/ProfileCard.qml b/Modules/SidePanel/Cards/ProfileCard.qml index 90a23db..3fa605d 100644 --- a/Modules/SidePanel/Cards/ProfileCard.qml +++ b/Modules/SidePanel/Cards/ProfileCard.qml @@ -62,11 +62,18 @@ NBox { } } NIconButton { + id: powerButton icon: "power_settings_new" + onClicked: { + //settingsPanel.isLoaded = !settingsPanel.isLoaded + powerMenu.show() + } } } } + // ---------------------------------- + // Uptime Timer { interval: 60000 repeat: true @@ -99,4 +106,378 @@ NBox { } } } + + // ---------------------------------- + // Logout menu + function logout() { + if (WorkspaceManager.isNiri) { + logoutProcessNiri.running = true + } else if (WorkspaceManager.isHyprland) { + logoutProcessHyprland.running = true + } else { + console.warn("No supported compositor detected for logout") + } + } + + function suspend() { + suspendProcess.running = true + } + + function shutdown() { + shutdownProcess.running = true + } + + function reboot() { + rebootProcess.running = true + } + + function updateSystemInfo() { + uptimeProcess.running = true + } + + + Process { + id: shutdownProcess + + command: ["shutdown", "-h", "now"] + running: false + } + + Process { + id: rebootProcess + + command: ["reboot"] + running: false + } + + Process { + id: suspendProcess + + command: ["systemctl", "suspend"] + running: false + } + + Process { + id: logoutProcessNiri + + command: ["niri", "msg", "action", "quit", "--skip-confirmation"] + running: false + } + + Process { + id: logoutProcessHyprland + + command: ["hyprctl", "dispatch", "exit"] + running: false + } + + Process { + id: logoutProcess + + command: ["loginctl", "terminate-user", Quickshell.env("USER")] + running: false + } + + NPanel { + id: powerMenu + + anchors.top: powerButton.bottom + anchors.right: powerButton.right + + Rectangle { + width: 160 * scaling + height: 220 * scaling + color: Colors.surface + radius: 8 * scaling + border.color: Colors.outline + border.width: 1 * scaling + visible: true + z: 9999 + anchors.top: parent.top + anchors.right: parent.right + anchors.rightMargin: 32 * scaling + anchors.topMargin: powerButton.y + powerButton.height + 48 * scaling + + // Prevent closing when clicking in the panel bg + MouseArea { + anchors.fill: parent + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 8 * scaling + spacing: 4 * scaling + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 36 * scaling + radius: 6 * scaling + color: lockButtonArea.containsMouse ? Colors.accentPrimary : "transparent" + + Item { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 12 * scaling + anchors.rightMargin: 12 * scaling + + Row { + id: lockRow + spacing: 8 * scaling + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Text { + text: "lock_outline" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 * scaling + color: lockButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + + Text { + text: "Lock Screen" + font.pixelSize: 14 * scaling + color: lockButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + } + } + + MouseArea { + id: lockButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + lockScreen.locked = true + systemMenu.visible = false + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 36 * scaling + radius: 6 * scaling + color: suspendButtonArea.containsMouse ? Colors.accentPrimary : "transparent" + + Item { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 12 * scaling + anchors.rightMargin: 12 * scaling + + Row { + id: suspendRow + spacing: 8 * scaling + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Text { + text: "bedtime" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 * scaling + color: suspendButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + + Text { + text: "Suspend" + font.pixelSize: 14 * scaling + color: suspendButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + } + } + + MouseArea { + id: suspendButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + suspend() + systemMenu.visible = false + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 36 * scaling + radius: 6 * scaling + color: rebootButtonArea.containsMouse ? Colors.accentPrimary : "transparent" + + Item { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 12 * scaling + anchors.rightMargin: 12 * scaling + + Row { + id: rebootRow + spacing: 8 * scaling + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Text { + text: "refresh" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 * scaling + color: rebootButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + + Text { + text: "Reboot" + font.pixelSize: 14 * scaling + color: rebootButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + } + } + + MouseArea { + id: rebootButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + reboot() + systemMenu.visible = false + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 36 * scaling + radius: 6 * scaling + color: logoutButtonArea.containsMouse ? Colors.accentPrimary : "transparent" + + Item { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 12 * scaling + anchors.rightMargin: 12 * scaling + + Row { + id: logoutRow + spacing: 8 * scaling + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Text { + text: "exit_to_app" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 * scaling + color: logoutButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + + Text { + text: "Logout" + font.pixelSize: 14 * scaling + color: logoutButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + } + } + + MouseArea { + id: logoutButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + logout() + systemMenu.visible = false + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 36 * scaling + radius: 6 * scaling + color: shutdownButtonArea.containsMouse ? Colors.accentPrimary : "transparent" + + Item { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 12 * scaling + anchors.rightMargin: 12 * scaling + + Row { + id: shutdownRow + spacing: 8 * scaling + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + Text { + text: "power_settings_new" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 * scaling + color: shutdownButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + + Text { + text: "Shutdown" + font.pixelSize: 14 * scaling + color: shutdownButtonArea.containsMouse ? Colors.onAccent : Colors.textPrimary + verticalAlignment: Text.AlignVCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: 1 * scaling + } + } + } + + MouseArea { + id: shutdownButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + shutdown() + systemMenu.visible = false + } + } + } + } + } + } } diff --git a/Services/Settings.qml b/Services/Settings.qml index 392f42e..301714a 100644 --- a/Services/Settings.qml +++ b/Services/Settings.qml @@ -53,7 +53,7 @@ Singleton { onLoaded: function () { Qt.callLater(function () { if (adapter.wallpaper.current !== "") { - console.log("Settings: Initializing wallpaper to:", adapter.wallpaper.current) + console.log("[Settings] Set current wallpaper") Wallpapers.setCurrentWallpaper(adapter.wallpaper.current, true) } })