// Now-playing readout. Click toggles play/pause, scroll skips tracks. // // "Nothing playing" means no MPRIS player is exposing a track at all — a // paused player still shows, because otherwise there would be nothing left on // the bar to press play on. import QtQuick import Quickshell.Services.Mpris import qs.config import qs.services import qs.widgets Pill { id: root // Prefer whatever is actually playing; fall back to the first player that // has a track loaded so a paused session stays reachable. The model's // insertion behavior is covered by the live-player verification. readonly property MprisPlayer player: { const players = Mpris.players ? Mpris.players.values : []; return players.find(p => p.isPlaying) ?? players.find(p => p.trackTitle) ?? null; } readonly property string label: { if (!root.player) return ""; const title = root.player.trackTitle ?? ""; const artist = root.player.trackArtist ?? ""; return artist ? artist + " — " + title : title; } visible: root.player !== null horizontalPadding: 8 onActivated: if (root.player?.canTogglePlaying) root.player.togglePlaying() // Right-click opens the settings that govern this widget. Output device and per-application volume. onSecondaryActivated: ShellState.openSettings("sound") // Scroll up = previous, down = next — the same direction as the workspace // switcher, so the whole bar scrolls consistently. onScrolled: delta => { if (!root.player) return; if (delta > 0 && root.player.canGoPrevious) root.player.previous(); else if (delta < 0 && root.player.canGoNext) root.player.next(); } Text { anchors.verticalCenter: parent.verticalCenter // Shows the action the click will perform, not the current state. text: root.player?.isPlaying ? "\u{F04C}" : "\u{F04B}" // fa-pause / fa-play font.family: Theme.fontMono font.pixelSize: Theme.fontSizeSmall color: Theme.accent } Text { anchors.verticalCenter: parent.verticalCenter text: root.label font.family: Theme.fontFamily font.pixelSize: Theme.fontSize color: Theme.fg // Titles are unbounded; the bar is not. Elide rather than let one // podcast episode push the tray off the edge. elide: Text.ElideRight width: Math.min(implicitWidth, 200) } }