diff --git a/shell/bar/volume/ApplicationMixer.qml b/shell/bar/volume/ApplicationMixer.qml
new file mode 100644
index 0000000..584be77
--- /dev/null
+++ b/shell/bar/volume/ApplicationMixer.qml
@@ -0,0 +1,70 @@
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Layouts
+import Quickshell.Widgets
+import Quickshell.Services.Pipewire
+import "../../widgets/" as Widgets
+import "../.."
+
+ColumnLayout {
+ id: root
+
+ Loader {
+ id: sinkLoader
+ active: sink
+
+ property PwNode sink: Pipewire.defaultAudioSink
+
+ sourceComponent: WrapperItem {
+ PwNodeLinkTracker {
+ id: linkTracker
+ node: sinkLoader.sink
+ }
+
+ ColumnLayout {
+ Repeater {
+ model: linkTracker.linkGroups
+
+ delegate: Loader {
+ id: nodeLoader
+ active: modelData.source !== null
+ Layout.preferredWidth: 350
+ Layout.preferredHeight: 45
+
+ required property PwLinkGroup modelData
+
+ sourceComponent: VolumeCard {
+ id: nodeCard
+ node: nodeLoader.modelData.source
+ text: node.properties["media.name"] ?? ""
+
+ // if icon-name is undefined, just gonna fallback on the application name
+ icon: IconImage {
+ source: {
+ if (nodeCard.node.properties["application.icon-name"] !== undefined)
+ return `image://icon/${nodeCard.node.properties["application.icon-name"]}`;
+
+ let applicationName = nodeCard.node.properties["application.name"];
+ return `image://icon/${applicationName?.toLowerCase() ?? "image-missing"}`;
+ }
+ }
+
+ button: Widgets.FontIconButton {
+ hoverEnabled: false
+ iconName: nodeCard.node.audio.muted ? "volume_off" : "volume_up"
+ checked: !nodeCard.node.audio.muted
+ inactiveColor: ShellSettings.colors["surface_container_highest"]
+ onClicked: {
+ nodeCard.node.audio.muted = !nodeCard.node.audio.muted;
+ }
+ }
+
+ anchors.fill: parent
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/shell/bar/volume/DeviceMixer.qml b/shell/bar/volume/DeviceMixer.qml
new file mode 100644
index 0000000..e7076dc
--- /dev/null
+++ b/shell/bar/volume/DeviceMixer.qml
@@ -0,0 +1,65 @@
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Layouts
+import Quickshell.Services.Pipewire
+import Quickshell.Widgets
+import qs
+import qs.widgets
+
+ColumnLayout {
+ id: root
+
+ // headphones
+ // don't load until the node is not null
+ Loader {
+ id: sinkLoader
+ active: sink !== null
+ Layout.preferredWidth: 350
+ Layout.preferredHeight: 45
+
+ property PwNode sink: Pipewire.defaultAudioSink
+
+ sourceComponent: VolumeCard {
+ id: sinkCard
+ node: sinkLoader.sink
+ button: StyledMouseArea {
+ property bool checked: !sinkCard.node.audio.muted
+
+ // IconImage {}
+
+ onClicked: {
+ sinkCard.node.audio.muted = !sinkCard.node.audio.muted;
+ }
+ }
+
+ anchors.fill: parent
+ }
+ }
+
+ // microphone, same as above
+ Loader {
+ id: sourceLoader
+ active: source !== null
+ Layout.preferredWidth: 350
+ Layout.preferredHeight: 45
+
+ property PwNode source: Pipewire.defaultAudioSource
+
+ sourceComponent: VolumeCard {
+ id: sourceCard
+ node: sourceLoader.source
+ button: StyledMouseArea {
+ property bool checked: !sourceCard.node.audio.muted
+
+ // IconImage {}
+
+ onClicked: {
+ sourceCard.node.audio.muted = !sourceCard.node.audio.muted;
+ }
+ }
+
+ anchors.fill: parent
+ }
+ }
+}
diff --git a/shell/bar/volume/VolumeCard.qml b/shell/bar/volume/VolumeCard.qml
index 9a47ba4..c431c30 100644
--- a/shell/bar/volume/VolumeCard.qml
+++ b/shell/bar/volume/VolumeCard.qml
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
+import QtQuick.Controls
import Quickshell.Widgets
import Quickshell.Services.Pipewire
import qs.widgets
@@ -12,34 +13,34 @@ Loader {
active: node != null
required property PwNode node
- property string label: node.nickname === "" ? node.description : node.nickname
+ property string label: node.nickname
- property Component leftWidget
-
- PwObjectTracker {
- id: tracker
- objects: [root.node]
- }
-
- sourceComponent: WrapperItem {
+ sourceComponent: WrapperRectangle {
+ id: comp
+ color: ShellSettings.colors.surface_container_translucent
+ radius: 12
margin: 6
+ border {
+ width: 1
+ color: ShellSettings.colors.active_translucent
+ }
+
+ // property Component button
+ // property Component icon
+
+ PwObjectTracker {
+ id: tracker
+ objects: [root.node]
+ }
+
RowLayout {
- spacing: 10
-
- Loader {
- id: leftWidget
- sourceComponent: root.leftWidget
- Layout.preferredWidth: this.height
- Layout.fillHeight: true
- }
-
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Text {
- text: root.label
+ text: root.label
color: ShellSettings.colors.active
elide: Text.ElideRight
Layout.fillWidth: true
@@ -48,6 +49,8 @@ Loader {
StyledSlider {
value: root.node.audio.volume ?? 0
+ // text: root.text
+ // icon: root.icon
onValueChanged: {
// only allow changes when the node is ready other wise you will combust
@@ -61,6 +64,42 @@ Loader {
Layout.fillHeight: true
}
}
+
+ // StyledMouseArea {
+ // id: rightArrow
+ // Layout.preferredWidth: rightArrow.height
+ // // Layout.fillWidth: true
+ // Layout.fillHeight: true
+ //
+ // IconImage {
+ // source: "root:resources/general/right-arrow.svg"
+ // anchors.fill: parent
+ // }
+ // }
+
+ // Loader {
+ // id: buttonLoader
+ // sourceComponent: root.button
+ //
+ // Layout.preferredWidth: this.height
+ // Layout.fillHeight: true
+ // }
}
}
+
+ // sourceComponent: VolumeCard {
+ // id: sinkCard
+ // node: sinkLoader.sink
+ // button: StyledMouseArea {
+ // property bool checked: !sinkCard.node.audio.muted
+ //
+ // // IconImage {}
+ //
+ // onClicked: {
+ // sinkCard.node.audio.muted = !sinkCard.node.audio.muted;
+ // }
+ // }
+ //
+ // anchors.fill: parent
+ // }
}
diff --git a/shell/bar/volume/VolumeControl.qml b/shell/bar/volume/VolumeControl.qml
new file mode 100644
index 0000000..bcb4f48
--- /dev/null
+++ b/shell/bar/volume/VolumeControl.qml
@@ -0,0 +1,36 @@
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Layouts
+import Quickshell.Widgets
+import qs.widgets
+
+DeviceMixer {}
+
+// WrapperItem {
+// id: root
+//
+// ColumnLayout {
+// spacing: 10
+//
+// // TabBar {
+// // id: tabBar
+// // model: ["headphones", "tune"]
+// // Layout.fillWidth: true
+// // Layout.preferredHeight: 35
+// // }
+//
+//
+// // StackLayout {
+// // id: page
+// // currentIndex: tabBar.currentIndex
+// // Layout.fillWidth: true
+// // Layout.preferredHeight: currentItem ? currentItem.implicitHeight : 0
+// //
+// // readonly property Item currentItem: children[currentIndex]
+// //
+// // DeviceMixer {}
+// // ApplicationMixer {}
+// // }
+// }
+// }
diff --git a/shell/bar/volume/VolumeIndicator.qml b/shell/bar/volume/VolumeIndicator.qml
index f4b6785..20b51ef 100644
--- a/shell/bar/volume/VolumeIndicator.qml
+++ b/shell/bar/volume/VolumeIndicator.qml
@@ -2,11 +2,11 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
-import QtQuick.Effects
import Quickshell.Widgets
import Quickshell.Services.Pipewire
import qs.widgets
import qs.bar
+import qs
StyledMouseArea {
id: root
@@ -14,19 +14,14 @@ StyledMouseArea {
required property var bar
property bool showMenu: false
- property PwNode sink: Pipewire.defaultAudioSink
IconImage {
id: icon
- anchors.fill: parent
- source: if (root.sink.audio.muted) {
- return "image://icon/audio-volume-muted";
- } else if (root.sink.audio.volume > 0.66) {
- return "image://icon/audio-volume-high";
- } else if (root.sink.audio.volume > 0.33) {
- return "image://icon/audio-volume-medium";
- } else {
- return "image://icon/audio-volume-low";
+ source: "root:resources/volume/volume-full.svg"
+
+ anchors {
+ fill: parent
+ margins: 2
}
}
@@ -37,95 +32,66 @@ StyledMouseArea {
show: root.showMenu
onClosed: root.showMenu = false
- implicitWidth: 275
+ implicitWidth: 300
implicitHeight: container.implicitHeight + (2 * 8)
- property real entryHeight: 40
+ property PwNode sink: Pipewire.defaultAudioSink
+ property real entryHeight: 45
ColumnLayout {
id: container
- spacing: 2
+ spacing: 4
anchors {
fill: parent
- margins: 4
+ margins: 8
}
// Default Audio
VolumeCard {
- id: defaultCard
- node: root.sink
+ node: menu.sink
Layout.fillWidth: true
Layout.preferredHeight: menu.entryHeight
+ }
- leftWidget: StyledMouseArea {
- onClicked: defaultCard.node.audio.muted = !defaultCard.node.audio.muted
-
- IconImage {
- anchors.fill: parent
- source: if (root.sink.audio.muted) {
- return "image://icon/audio-volume-muted";
- } else if (root.sink.audio.volume > 0.66) {
- return "image://icon/audio-volume-high";
- } else if (root.sink.audio.volume > 0.33) {
- return "image://icon/audio-volume-medium";
- } else {
- return "image://icon/audio-volume-low";
- }
- }
- }
+ Rectangle {
+ color: ShellSettings.colors.active_translucent
+ radius: height / 2
+ Layout.leftMargin: 3
+ Layout.rightMargin: 3
+ Layout.fillWidth: true
+ Layout.preferredHeight: 2
}
// Application Mixer
- PwNodeLinkTracker {
- id: linkTracker
- node: root.sink
- }
-
- StyledListView {
- id: appList
- visible: linkTracker.linkGroups.length !== 0
- spacing: 2
- model: linkTracker.linkGroups
- clip: true
+ Loader {
+ id: sinkLoader
+ active: menu.sink
Layout.fillWidth: true
- Layout.preferredHeight: {
- const entryHeight = Math.min(5, linkTracker.linkGroups.length);
+ Layout.preferredHeight: 5 * menu.entryHeight
- return entryHeight * (menu.entryHeight + appList.spacing);
+ PwNodeLinkTracker {
+ id: linkTracker
+ node: menu.sink
}
- delegate: VolumeCard {
- id: appCard
- node: modelData.source
- label: node.properties["media.name"] ?? ""
- width: ListView.view.width
- height: menu.entryHeight
+ sourceComponent: ListView {
+ anchors.fill: parent
+ spacing: 6
+ model: linkTracker.linkGroups
- required property PwLinkGroup modelData
+ delegate: Loader {
+ id: nodeLoader
+ active: modelData.source != null
+ width: ListView.view.width
+ height: menu.entryHeight
- leftWidget: StyledMouseArea {
- onClicked: appCard.node.audio.muted = !appCard.node.audio.muted
+ required property PwLinkGroup modelData
- IconImage {
- id: appIcon
- visible: false
- anchors.fill: parent
-
- source: {
- if (appCard.node.properties["application.icon-name"] !== undefined)
- return `image://icon/${appCard.node.properties["application.icon-name"]}`;
-
- let applicationName = appCard.node.properties["application.name"];
- return `image://icon/${applicationName?.toLowerCase() ?? "image-missing"}`;
- }
- }
-
- MultiEffect {
- source: appIcon
- anchors.fill: appIcon
- saturation: appCard.node.audio.muted ? -1.0 : 0.0
+ sourceComponent: VolumeCard {
+ node: nodeLoader.modelData.source
+ label: node.properties["media.name"] ?? ""
}
}
}
diff --git a/shell/launcher/Controller.qml b/shell/launcher/Controller.qml
index 099d407..e56f366 100644
--- a/shell/launcher/Controller.qml
+++ b/shell/launcher/Controller.qml
@@ -64,7 +64,7 @@ Singleton {
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
- topMargin: screen.height / 3
+ topMargin: screen.height / 2.75
}
ColumnLayout {
diff --git a/shell/resources/mask.png b/shell/resources/mask.png
new file mode 100644
index 0000000..e6cac94
Binary files /dev/null and b/shell/resources/mask.png differ
diff --git a/shell/resources/volume/microphone-full.svg b/shell/resources/volume/microphone-full.svg
new file mode 100644
index 0000000..8d1a116
--- /dev/null
+++ b/shell/resources/volume/microphone-full.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/shell/resources/volume/microphone-mute.svg b/shell/resources/volume/microphone-mute.svg
new file mode 100644
index 0000000..8c2d3b5
--- /dev/null
+++ b/shell/resources/volume/microphone-mute.svg
@@ -0,0 +1,2 @@
+
+
diff --git a/shell/resources/volume/volume-full.svg b/shell/resources/volume/volume-full.svg
new file mode 100644
index 0000000..19bbfb1
--- /dev/null
+++ b/shell/resources/volume/volume-full.svg
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/shell/resources/volume/volume-low.svg b/shell/resources/volume/volume-low.svg
new file mode 100644
index 0000000..69e95e5
--- /dev/null
+++ b/shell/resources/volume/volume-low.svg
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/shell/resources/volume/volume-mute.svg b/shell/resources/volume/volume-mute.svg
new file mode 100644
index 0000000..4b67e0c
--- /dev/null
+++ b/shell/resources/volume/volume-mute.svg
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/shell/resources/volume/volume-off.svg b/shell/resources/volume/volume-off.svg
new file mode 100644
index 0000000..f975a8b
--- /dev/null
+++ b/shell/resources/volume/volume-off.svg
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/shell/shell.qml b/shell/shell.qml
index 7986317..8527c2d 100644
--- a/shell/shell.qml
+++ b/shell/shell.qml
@@ -6,7 +6,7 @@ import QtQuick
import "bar"
import "notifications" as Notifications
import "mpris" as Mpris
-import "volosd" as VolumeOSD
+import "volume-osd" as VolumeOSD
import "settings" as Settings
import "launcher" as Launcher
import "lockscreen" as LockScreen
diff --git a/shell/volosd/Controller.qml b/shell/volume-osd/Controller.qml
similarity index 73%
rename from shell/volosd/Controller.qml
rename to shell/volume-osd/Controller.qml
index 2b7f45e..5e6bd3e 100644
--- a/shell/volosd/Controller.qml
+++ b/shell/volume-osd/Controller.qml
@@ -21,11 +21,7 @@ Scope {
target: Pipewire.defaultAudioSink?.audio
function onVolumeChanged() {
- root.shouldShowOsd = true;
- hideTimer.restart();
- }
-
- function onMutedChanged() {
+ console.log("Volume Changed, showing OSD.");
root.shouldShowOsd = true;
hideTimer.restart();
}
@@ -57,8 +53,6 @@ Scope {
// radius: 8
RowLayout {
- spacing: 10
-
anchors {
fill: parent
leftMargin: 10
@@ -67,25 +61,15 @@ Scope {
IconImage {
implicitSize: 30
- source: if (Pipewire.defaultAudioSink?.audio.muted) {
- return "image://icon/audio-volume-muted";
- } else if (Pipewire.defaultAudioSink?.audio.volume > 0.66) {
- return "image://icon/audio-volume-high";
- } else if (Pipewire.defaultAudioSink?.audio.volume > 0.33) {
- return "image://icon/audio-volume-medium";
- } else {
- return "image://icon/audio-volume-low";
- }
+ source: "root:resources/volume/volume-full.svg"
}
Rectangle {
id: sliderBackground
Layout.fillWidth: true
implicitHeight: 10
- radius: height / 2
- color: "transparent"
- border.color: ShellSettings.colors.active_translucent
- border.width: 1
+ radius: 20
+ color: ShellSettings.colors.inactive
layer.enabled: true
layer.effect: OpacityMask {
@@ -99,13 +83,13 @@ Scope {
Rectangle {
color: ShellSettings.colors.active
- implicitWidth: parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0)
-
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
+
+ implicitWidth: parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0)
}
}
}
diff --git a/shell/widgets/StyledListView.qml b/shell/widgets/StyledListView.qml
deleted file mode 100644
index c53f7f9..0000000
--- a/shell/widgets/StyledListView.qml
+++ /dev/null
@@ -1,53 +0,0 @@
-import QtQuick
-
-ListView {
- id: root
-
- add: Transition {
- NumberAnimation {
- property: "opacity"
- from: 0
- to: 1
- duration: 100
- }
- }
-
- displaced: Transition {
- NumberAnimation {
- property: "y"
- duration: 200
- easing.type: Easing.OutCubic
- }
- NumberAnimation {
- property: "opacity"
- to: 1
- duration: 100
- }
- }
-
- move: Transition {
- NumberAnimation {
- property: "y"
- duration: 200
- easing.type: Easing.OutCubic
- }
- NumberAnimation {
- property: "opacity"
- to: 1
- duration: 100
- }
- }
-
- remove: Transition {
- NumberAnimation {
- property: "y"
- duration: 200
- easing.type: Easing.OutCubic
- }
- NumberAnimation {
- property: "opacity"
- to: 0
- duration: 100
- }
- }
-}
diff --git a/shell/widgets/StyledSlider.qml b/shell/widgets/StyledSlider.qml
index d7b59c8..11b653a 100644
--- a/shell/widgets/StyledSlider.qml
+++ b/shell/widgets/StyledSlider.qml
@@ -7,18 +7,15 @@ import ".."
Slider {
id: slider
- implicitHeight: 7
-
+ implicitHeight: 8
property var accentColor: ShellSettings.colors.active
background: Rectangle {
id: sliderContainer
width: slider.availableWidth
height: slider.implicitHeight
- color: "transparent"
- border.color: ShellSettings.colors.active_translucent
- border.width: 1
- radius: 5
+ color: ShellSettings.colors.inactive
+ radius: 4
anchors.verticalCenter: parent.verticalCenter
layer.enabled: true
@@ -42,7 +39,7 @@ Slider {
id: fill
width: slider.handle.width / 2 + slider.visualPosition * (sliderContainer.width - slider.handle.width)
height: sliderContainer.height
- color: ShellSettings.colors.active_translucent
+ color: Qt.color(slider.accentColor ?? "purple").darker(1.2)
}
}