import QtQuick import QtQuick.Controls import QtQuick.Layouts import Quickshell import Quickshell.Bluetooth import Quickshell.Io PanelWindow { id: root visible: controller !== null && controller.connected aboveWindows: false anchors { right: true top: true } margins { top: 16 right: 8 } implicitWidth: expanded ? 300 : 22 implicitHeight: 120 color: "transparent" property bool expanded: false Controller { id: controller connected: false } Process { id: connectionProcess command: ["sh", "-c", "dualsensectl monitor add 'echo connected' remove 'echo disconnected'"] running: true stdout: SplitParser { onRead: line => { if (line === "connected") { controller.connected = true } else { controller.connected = false } } } } Timer { interval: 30000 running: true repeat: true onTriggered: batteryProcess.running = true } Timer { interval: 1000 running: true onTriggered: batteryProcess.running = true } Process { id: batteryProcess command: ["sh", "-c", "dualsensectl battery"] running: true stdout: StdioCollector { onStreamFinished: () => { const s = text.split(" "); controller.percentage = s[0]; controller.chargingState = s[1]; if (s[1].indexOf("charging") === 0) { controller.chargingState = "Charging" } if (s[1].indexOf("discharging") === 0) { controller.chargingState = "Discharging" } if (s[1].indexOf("full") === 0) { controller.chargingState = "Full" } } } } Rectangle { id: surface anchors { right: parent.right top: parent.top bottom: parent.bottom } width: root.expanded ? parent.width : 22 radius: 10 color: '#1e1e2e' border.color: "#555c637a" border.width: 1 clip: true MouseArea { anchors.fill: parent hoverEnabled: true onClicked: (evt) => { root.expanded = !root.expanded } } HoverHandler { id: hoverHandler onHoveredChanged: { if (!hovered) root.expanded = false } } Rectangle { id: edgeHandle width: 5 height: 70 radius: 3 anchors { right: parent.right verticalCenter: parent.verticalCenter rightMargin: 8 } color: '#828282' Rectangle { id: powerLevel anchors { bottom: parent.bottom } width: parent.width height: controller.percentage * 0.01 * parent.height radius: 3 color: controller.chargingState === "Charging" ? "#f5c542" : controller.chargingState === "Full" ? '#68b864' : "#8ab4f8" } } RowLayout { visible: root.expanded anchors { left: parent.left right: edgeHandle.left verticalCenter: parent.verticalCenter leftMargin: 16 rightMargin: 8 } spacing: 10 Image { id: image source: "icons/dualsense.png" Layout.preferredWidth: 100 Layout.preferredHeight: parent.height fillMode: Image.PreserveAspectFit } ColumnLayout { Label { Layout.fillWidth: true text: "Dualsense" color: "white" font.bold: true font.pixelSize: 16 elide: Text.ElideRight } Label { Layout.fillWidth: true text: `Battery: ${Math.round(controller.percentage)} %` color: "#d8dee9" font.pixelSize: 14 } Label { Layout.fillWidth: true text: `Status: ${controller.chargingState}` color: root.chargingState === "charging" ? "#f5c542" : "#d8dee9" font.pixelSize: 14 } } Button { contentItem: Image { source: "icons/power.svg" height: 48 fillMode: Image.PreserveAspectFit horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } background: Rectangle { color: 'transparent' height: 48 border.width: 0 } onClicked: { disconnectProcess.running = true } } Process { id: disconnectProcess command: ["sh", "-c", "dualsensectl power-off"] } } } }