feat: added widgets capture, music visualiser, bluetooth, notes
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
import QtQuick
|
||||
import Quickshell.Services.Mpris
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: widget
|
||||
|
||||
readonly property var activePlayer: MprisController.activePlayer
|
||||
readonly property bool isPlaying: activePlayer !== null && activePlayer !== undefined && activePlayer.playbackState === MprisPlaybackState.Playing
|
||||
// Follow whatever is actually coming out of this machine - games do not
|
||||
// publish MPRIS, and a remote MPRIS player produces no local audio at all.
|
||||
readonly property bool live: visible
|
||||
readonly property bool hasSignal: {
|
||||
const values = CavaService.values;
|
||||
if (!values || values.length < 6)
|
||||
return false;
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
if (values[i] > 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cava reports six bands; mirroring them reads as a classic visualiser.
|
||||
// CavaService.values is a list<int>, so follow its change signal rather than
|
||||
// binding to it (this is how the shell's own visualiser reads it).
|
||||
property var bands: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
|
||||
function refreshBands() {
|
||||
const values = CavaService.values;
|
||||
if (!values || values.length < 6) {
|
||||
widget.bands = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
return;
|
||||
}
|
||||
|
||||
const left = [values[5], values[4], values[3], values[2], values[1], values[0]];
|
||||
widget.bands = left.concat([values[0], values[1], values[2], values[3], values[4], values[5]]);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: CavaService
|
||||
enabled: widget.live
|
||||
|
||||
function onValuesChanged() {
|
||||
widget.refreshBands();
|
||||
}
|
||||
}
|
||||
|
||||
onLiveChanged: {
|
||||
if (!widget.live)
|
||||
widget.bands = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
else
|
||||
widget.refreshBands();
|
||||
}
|
||||
|
||||
// Holding a Ref keeps the cava process alive only while this widget is up.
|
||||
Loader {
|
||||
active: widget.live
|
||||
|
||||
sourceComponent: Component {
|
||||
Ref {
|
||||
service: CavaService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: barRow
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
width: parent.width
|
||||
height: parent.height - trackLabel.height - Theme.spacingS
|
||||
spacing: Math.max(2, width * 0.012)
|
||||
|
||||
readonly property real barWidth: (width - spacing * 11) / 12
|
||||
|
||||
Repeater {
|
||||
model: widget.bands
|
||||
|
||||
// A positioner manages x, so the growing bar lives inside a
|
||||
// full-height cell rather than anchoring to the Row itself.
|
||||
delegate: Item {
|
||||
required property var modelData
|
||||
|
||||
readonly property real level: {
|
||||
if (!widget.live)
|
||||
return 0;
|
||||
|
||||
// Square-root curve, as the shell's own visualiser uses -
|
||||
// linear levels look flat for anything but loud passages.
|
||||
const raw = Math.max(0, Math.min(100, modelData || 0));
|
||||
return Math.sqrt(raw * 0.01);
|
||||
}
|
||||
|
||||
width: barRow.barWidth
|
||||
height: barRow.height
|
||||
|
||||
Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
height: Math.max(3, parent.height * parent.level)
|
||||
radius: width / 2
|
||||
color: Theme.primary
|
||||
opacity: 0.55 + parent.level * 0.45
|
||||
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: 90
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: trackLabel
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: {
|
||||
if (!CavaService.cavaAvailable)
|
||||
return "cava is not installed";
|
||||
if (widget.isPlaying) {
|
||||
const title = widget.activePlayer.trackTitle || "";
|
||||
const artist = widget.activePlayer.trackArtist || "";
|
||||
return artist !== "" ? title + " — " + artist : title;
|
||||
}
|
||||
|
||||
return widget.hasSignal ? "Playing" : "No audio playing";
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user