audio module, system stats, fixed activewindow

This commit is contained in:
Zacharias-Brohn
2025-10-13 20:06:51 +02:00
parent 51cc2d867b
commit 375fd00d21
8 changed files with 382 additions and 8 deletions
+14
View File
@@ -48,6 +48,20 @@ Scope {
Layout.topMargin: 6 Layout.topMargin: 6
Layout.bottomMargin: 6 Layout.bottomMargin: 6
} }
AudioWidget {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillHeight: true
Layout.topMargin: 6
Layout.bottomMargin: 6
}
Resources {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Layout.fillHeight: true
Layout.topMargin: 6
Layout.bottomMargin: 6
}
} }
RowLayout { RowLayout {
+1 -1
View File
@@ -5,5 +5,5 @@ import Quickshell.Hyprland
Singleton { Singleton {
id: root id: root
property string activeWindow: Hyprland.activeToplevel?.lastIpcObject.class || "" property string activeWindow: Hyprland.activeToplevel?.title || ""
} }
+112
View File
@@ -0,0 +1,112 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Services.Pipewire
import Quickshell.Widgets
import qs.Modules
Item {
id: root
implicitWidth: 150
implicitHeight: 100
PwObjectTracker {
objects: [ Pipewire.defaultAudioSink ]
}
PwObjectTracker {
objects: [ Pipewire.defaultAudioSource ]
}
Rectangle {
anchors.fill: parent
radius: height / 2
color: "#40000000"
// Background circle
Rectangle {
anchors.centerIn: parent
width: parent.width
height: parent.height
radius: width / 2
color: "transparent"
border.color: "#30ffffff"
border.width: 0
}
RowLayout {
anchors {
fill: parent
leftMargin: 10
rightMargin: 15
}
Text {
Layout.alignment: Qt.AlignVCenter
font.family: "Material Symbols Rounded"
font.pixelSize: 18
text: "\ue050" // volume_up icon
color: "#ffffff"
}
Rectangle {
Layout.fillWidth: true
implicitHeight: 4
radius: 20
color: "#50ffffff"
Rectangle {
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
implicitWidth: parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0)
radius: parent.radius
color: "#ffffff"
}
}
Text {
Layout.alignment: Qt.AlignVCenter
font.family: "Material Symbols Rounded"
font.pixelSize: 18
text: "\ue029"
color: (Pipewire.defaultAudioSource?.audio.muted ?? false) ? "#ff4444" : "#ffffff"
}
Rectangle {
Layout.fillWidth: true
implicitHeight: 4
radius: 20
color: "#50ffffff"
Rectangle {
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
implicitWidth: parent.width * (Pipewire.defaultAudioSource?.audio.volume ?? 0)
radius: parent.radius
color: (Pipewire.defaultAudioSource?.audio.muted ?? false) ? "#ff4444" : "#ffffff"
}
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: pavucontrolProc.running = true
}
Process {
id: pavucontrolProc
command: ["pavucontrol"]
running: false
}
}
}
+99
View File
@@ -0,0 +1,99 @@
import qs.Modules
import QtQuick
import QtQuick.Layouts
import Quickshell
Item {
id: root
required property double percentage
property int warningThreshold: 100
property bool shown: true
clip: true
visible: width > 0 && height > 0
implicitWidth: resourceRowLayout.x < 0 ? 0 : resourceRowLayout.implicitWidth
implicitHeight: 22
property bool warning: percentage * 100 >= warningThreshold
RowLayout {
id: resourceRowLayout
spacing: 2
x: shown ? 0 : -resourceRowLayout.width
anchors {
verticalCenter: parent.verticalCenter
}
Item {
Layout.alignment: Qt.AlignVCenter
implicitWidth: 14
implicitHeight: root.implicitHeight
// Background circle
Rectangle {
id: backgroundCircle
anchors.centerIn: parent
width: 14
height: 14
radius: height / 2
color: "#40000000"
border.color: "#404040"
border.width: 1
}
// Pie progress indicator
Canvas {
id: progressCanvas
anchors.fill: backgroundCircle
Connections {
target: root
function onPercentageChanged() {
progressCanvas.requestPaint()
}
function onWarningChanged() {
progressCanvas.requestPaint()
}
}
onPaint: {
var ctx = getContext("2d");
ctx.reset();
var centerX = width / 2;
var centerY = height / 2;
var radius = width / 2;
var startAngle = -Math.PI / 2; // Start at top
var endAngle = startAngle + (2 * Math.PI * percentage);
ctx.fillStyle = warning ? "#ff6b6b" : "#4080ff";
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.lineTo(centerX, centerY);
ctx.fill();
}
}
// // Percentage text
// Item {
// anchors.centerIn: backgroundCircle
// implicitWidth: fullPercentageTextMetrics.width
// implicitHeight: percentageText.implicitHeight
//
// TextMetrics {
// id: fullPercentageTextMetrics
// text: "100"
// font.pixelSize: 12
// }
//
// Text {
// id: percentageText
// anchors.centerIn: parent
// color: "#ffffff"
// font.pixelSize: 12
// font.bold: true
// text: `${Math.round(percentage * 100).toString()}`
// }
// }
}
}
}
+78
View File
@@ -0,0 +1,78 @@
pragma Singleton
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
/**
* Simple polled resource usage service with RAM, Swap, and CPU usage.
*/
Singleton {
property double memoryTotal: 1
property double memoryFree: 1
property double memoryUsed: memoryTotal - memoryFree
property double memoryUsedPercentage: memoryUsed / memoryTotal
property double swapTotal: 1
property double swapFree: 1
property double swapUsed: swapTotal - swapFree
property double swapUsedPercentage: swapTotal > 0 ? (swapUsed / swapTotal) : 0
property double cpuUsage: 0
property var previousCpuStats
property double gpuUsage: 0
property double gpuMemUsage: 0
Timer {
interval: 1
running: true
repeat: true
onTriggered: {
// Reload files
fileMeminfo.reload()
fileStat.reload()
// Parse memory and swap usage
const textMeminfo = fileMeminfo.text()
memoryTotal = Number(textMeminfo.match(/MemTotal: *(\d+)/)?.[1] ?? 1)
memoryFree = Number(textMeminfo.match(/MemAvailable: *(\d+)/)?.[1] ?? 0)
swapTotal = Number(textMeminfo.match(/SwapTotal: *(\d+)/)?.[1] ?? 1)
swapFree = Number(textMeminfo.match(/SwapFree: *(\d+)/)?.[1] ?? 0)
// Parse CPU usage
const textStat = fileStat.text()
const cpuLine = textStat.match(/^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/)
if (cpuLine) {
const stats = cpuLine.slice(1).map(Number)
const total = stats.reduce((a, b) => a + b, 0)
const idle = stats[3]
if (previousCpuStats) {
const totalDiff = total - previousCpuStats.total
const idleDiff = idle - previousCpuStats.idle
cpuUsage = totalDiff > 0 ? (1 - idleDiff / totalDiff) : 0
}
previousCpuStats = { total, idle }
}
processGpu.running = true
interval = 1000
}
}
FileView { id: fileMeminfo; path: "/proc/meminfo" }
FileView { id: fileStat; path: "/proc/stat" }
Process {
id: processGpu
command: ["nvidia-smi", "--query-gpu=utilization.gpu,utilization.memory", "--format=csv,noheader,nounits"]
running: false
stdout: StdioCollector {
onStreamFinished: {
const parts = this.text.trim().split(", ")
gpuUsage = Number(parts[0]) / 100
gpuMemUsage = Number(parts[1]) / 100
}
}
}
}
+71
View File
@@ -0,0 +1,71 @@
import QtQuick
import Quickshell
import QtQuick.Layouts
import qs.Modules
Item {
id: root
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
Rectangle {
anchors.fill: parent
color: "#40000000"
radius: height / 2
RowLayout {
id: rowLayout
spacing: 6
anchors.fill: parent
anchors.leftMargin: 5
anchors.rightMargin: 5
Text {
Layout.alignment: Qt.AlignVCenter
font.family: "Material Symbols Rounded"
font.pixelSize: 18
text: "\uf7a3"
color: "#ffffff"
}
Resource {
percentage: ResourceUsage.memoryUsedPercentage
}
Text {
Layout.alignment: Qt.AlignVCenter
font.family: "Material Symbols Rounded"
font.pixelSize: 18
text: "\ue322"
color: "#ffffff"
}
Resource {
percentage: ResourceUsage.cpuUsage
}
Text {
Layout.alignment: Qt.AlignVCenter
font.family: "Material Symbols Rounded"
font.pixelSize: 18
text: "\ue30d"
color: "#ffffff"
}
Resource {
percentage: ResourceUsage.gpuUsage
}
Text {
Layout.alignment: Qt.AlignVCenter
font.family: "Material Symbols Rounded"
font.pixelSize: 18
text: "\ue30d"
color: "#ffffff"
}
Resource {
percentage: ResourceUsage.gpuMemUsage
}
}
}
}
View File
+7 -7
View File
@@ -7,9 +7,9 @@ import Quickshell.Hyprland
Rectangle { Rectangle {
id: root id: root
implicitWidth: workspacesRow.implicitWidth + 12 implicitWidth: workspacesRow.implicitWidth + 10
implicitHeight: workspacesRow.implicitHeight + 8 implicitHeight: workspacesRow.implicitHeight + 7
color: "#40000000" color: "#40000000"
radius: height / 2 radius: height / 2
@@ -24,7 +24,7 @@ Rectangle {
id: workspacesRow id: workspacesRow
anchors.left: parent.left anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 6 anchors.leftMargin: 5
spacing: 8 spacing: 8
Repeater { Repeater {
@@ -33,9 +33,9 @@ Rectangle {
Rectangle { Rectangle {
required property var modelData required property var modelData
width: 12 width: 14
height: 12 height: 14
radius: 6 radius: height / 2
color: modelData.id === Hyprland.focusedWorkspace.id ? "#4080ff" : "#606060" color: modelData.id === Hyprland.focusedWorkspace.id ? "#4080ff" : "#606060"