summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--components/controls/FilledSlider.qml (renamed from components/controls/VerticalSlider.qml)2
-rw-r--r--components/controls/StyledRadioButton.qml58
-rw-r--r--components/controls/StyledSlider.qml59
-rw-r--r--modules/bar/components/StatusIcons.qml2
-rw-r--r--modules/bar/popouts/Audio.qml145
-rw-r--r--modules/dashboard/Media.qml54
-rw-r--r--modules/lock/MediaPlaying.qml53
-rw-r--r--modules/osd/Content.qml15
-rw-r--r--services/Audio.qml27
-rw-r--r--utils/Icons.qml10
10 files changed, 274 insertions, 151 deletions
diff --git a/components/controls/VerticalSlider.qml b/components/controls/FilledSlider.qml
index 306cc24..8d0e547 100644
--- a/components/controls/VerticalSlider.qml
+++ b/components/controls/FilledSlider.qml
@@ -25,7 +25,7 @@ Slider {
implicitHeight: parent.height - y
color: Colours.alpha(Colours.palette.m3secondary, true)
- radius: Appearance.rounding.full
+ radius: parent.radius
}
}
diff --git a/components/controls/StyledRadioButton.qml b/components/controls/StyledRadioButton.qml
new file mode 100644
index 0000000..73fc836
--- /dev/null
+++ b/components/controls/StyledRadioButton.qml
@@ -0,0 +1,58 @@
+import qs.components
+import qs.services
+import qs.config
+import QtQuick
+import QtQuick.Controls
+
+RadioButton {
+ id: root
+
+ font.pointSize: Appearance.font.size.smaller
+
+ indicator: Rectangle {
+ id: outerCircle
+
+ implicitWidth: 20
+ implicitHeight: 20
+ radius: Appearance.rounding.full
+ color: "transparent"
+ border.color: root.checked ? Colours.palette.m3primary : Colours.palette.m3onSurfaceVariant
+ border.width: 2
+ anchors.verticalCenter: parent.verticalCenter
+
+ StateLayer {
+ anchors.margins: -Appearance.padding.smaller
+ color: root.checked ? Colours.palette.m3onSurface : Colours.palette.m3primary
+ z: -1
+
+ function onClicked(): void {
+ root.click();
+ }
+ }
+
+ StyledRect {
+ anchors.centerIn: parent
+ implicitWidth: 8
+ implicitHeight: 8
+
+ radius: Appearance.rounding.full
+ color: root.checked ? Colours.palette.m3primary : "transparent"
+ }
+
+ Behavior on border.color {
+ ColorAnimation {
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
+ }
+ }
+
+ contentItem: StyledText {
+ text: root.text
+ font.pointSize: root.font.pointSize
+ anchors.verticalCenter: parent.verticalCenter
+ anchors.left: outerCircle.right
+ anchors.leftMargin: Appearance.spacing.smaller
+ }
+}
diff --git a/components/controls/StyledSlider.qml b/components/controls/StyledSlider.qml
new file mode 100644
index 0000000..0352cef
--- /dev/null
+++ b/components/controls/StyledSlider.qml
@@ -0,0 +1,59 @@
+import qs.components
+import qs.config
+import qs.services
+import QtQuick.Controls
+import QtQuick
+
+Slider {
+ id: slider
+
+ background: Item {
+ StyledRect {
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ anchors.topMargin: slider.implicitHeight / 3
+ anchors.bottomMargin: slider.implicitHeight / 3
+
+ implicitWidth: slider.handle.x - slider.implicitHeight / 6
+
+ color: Colours.palette.m3primary
+ radius: Appearance.rounding.full
+ topRightRadius: slider.implicitHeight / 15
+ bottomRightRadius: slider.implicitHeight / 15
+ }
+
+ StyledRect {
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ anchors.topMargin: slider.implicitHeight / 3
+ anchors.bottomMargin: slider.implicitHeight / 3
+
+ implicitWidth: parent.width - slider.handle.x - slider.handle.implicitWidth - slider.implicitHeight / 6
+
+ color: Colours.palette.m3surfaceContainer
+ radius: Appearance.rounding.full
+ topLeftRadius: slider.implicitHeight / 15
+ bottomLeftRadius: slider.implicitHeight / 15
+ }
+ }
+
+ handle: StyledRect {
+ id: rect
+
+ x: slider.visualPosition * slider.availableWidth
+
+ implicitWidth: slider.implicitHeight / 4.5
+ implicitHeight: slider.implicitHeight
+
+ color: Colours.palette.m3primary
+ radius: Appearance.rounding.full
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: Qt.PointingHandCursor
+ onPressed: event => event.accepted = false
+ }
+ }
+}
diff --git a/modules/bar/components/StatusIcons.qml b/modules/bar/components/StatusIcons.qml
index ad8c35e..c364d6b 100644
--- a/modules/bar/components/StatusIcons.qml
+++ b/modules/bar/components/StatusIcons.qml
@@ -58,7 +58,7 @@ Item {
sourceComponent: MaterialIcon {
animate: true
- text: Audio.muted ? "volume_off" : Audio.volume >= 0.66 ? "volume_up" : Audio.volume >= 0.33 ? "volume_down" : "volume_mute"
+ text: Icons.getVolumeIcon(Audio.volume, Audio.muted)
color: root.colour
}
}
diff --git a/modules/bar/popouts/Audio.qml b/modules/bar/popouts/Audio.qml
index e6e5514..84378c9 100644
--- a/modules/bar/popouts/Audio.qml
+++ b/modules/bar/popouts/Audio.qml
@@ -1,58 +1,139 @@
+pragma ComponentBehavior: Bound
+
import qs.components
import qs.components.controls
import qs.services
import qs.config
-import QtQuick.Layouts
import Quickshell
+import Quickshell.Services.Pipewire
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
-ColumnLayout {
+Item {
id: root
required property var wrapper
- spacing: Appearance.spacing.normal
+ implicitWidth: layout.implicitWidth + Appearance.padding.normal * 2
+ implicitHeight: layout.implicitHeight + Appearance.padding.normal * 2
+
+ ButtonGroup {
+ id: sinks
+ }
+
+ ButtonGroup {
+ id: sources
+ }
+
+ ColumnLayout {
+ id: layout
- VerticalSlider {
- id: volumeSlider
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ spacing: 0
- icon: {
- if (Audio.muted)
- return "no_sound";
- if (value >= 0.5)
- return "volume_up";
- if (value > 0)
- return "volume_down";
- return "volume_mute";
+ StyledText {
+ Layout.bottomMargin: Appearance.spacing.small / 2
+ text: qsTr("Output")
+ font.weight: 500
}
- value: Audio.volume
- onMoved: Audio.setVolume(value)
+ Repeater {
+ model: Audio.sinks
- implicitWidth: Config.osd.sizes.sliderWidth
- implicitHeight: Config.osd.sizes.sliderHeight
- }
+ StyledRadioButton {
+ id: control
+
+ required property PwNode modelData
+
+ ButtonGroup.group: sinks
+ checked: Audio.sink?.id === modelData.id
+ onClicked: Audio.setAudioSink(modelData)
+ text: modelData.description
+ }
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.small
+ Layout.bottomMargin: Appearance.spacing.small / 2
+ text: qsTr("Input")
+ font.weight: 500
+ }
- StyledRect {
- id: pavuButton
+ Repeater {
+ model: Audio.sources
- implicitWidth: implicitHeight
- implicitHeight: icon.implicitHeight + Appearance.padding.small * 2
+ StyledRadioButton {
+ required property PwNode modelData
- radius: Appearance.rounding.normal
- color: Colours.palette.m3surfaceContainer
+ ButtonGroup.group: sources
+ checked: Audio.source?.id === modelData.id
+ onClicked: Audio.setAudioSource(modelData)
+ text: modelData.description
+ }
+ }
+
+ StyledText {
+ Layout.topMargin: Appearance.spacing.small
+ Layout.bottomMargin: Appearance.spacing.small / 2
+ text: qsTr("Volume")
+ font.weight: 500
+ }
- StateLayer {
- function onClicked(): void {
- root.wrapper.hasCurrent = false;
- Quickshell.execDetached(["app2unit", "--", ...Config.general.apps.audio]);
+ StyledSlider {
+ Layout.fillWidth: true
+ implicitHeight: Appearance.padding.normal * 3
+
+ value: Audio.volume
+ onMoved: Audio.setVolume(value)
+
+ Behavior on value {
+ NumberAnimation {
+ duration: Appearance.anim.durations.normal
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: Appearance.anim.curves.standard
+ }
}
}
- MaterialIcon {
- id: icon
+ StyledRect {
+ Layout.topMargin: Appearance.spacing.normal
+ visible: Config.general.apps.audio.length > 0
+
+ implicitWidth: expandBtn.implicitWidth + Appearance.padding.normal * 2
+ implicitHeight: expandBtn.implicitHeight + Appearance.padding.small
+
+ radius: Appearance.rounding.normal
+ color: Colours.palette.m3primaryContainer
- anchors.centerIn: parent
- text: "settings"
+ StateLayer {
+ color: Colours.palette.m3onPrimaryContainer
+
+ function onClicked(): void {
+ root.wrapper.hasCurrent = false;
+ Quickshell.execDetached(["app2unit", "--", ...Config.general.apps.audio]);
+ }
+ }
+
+ RowLayout {
+ id: expandBtn
+
+ anchors.centerIn: parent
+ spacing: Appearance.spacing.small
+
+ StyledText {
+ Layout.leftMargin: Appearance.padding.smaller
+ text: qsTr("Open settings")
+ color: Colours.palette.m3onPrimaryContainer
+ }
+
+ MaterialIcon {
+ text: "chevron_right"
+ color: Colours.palette.m3onPrimaryContainer
+ font.pointSize: Appearance.font.size.large
+ }
+ }
}
}
}
diff --git a/modules/dashboard/Media.qml b/modules/dashboard/Media.qml
index c12db22..edb09af 100644
--- a/modules/dashboard/Media.qml
+++ b/modules/dashboard/Media.qml
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
import qs.components
import qs.components.effects
import qs.components.misc
+import qs.components.controls
import qs.services
import qs.utils
import qs.config
@@ -279,9 +280,10 @@ Item {
}
}
- Slider {
+ StyledSlider {
id: slider
+ enabled: !!Players.active
implicitWidth: controls.implicitWidth * 1.5
implicitHeight: Appearance.padding.normal * 3
@@ -291,56 +293,6 @@ Item {
if (active?.canSeek && active?.positionSupported)
active.position = value * active.length;
}
-
- background: Item {
- StyledRect {
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.topMargin: slider.implicitHeight / 3
- anchors.bottomMargin: slider.implicitHeight / 3
-
- implicitWidth: slider.handle.x - slider.implicitHeight / 6
-
- color: Colours.palette.m3primary
- radius: Appearance.rounding.full
- topRightRadius: slider.implicitHeight / 15
- bottomRightRadius: slider.implicitHeight / 15
- }
-
- StyledRect {
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- anchors.right: parent.right
- anchors.topMargin: slider.implicitHeight / 3
- anchors.bottomMargin: slider.implicitHeight / 3
-
- implicitWidth: parent.width - slider.handle.x - slider.handle.implicitWidth - slider.implicitHeight / 6
-
- color: Colours.palette.m3surfaceContainer
- radius: Appearance.rounding.full
- topLeftRadius: slider.implicitHeight / 15
- bottomLeftRadius: slider.implicitHeight / 15
- }
- }
-
- handle: StyledRect {
- id: rect
-
- x: slider.visualPosition * slider.availableWidth
-
- implicitWidth: slider.implicitHeight / 4.5
- implicitHeight: slider.implicitHeight
-
- color: Colours.palette.m3primary
- radius: Appearance.rounding.full
-
- MouseArea {
- anchors.fill: parent
- cursorShape: Qt.PointingHandCursor
- onPressed: event => event.accepted = false
- }
- }
}
Item {
diff --git a/modules/lock/MediaPlaying.qml b/modules/lock/MediaPlaying.qml
index 08f1490..2ebd177 100644
--- a/modules/lock/MediaPlaying.qml
+++ b/modules/lock/MediaPlaying.qml
@@ -1,4 +1,5 @@
import qs.components
+import qs.components.controls
import qs.services
import qs.config
import Quickshell.Widgets
@@ -164,7 +165,7 @@ RowLayout {
spacing: Appearance.spacing.small
- Slider {
+ StyledSlider {
id: slider
Layout.rightMargin: root.isLarge ? Appearance.spacing.small : 0
@@ -177,56 +178,6 @@ RowLayout {
if (active?.canSeek && active?.positionSupported)
active.position = value * active.length;
}
-
- background: Item {
- StyledRect {
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- anchors.left: parent.left
- anchors.topMargin: slider.implicitHeight / 3
- anchors.bottomMargin: slider.implicitHeight / 3
-
- implicitWidth: slider.handle.x - slider.implicitHeight / 6
-
- color: Colours.palette.m3primary
- radius: Appearance.rounding.full
- topRightRadius: slider.implicitHeight / 15
- bottomRightRadius: slider.implicitHeight / 15
- }
-
- StyledRect {
- anchors.top: parent.top
- anchors.bottom: parent.bottom
- anchors.right: parent.right
- anchors.topMargin: slider.implicitHeight / 3
- anchors.bottomMargin: slider.implicitHeight / 3
-
- implicitWidth: parent.width - slider.handle.x - slider.handle.implicitWidth - slider.implicitHeight / 6
-
- color: Colours.palette.m3surfaceContainer
- radius: Appearance.rounding.full
- topLeftRadius: slider.implicitHeight / 15
- bottomLeftRadius: slider.implicitHeight / 15
- }
- }
-
- handle: StyledRect {
- id: rect
-
- x: slider.visualPosition * slider.availableWidth
-
- implicitWidth: slider.implicitHeight / 4.5
- implicitHeight: slider.implicitHeight
-
- color: Colours.palette.m3primary
- radius: Appearance.rounding.full
-
- MouseArea {
- anchors.fill: parent
- cursorShape: Qt.PointingHandCursor
- onPressed: event => event.accepted = false
- }
- }
}
Control {
diff --git a/modules/osd/Content.qml b/modules/osd/Content.qml
index 3177ee9..29b1b39 100644
--- a/modules/osd/Content.qml
+++ b/modules/osd/Content.qml
@@ -1,6 +1,7 @@
import qs.components.controls
import qs.services
import qs.config
+import qs.utils
import QtQuick
Column {
@@ -26,18 +27,10 @@ Column {
Audio.setVolume(Audio.volume - 0.1);
}
- VerticalSlider {
+ FilledSlider {
anchors.fill: parent
- icon: {
- if (Audio.muted)
- return "no_sound";
- if (value >= 0.5)
- return "volume_up";
- if (value > 0)
- return "volume_down";
- return "volume_mute";
- }
+ icon: Icons.getVolumeIcon(value, Audio.muted)
value: Audio.volume
onMoved: Audio.setVolume(value)
}
@@ -57,7 +50,7 @@ Column {
monitor.setBrightness(monitor.brightness - 0.1);
}
- VerticalSlider {
+ FilledSlider {
anchors.fill: parent
icon: `brightness_${(Math.round(value * 6) + 1)}`
diff --git a/services/Audio.qml b/services/Audio.qml
index c91adbc..54cea3b 100644
--- a/services/Audio.qml
+++ b/services/Audio.qml
@@ -6,20 +6,39 @@ import Quickshell.Services.Pipewire
Singleton {
id: root
+ readonly property var nodes: Pipewire.nodes.values.reduce((acc, node) => {
+ if (!node.isStream) {
+ if (node.isSink) acc.sinks.push(node)
+ else if (node.audio) acc.sources.push(node)
+ }
+ return acc
+ }, { sources: [], sinks: [] })
+
+ readonly property list<PwNode> sinks: nodes.sinks
+ readonly property list<PwNode> sources: nodes.sources
+
readonly property PwNode sink: Pipewire.defaultAudioSink
readonly property PwNode source: Pipewire.defaultAudioSource
- readonly property bool muted: sink?.audio?.muted ?? false
+ readonly property bool muted: !!sink?.audio?.muted
readonly property real volume: sink?.audio?.volume ?? 0
- function setVolume(volume: real): void {
+ function setVolume(newVolume: real): void {
if (sink?.ready && sink?.audio) {
sink.audio.muted = false;
- sink.audio.volume = volume;
+ sink.audio.volume = newVolume;
}
}
+ function setAudioSink(newSink: PwNode): void {
+ Pipewire.preferredDefaultAudioSink = newSink
+ }
+
+ function setAudioSource(newSource: PwNode): void {
+ Pipewire.preferredDefaultAudioSource = newSource
+ }
+
PwObjectTracker {
- objects: [Pipewire.defaultAudioSink, Pipewire.defaultAudioSource]
+ objects: [...root.sinks, ...root.sources]
}
}
diff --git a/utils/Icons.qml b/utils/Icons.qml
index 5e95575..cd635a0 100644
--- a/utils/Icons.qml
+++ b/utils/Icons.qml
@@ -223,6 +223,16 @@ Singleton {
return "chat";
}
+ function getVolumeIcon(volume: real, isMuted: bool): string {
+ if (isMuted)
+ return "no_sound";
+ if (volume >= 0.5)
+ return "volume_up";
+ if (volume > 0)
+ return "volume_down";
+ return "volume_mute";
+ }
+
FileView {
path: "/etc/os-release"
onLoaded: {