70 lines
2.3 KiB
QML
70 lines
2.3 KiB
QML
// 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.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()
|
|
|
|
// 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)
|
|
}
|
|
}
|