background: add visualiser

Also dashboard.visualiserBars -> services.visualiserBars
This commit is contained in:
2 * r + 2 * t 2025-08-25 20:13:55 +10:00
parent fd3b98db60
commit 783057ab0d
10 changed files with 181 additions and 13 deletions

View file

@ -198,7 +198,13 @@ default, you must create it manually.
"desktopClock": {
"enabled": false
},
"enabled": true
"enabled": true,
"visualiser": {
"enabled": true,
"autoHide": true,
"rounding": 1,
"spacing": 1
}
},
"bar": {
"dragThreshold": 20,
@ -277,8 +283,7 @@ default, you must create it manually.
"enabled": true,
"dragThreshold": 50,
"mediaUpdateInterval": 500,
"showOnHover": true,
"visualiserBars": 45
"showOnHover": true
},
"launcher": {
"actionPrefix": ">",
@ -324,7 +329,8 @@ default, you must create it manually.
"weatherLocation": "",
"useFahrenheit": false,
"useTwelveHourClock": false,
"smartScheme": true
"smartScheme": true,
"visualiserBars": 45
},
"session": {
"dragThreshold": 30,

View file

@ -3,8 +3,16 @@ import Quickshell.Io
JsonObject {
property bool enabled: true
property DesktopClock desktopClock: DesktopClock {}
property Visualiser visualiser: Visualiser {}
component DesktopClock: JsonObject {
property bool enabled: false
}
component Visualiser: JsonObject {
property bool enabled: true
property bool autoHide: true
property real rounding: 1
property real spacing: 1
}
}

View file

@ -4,7 +4,6 @@ JsonObject {
property bool enabled: true
property bool showOnHover: true
property int mediaUpdateInterval: 500
property int visualiserBars: 45
property int dragThreshold: 50
property Sizes sizes: Sizes {}

View file

@ -6,6 +6,7 @@ JsonObject {
property bool useFahrenheit: [Locale.ImperialUSSystem, Locale.ImperialSystem].includes(Qt.locale().measurementSystem)
property bool useTwelveHourClock: Qt.locale().timeFormat(Locale.ShortFormat).toLowerCase().includes("a")
property string gpuType: ""
property int visualiserBars: 45
property real audioIncrement: 0.1
property bool smartScheme: true
property string defaultPlayer: "Spotify"

View file

@ -1,4 +1,8 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.components.containers
import qs.services
import qs.config
import Quickshell
import Quickshell.Wayland
@ -27,7 +31,34 @@ Loader {
anchors.left: true
anchors.right: true
Wallpaper {}
Wallpaper {
id: wallpaper
}
Loader {
readonly property bool shouldBeActive: Config.background.visualiser.enabled && (!Config.background.visualiser.autoHide || Hypr.monitorFor(win.modelData).activeWorkspace.toplevels.values.every(t => t.lastIpcObject.floating)) ? 1 : 0
property real offset: shouldBeActive ? 0 : win.modelData.height * 0.2
anchors.fill: parent
anchors.topMargin: offset
anchors.bottomMargin: -offset
opacity: shouldBeActive ? 1 : 0
active: opacity > 0
asynchronous: true
sourceComponent: Visualiser {
screen: win.modelData
wallpaper: wallpaper
}
Behavior on offset {
Anim {}
}
Behavior on opacity {
Anim {}
}
}
Loader {
anchors.right: parent.right

View file

@ -0,0 +1,120 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.components.misc
import qs.services
import qs.config
import Quickshell
import Quickshell.Widgets
import QtQuick
import QtQuick.Effects
Item {
id: root
required property ShellScreen screen
required property Wallpaper wallpaper
Ref {
service: Cava
}
MultiEffect {
anchors.fill: parent
source: root.wallpaper
maskSource: wrapper
maskEnabled: true
blurEnabled: true
blur: 1
blurMax: 32
autoPaddingEnabled: false
}
Item {
id: wrapper
anchors.fill: parent
layer.enabled: true
Item {
id: content
anchors.fill: parent
anchors.margins: Config.border.thickness
anchors.leftMargin: Visibilities.bars.get(root.screen).exclusiveZone + Appearance.spacing.small * Config.background.visualiser.spacing
Side {}
Side {
isRight: true
}
Behavior on anchors.leftMargin {
Anim {}
}
}
}
component Side: Repeater {
id: side
property bool isRight
model: Config.services.visualiserBars
ClippingRectangle {
id: bar
required property int modelData
property real value: Math.max(1, Math.min(100, Cava.values[side.isRight ? modelData : side.count - modelData - 1])) / 100
clip: true
x: modelData * ((content.width * 0.4) / Config.services.visualiserBars) + (side.isRight ? content.width * 0.6 : 0)
implicitWidth: (content.width * 0.4) / Config.services.visualiserBars - Appearance.spacing.small * Config.background.visualiser.spacing
y: content.height - height
implicitHeight: bar.value * content.height * 0.4
color: "transparent"
topLeftRadius: Appearance.rounding.small * Config.background.visualiser.rounding
topRightRadius: Appearance.rounding.small * Config.background.visualiser.rounding
Rectangle {
topLeftRadius: parent.topLeftRadius
topRightRadius: parent.topRightRadius
gradient: Gradient {
orientation: Gradient.Vertical
GradientStop {
position: 0
color: Qt.alpha(Colours.palette.m3primary, 0.7)
Behavior on color {
CAnim {}
}
}
GradientStop {
position: 1
color: Qt.alpha(Colours.palette.m3inversePrimary, 0.7)
Behavior on color {
CAnim {}
}
}
}
anchors.left: parent.left
anchors.right: parent.right
y: parent.height - height
implicitHeight: content.height * 0.4
}
Behavior on value {
Anim {
duration: Appearance.anim.durations.small
}
}
}
}
}

View file

@ -79,7 +79,7 @@ Item {
id: visualiserBars
model: Array.from({
length: Config.dashboard.visualiserBars
length: Config.services.visualiserBars
}, (_, i) => i)
ShapePath {
@ -88,13 +88,13 @@ Item {
required property int modelData
readonly property int value: Math.max(1, Math.min(100, Cava.values[modelData]))
readonly property real angle: modelData * 2 * Math.PI / Config.dashboard.visualiserBars
readonly property real angle: modelData * 2 * Math.PI / Config.services.visualiserBars
readonly property real magnitude: value / 100 * Config.dashboard.sizes.mediaVisualiserSize
readonly property real cos: Math.cos(angle)
readonly property real sin: Math.sin(angle)
capStyle: Appearance.rounding.scale === 0 ? ShapePath.SquareCap : ShapePath.RoundCap
strokeWidth: 360 / Config.dashboard.visualiserBars - Appearance.spacing.small / 4
strokeWidth: 360 / Config.services.visualiserBars - Appearance.spacing.small / 4
strokeColor: Colours.palette.m3primary
startX: visualiser.centerX + (visualiser.innerX + strokeWidth / 2) * cos

View file

@ -139,6 +139,8 @@ Variants {
screen: scope.modelData
visibilities: visibilities
popouts: panels.popouts
Component.onCompleted: Visibilities.bars.set(scope.modelData, this)
}
}
}

View file

@ -8,14 +8,14 @@ import QtQuick
Singleton {
id: root
property list<int> values: Array(Config.dashboard.visualiserBars)
property list<int> values: Array(Config.services.visualiserBars)
property int refCount
Connections {
target: Config.dashboard
target: Config.services
function onVisualiserBarsChanged() {
root.values = Array(Config.dashboard.visualiserBars);
root.values = Array(Config.services.visualiserBars);
cavaProc.running = false;
cavaProc.running = true;
}
@ -25,7 +25,7 @@ Singleton {
id: cavaProc
running: true
command: ["sh", "-c", `printf '[general]\nframerate=60\nbars=${Config.dashboard.visualiserBars}\nsleep_timer=3\n[output]\nchannels=mono\nmethod=raw\nraw_target=/dev/stdout\ndata_format=ascii\nascii_max_range=100\n[smoothing]\nnoise_reduction=85\nmonstercat=1\ngravity=120\n[eq]\n1=0.8\n2=0.9\n3=1\n4=1.1\n5=1.2' | cava -p /dev/stdin`]
command: ["sh", "-c", `printf '[general]\nframerate=60\nbars=${Config.services.visualiserBars}\nsleep_timer=3\n[output]\nchannels=mono\nmethod=raw\nraw_target=/dev/stdout\ndata_format=ascii\nascii_max_range=100\n[smoothing]\nnoise_reduction=85\nmonstercat=1\ngravity=120\n[eq]\n1=0.8\n2=0.9\n3=1\n4=1.1\n5=1.2' | cava -p /dev/stdin`]
stdout: SplitParser {
onRead: data => {
if (root.refCount)

View file

@ -4,6 +4,7 @@ import Quickshell
Singleton {
property var screens: new Map()
property var bars: new Map()
function load(screen: ShellScreen, visibilities: var): void {
screens.set(Hypr.monitorFor(screen), visibilities);