//@ pragma UseQApplication import Quickshell import Quickshell.I3 import Quickshell.Wayland import Quickshell.Io import Quickshell.Services.SystemTray import Quickshell.Widgets import Quickshell.Services.UPower import Quickshell.Services.Pipewire import QtQuick import QtQuick.Layouts // Some credit goes to my friends Lucy and Babsi // Work in progress (Audio) PanelWindow { id: root property color colBg: "#64320a" property color colAppBg: "#2d1405" property color colFg: "#d2d2d2" property color colDark: "#230f05" property string fontFamily: "Noto Sans" property int fontSize: 18 property int appletRadHeight: 25 property int appletSpacing: 6 property int cpuUsage: 0 property var lastCpuIdle: 0 property var lastCpuTotal: 0 property real ramUsage: 0.00 property var batTime: 0 property var batCharMin: (UPower.displayDevice.timeToFull / 60).toFixed(2) property var batDisMin: (UPower.displayDevice.timeToEmpty / 60).toFixed(2) anchors { right: true left: true top:true } color: "transparent" implicitHeight: root.appletRadHeight + 10 margins { right: 5 left: 5 top: 5 bottom: 0 } Rectangle { color: root.colBg anchors.fill: parent radius: root.appletRadHeight } RowLayout { id: leftRow anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left Item { id: rootItem implicitWidth: root.appletRadHeight height: root.appletRadHeight Row { id: workspaceRow anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left leftPadding: 10 Repeater { id: wsRepeater model: I3.workspaces anchors.centerIn: parent property var ws: I3.workspaces Rectangle { id: workspaceNumber property bool isOnMon: { if (!modelData) return false if (!modelData.monitor) return false if (!rootItem.screen) return false; return modelData.monitor.name === rootItem.screen.name; } required property var modelData height: root.appletRadHeight * 1.1 width: root.appletRadHeight * 1.1 radius: root.appletRadHeight color: root.colAppBg Text { anchors.centerIn: parent text: parent.modelData.number color: root.colFg opacity: workspaceNumber.modelData.focused ? 1 : 0.5 font { family: root.fontFamily; pixelSize: root.fontSize; bold: true } } MouseArea { anchors.fill: parent onClicked: {parent.modelData.activate();} } } } } } } /* RowLayout { id: centerRow spacing: root.appletSpacing anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter } */ RowLayout { id: rightRow anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter spacing: root.appletSpacing Item { Process { id: cpuProc command: ["sh", "-c", "head -1 /proc/stat"] stdout: SplitParser { onRead: data => { if (!data) return var p = data.trim().split(/\s+/) var idle = parseInt(p[4]) + parseInt(p[5]) var total = p.slice(1, 8).reduce((a, b) => a + parseInt(b), 0) if (lastCpuTotal > 0) { cpuUsage = Math.round(100 * (1 - (idle - lastCpuIdle) / (total - lastCpuTotal))) } lastCpuTotal = total lastCpuIdle = idle } } Component.onCompleted: running = true } Process { id: ramProc command: ["sh", "-c", "free | grep Mem"] stdout: SplitParser { onRead: data => { if (!data) return var parts = data.trim().split(/\s+/) var used = parseInt(parts[2]) || 0 ramUsage = used /1048576 } } Component.onCompleted: running = true } Timer { interval: 2000 running: true repeat: true onTriggered: { cpuProc.running = true ramProc.running = true } } Item { id: procItem Row { id: procRow spacing: root.appletSpacing anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter leftPadding: 10 Loader { id: statLoader active: true sourceComponent: Rectangle { id: batRect height: root.appletRadHeight width: batText.paintedWidth + root.appletRadHeight radius: root.appletRadHeight color: root.colAppBg function getBatStat() { if (UPower.displayDevice.state == UPowerDeviceState.Discharging) { batTime = Math.floor(batDisMin / 60) + "h " + ((batDisMin - (Math.floor(batDisMin / 60) * 60)).toFixed(0)) + "min" return batTime; } if (UPower.displayDevice.state == UPowerDeviceState.Charging) { batTime = Math.floor(batCharMin / 60) + "h " + ((batCharMin - (Math.floor(batCharMin / 60) * 60)).toFixed(0)) + "min" return batTime; } if (UPower.displayDevice.state == UPowerDeviceState.FullyCharged) { return "Full"; } } function batStatProc() { return Math.round(UPower.displayDevice.percentage * 100) + "%"; } Text { id: batText anchors.centerIn: parent color: root.colFg text: batStatProc() + " - " + getBatStat() font { family: root.fontFamily; pixelSize: root.fontSize; bold: true } } } } Rectangle { height: root.appletRadHeight width: cpuText.paintedWidth + 25 radius: root.appletRadHeight color: root.colAppBg Text { id: cpuText anchors.centerIn: parent text: "CPU: " + cpuUsage + "%" color: root.colFg font { family: root.fontFamily; pixelSize: root.fontSize; bold: true } } } Rectangle { height: root.appletRadHeight width: ramText.paintedWidth + 28 radius: root.appletRadHeight color: root.colAppBg Text { id: ramText anchors.centerIn: parent text: "RAM: " + ramUsage.toFixed(2) + " GiB" color: root.colFg font { family: root.fontFamily; pixelSize: root.fontSize; bold: true } } } } } } Row { id: clockRow Rectangle { height: root.appletRadHeight width: clock.paintedWidth + root.appletRadHeight radius: root.appletRadHeight color: root.colAppBg Text { id: clock anchors.centerIn: parent anchors.right: parent.right text: Qt.formatDateTime(new Date(), "yyyy.MM.dd - HH:mm:ss") color: root.colFg font { family: root.fontFamily; pixelSize: root.fontSize; bold: true } Timer { interval: 1000 running: true repeat: true onTriggered: clock.text = Qt.formatDateTime(new Date(), "yyyy.MM.dd - HH:mm:ss") } } } } Row { id: trayWholeRow rightPadding: 10 Item{ id: miscItem implicitHeight: root.appletRadHeight implicitWidth: trayRect.implicitWidth visible: trayRepeater.count > 0 Rectangle { id: trayRect implicitHeight: root.appletRadHeight implicitWidth: trayRow.implicitWidth + 10 radius: root.appletRadHeight color: root.colAppBg anchors.centerIn: parent Row { id: trayRow spacing: root.appletSpacing anchors.verticalCenter: parent.verticalCenter anchors.centerIn: parent Repeater { id: trayRepeater model: SystemTray.items MouseArea { id: trayMouse property var bar: trayMouse.QsWindow.window required property SystemTrayItem modelData acceptedButtons: Qt.LeftButton | Qt.RightButton implicitWidth: 16 implicitHeight: 16 onClicked: event => { if (event.button === Qt.LeftButton) { modelData.activate(); } else if (modelData.hasMenu) { menu.open(); } } QsMenuAnchor { id: menu menu: trayMouse.modelData.hasMenu ? trayMouse.modelData.menu : null anchor.item: trayMouse } IconImage { id: trayIcon width: parent.implicitWidth height: parent.implicitHeight source: trayMouse.modelData.icon anchors.centerIn: parent } } } } } } } } }