feat: audio osd
This commit is contained in:
parent
83020b8b1f
commit
fb471f1023
13 changed files with 427 additions and 7 deletions
14
config/OsdConfig.qml
Normal file
14
config/OsdConfig.qml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Singleton {
|
||||
readonly property int hideDelay: 1500
|
||||
readonly property Sizes sizes: Sizes {}
|
||||
|
||||
component Sizes: QtObject {
|
||||
readonly property int sliderWidth: 30
|
||||
readonly property int sliderHeight: 150
|
||||
}
|
||||
}
|
||||
|
|
@ -11,8 +11,6 @@ Shape {
|
|||
readonly property int roundingY: Math.min(rounding, realWrapperHeight / 2)
|
||||
readonly property real wrapperHeight: realWrapperHeight - 1 // Pixel issues :sob:
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
opacity: Appearance.transparency.enabled ? Appearance.transparency.base : 1
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ Item {
|
|||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: search.top
|
||||
anchors.bottomMargin: root.spacing
|
||||
anchors.margins: root.padding
|
||||
|
||||
ContentList {
|
||||
id: list
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ Scope {
|
|||
id: bg
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
wrapperWidth: wrapper.width
|
||||
realWrapperHeight: Math.min(wrapper.height, content.height)
|
||||
|
|
@ -43,6 +44,7 @@ Scope {
|
|||
id: wrapper
|
||||
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
implicitWidth: content.width + bg.rounding * 2
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ Item {
|
|||
required property real contentHeight
|
||||
property bool shouldBeVisible
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
height: 0
|
||||
clip: true
|
||||
|
||||
|
|
|
|||
61
modules/osd/Background.qml
Normal file
61
modules/osd/Background.qml
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import "root:/config"
|
||||
import QtQuick
|
||||
import QtQuick.Shapes
|
||||
|
||||
Shape {
|
||||
id: root
|
||||
|
||||
required property real realWrapperWidth
|
||||
required property real wrapperHeight
|
||||
readonly property int rounding: Appearance.rounding.large
|
||||
readonly property int roundingX: Math.min(rounding, realWrapperWidth / 2)
|
||||
readonly property real wrapperWidth: realWrapperWidth - 1 // Pixel issues :sob:
|
||||
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
opacity: Appearance.transparency.enabled ? Appearance.transparency.base : 1
|
||||
|
||||
ShapePath {
|
||||
strokeWidth: -1
|
||||
fillColor: Appearance.colours.m3surfaceContainer
|
||||
|
||||
startX: root.wrapperWidth
|
||||
|
||||
PathArc {
|
||||
relativeX: -root.roundingX
|
||||
relativeY: root.rounding
|
||||
radiusX: root.roundingX
|
||||
radiusY: root.rounding
|
||||
}
|
||||
PathLine {
|
||||
x: root.roundingX
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: -root.roundingX
|
||||
relativeY: root.rounding
|
||||
radiusX: root.roundingX
|
||||
radiusY: root.rounding
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
y: root.wrapperHeight - root.rounding * 2
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.roundingX
|
||||
relativeY: root.rounding
|
||||
radiusX: root.roundingX
|
||||
radiusY: root.rounding
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
x: root.wrapperWidth - root.roundingX
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.roundingX
|
||||
relativeY: root.rounding
|
||||
radiusX: root.roundingX
|
||||
radiusY: root.rounding
|
||||
}
|
||||
}
|
||||
}
|
||||
42
modules/osd/Content.qml
Normal file
42
modules/osd/Content.qml
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
|
||||
padding: Appearance.padding.large
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
VerticalSlider {
|
||||
icon: {
|
||||
if (Audio.muted)
|
||||
return "no_sound";
|
||||
if (value >= 0.5)
|
||||
return "volume_up";
|
||||
if (value > 0)
|
||||
return "volume_down";
|
||||
return "volume_mute";
|
||||
}
|
||||
value: Audio.volume
|
||||
onMoved: Audio.setVolume(value)
|
||||
|
||||
implicitWidth: OsdConfig.sizes.sliderWidth
|
||||
implicitHeight: OsdConfig.sizes.sliderHeight
|
||||
}
|
||||
|
||||
VerticalSlider {
|
||||
icon: "brightness_6"
|
||||
|
||||
implicitWidth: OsdConfig.sizes.sliderWidth
|
||||
implicitHeight: OsdConfig.sizes.sliderHeight
|
||||
}
|
||||
}
|
||||
89
modules/osd/Osd.qml
Normal file
89
modules/osd/Osd.qml
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
import "root:/widgets"
|
||||
import "root:/services"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
property bool osdVisible
|
||||
|
||||
function show(): void {
|
||||
root.osdVisible = true;
|
||||
timer.restart();
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: Audio
|
||||
|
||||
function onMutedChanged(): void {
|
||||
root.show();
|
||||
}
|
||||
|
||||
function onVolumeChanged(): void {
|
||||
root.show();
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: timer
|
||||
|
||||
interval: OsdConfig.hideDelay
|
||||
onTriggered: root.osdVisible = false
|
||||
}
|
||||
|
||||
Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
LazyLoader {
|
||||
loading: true
|
||||
|
||||
required property ShellScreen modelData
|
||||
|
||||
StyledWindow {
|
||||
id: win
|
||||
|
||||
screen: parent.modelData
|
||||
name: "osd"
|
||||
visible: wrapper.shouldBeVisible
|
||||
|
||||
mask: Region {
|
||||
item: wrapper
|
||||
}
|
||||
|
||||
anchors.left: true
|
||||
anchors.right: true
|
||||
height: wrapper.height
|
||||
|
||||
Background {
|
||||
id: bg
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
|
||||
wrapperHeight: wrapper.height
|
||||
realWrapperWidth: Math.min(wrapper.width, content.width)
|
||||
}
|
||||
|
||||
Wrapper {
|
||||
id: wrapper
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
|
||||
implicitHeight: content.height + bg.rounding * 2
|
||||
|
||||
osdVisible: root.osdVisible
|
||||
contentWidth: content.width
|
||||
|
||||
Content {
|
||||
id: content
|
||||
|
||||
screen: parent.modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
modules/osd/Wrapper.qml
Normal file
62
modules/osd/Wrapper.qml
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import "root:/config"
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property bool osdVisible
|
||||
required property real contentWidth
|
||||
property bool shouldBeVisible
|
||||
|
||||
width: 0
|
||||
clip: true
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.osdVisible
|
||||
|
||||
PropertyChanges {
|
||||
root.width: contentWidth
|
||||
root.shouldBeVisible: true
|
||||
}
|
||||
}
|
||||
|
||||
transitions: [
|
||||
Transition {
|
||||
from: ""
|
||||
to: "visible"
|
||||
|
||||
SequentialAnimation {
|
||||
PropertyAction {
|
||||
target: root
|
||||
property: "shouldBeVisible"
|
||||
}
|
||||
NumberAnimation {
|
||||
target: root
|
||||
property: "width"
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
|
||||
}
|
||||
}
|
||||
},
|
||||
Transition {
|
||||
from: "visible"
|
||||
to: ""
|
||||
|
||||
SequentialAnimation {
|
||||
NumberAnimation {
|
||||
target: root
|
||||
property: "width"
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.emphasizedAccel
|
||||
}
|
||||
PropertyAction {
|
||||
target: root
|
||||
property: "shouldBeVisible"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
pragma Singleton
|
||||
|
||||
import "root:/utils/scripts/fuzzysort.js" as Fuzzy
|
||||
import "root:/widgets"
|
||||
import "root:/config"
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
|
||||
|
|
|
|||
25
services/Audio.qml
Normal file
25
services/Audio.qml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
readonly property PwNode sink: Pipewire.defaultAudioSink
|
||||
readonly property PwNode source: Pipewire.defaultAudioSource
|
||||
|
||||
readonly property bool muted: sink?.audio?.muted ?? false
|
||||
readonly property real volume: sink?.audio?.volume ?? 0
|
||||
|
||||
function setVolume(volume: real): void {
|
||||
if (sink?.ready && sink?.audio) {
|
||||
sink.audio.muted = false;
|
||||
sink.audio.volume = volume;
|
||||
}
|
||||
}
|
||||
|
||||
PwObjectTracker {
|
||||
objects: [Pipewire.defaultAudioSink, Pipewire.defaultAudioSource]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
import "modules/bar"
|
||||
import "modules/launcher"
|
||||
import "modules/osd"
|
||||
import Quickshell
|
||||
|
||||
ShellRoot {
|
||||
Bar {}
|
||||
Launcher {}
|
||||
Osd {}
|
||||
}
|
||||
|
|
|
|||
130
widgets/VerticalSlider.qml
Normal file
130
widgets/VerticalSlider.qml
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import "root:/widgets"
|
||||
import "root:/config"
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
|
||||
Slider {
|
||||
id: root
|
||||
|
||||
required property string icon
|
||||
|
||||
orientation: Qt.Vertical
|
||||
|
||||
background: StyledRect {
|
||||
color: Appearance.alpha(Appearance.colours.m3surfaceContainerHigh, true)
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
StyledRect {
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
y: root.handle.y
|
||||
implicitHeight: parent.height - y
|
||||
|
||||
color: Appearance.alpha(Appearance.colours.m3secondary, true)
|
||||
radius: Appearance.rounding.full
|
||||
}
|
||||
}
|
||||
|
||||
handle: Item {
|
||||
id: handle
|
||||
|
||||
property bool moving
|
||||
|
||||
y: root.visualPosition * (root.availableHeight - height)
|
||||
implicitWidth: root.width
|
||||
implicitHeight: root.width
|
||||
|
||||
RectangularShadow {
|
||||
anchors.fill: parent
|
||||
radius: rect.radius
|
||||
color: Appearance.colours.m3shadow
|
||||
blur: 5
|
||||
spread: 0
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
id: rect
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
color: Appearance.alpha(Appearance.colours.m3inverseSurface, true)
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
MaterialIcon {
|
||||
id: icon
|
||||
|
||||
animate: true
|
||||
text: root.icon
|
||||
color: Appearance.colours.m3onPrimary
|
||||
anchors.centerIn: parent
|
||||
|
||||
states: State {
|
||||
name: "value"
|
||||
when: handle.moving
|
||||
|
||||
PropertyChanges {
|
||||
icon.animate: false
|
||||
icon.text: Math.round(root.value * 100)
|
||||
icon.font.pointSize: Appearance.font.size.small
|
||||
icon.font.family: Appearance.font.family.sans
|
||||
}
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
from: "*"
|
||||
to: "*"
|
||||
|
||||
NumberAnimation {
|
||||
target: icon
|
||||
property: "scale"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standardAccel
|
||||
}
|
||||
PropertyAction {
|
||||
target: icon
|
||||
properties: "animate,text,font.pointSize,font.family"
|
||||
}
|
||||
NumberAnimation {
|
||||
target: icon
|
||||
property: "scale"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: Appearance.anim.durations.normal
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standardDecel
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPressedChanged: handle.moving = pressed
|
||||
|
||||
onValueChanged: {
|
||||
handle.moving = true;
|
||||
stateChangeDelay.restart();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: stateChangeDelay
|
||||
|
||||
interval: 500
|
||||
onTriggered: {
|
||||
if (!root.pressed)
|
||||
handle.moving = false;
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on value {
|
||||
NumberAnimation {
|
||||
duration: Appearance.anim.durations.large
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.standard
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue