import QtQuick import Quickshell import Quickshell.Io import Quickshell.Services.Mpris import Quickshell.Services.Pipewire import "../services" as S import "../applets" as C Item { id: root width: 280 opacity: 0 property real _slideX: 80 NumberAnimation on opacity { to: 1 duration: 400 easing.type: Easing.OutCubic } NumberAnimation on _slideX { to: 0 duration: 500 easing.type: Easing.OutCubic } transform: Translate { x: root._slideX } implicitHeight: _widgetContent.implicitHeight visible: _mprisCard.visible || _volumeCard.visible || _backlightCard.visible || _notifPills.visible Column { id: _widgetContent width: parent.width spacing: 12 // Notification pills LockNotifPills { id: _notifPills anchors.horizontalCenter: parent.horizontalCenter } // Media widget Rectangle { id: _mprisCard width: parent.width height: _mprisContent.implicitHeight + 16 radius: S.Theme.radius + 2 color: Qt.rgba(S.Theme.base01.r, S.Theme.base01.g, S.Theme.base01.b, 0.7) border.color: Qt.rgba(S.Theme.base03.r, S.Theme.base03.g, S.Theme.base03.b, 0.3) border.width: 1 visible: (S.Modules.lock.mpris ?? true) && _mprisPlayer !== null readonly property var _mprisPlayers: (Mpris.players.values ?? []).filter(p => p.trackTitle || p.playbackState === MprisPlaybackState.Playing || p.playbackState === MprisPlaybackState.Paused) property int _playerIdx: 0 readonly property var _mprisPlayer: _mprisPlayers[_playerIdx] ?? _mprisPlayers[0] ?? null readonly property bool _playing: _mprisPlayer?.playbackState === MprisPlaybackState.Playing C.MprisApplet { id: _mprisContent anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top anchors.topMargin: 8 player: _mprisCard._mprisPlayer players: _mprisCard._mprisPlayers playing: _mprisCard._playing playerIdx: _mprisCard._playerIdx accentColor: S.Theme.base0D cachedArt: _mprisCard._mprisPlayer?.trackArtUrl ?? "" onPlayerSwitched: idx => { _mprisCard._playerIdx = idx; } } } // Volume widget Rectangle { id: _volumeCard width: parent.width height: _volumeContent.implicitHeight + 16 radius: S.Theme.radius + 2 color: Qt.rgba(S.Theme.base01.r, S.Theme.base01.g, S.Theme.base01.b, 0.7) border.color: Qt.rgba(S.Theme.base03.r, S.Theme.base03.g, S.Theme.base03.b, 0.3) border.width: 1 visible: (S.Modules.lock.volume ?? true) && Pipewire.defaultAudioSink !== null PwObjectTracker { objects: [Pipewire.defaultAudioSink] } C.VolumeApplet { id: _volumeContent anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top anchors.topMargin: 8 sink: Pipewire.defaultAudioSink sinkList: [] streamList: [] accentColor: S.Theme.base0E } } // Brightness widget Rectangle { id: _backlightCard width: parent.width height: _backlightContent.implicitHeight + 8 radius: S.Theme.radius + 2 color: Qt.rgba(S.Theme.base01.r, S.Theme.base01.g, S.Theme.base01.b, 0.7) border.color: Qt.rgba(S.Theme.base03.r, S.Theme.base03.g, S.Theme.base03.b, 0.3) border.width: 1 visible: _blDev !== "" property string _blDev: "" property int _percent: 0 Process { running: true command: ["sh", "-c", "ls /sys/class/backlight/ 2>/dev/null | head -1"] stdout: StdioCollector { onStreamFinished: { const dev = text.trim(); if (dev) _backlightCard._blDev = "/sys/class/backlight/" + dev; } } } FileView { id: _blCurrent path: _backlightCard._blDev ? _backlightCard._blDev + "/brightness" : "" watchChanges: true onFileChanged: reload() onLoaded: _backlightCard._updatePercent() } FileView { id: _blMax path: _backlightCard._blDev ? _backlightCard._blDev + "/max_brightness" : "" onLoaded: _backlightCard._updatePercent() } function _updatePercent() { const c = parseInt(_blCurrent.text()); const m = parseInt(_blMax.text()); if (m > 0) _percent = Math.round((c / m) * 100); } Process { id: _blAdj property string cmd: "" command: ["sh", "-c", cmd] onRunningChanged: if (!running && cmd !== "") _blCurrent.reload() } C.BacklightApplet { id: _backlightContent anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top anchors.topMargin: 4 percent: _backlightCard._percent accentColor: S.Theme.base0A onSetPercent: pct => { _blAdj.cmd = "light -S " + Math.round(Math.max(0, Math.min(100, pct))); _blAdj.running = true; } } } } }