diff --git a/shell/bar/volume/ApplicationMixer.qml b/shell/bar/volume/ApplicationMixer.qml
deleted file mode 100644
index 584be77..0000000
--- a/shell/bar/volume/ApplicationMixer.qml
+++ /dev/null
@@ -1,70 +0,0 @@
-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
deleted file mode 100644
index e7076dc..0000000
--- a/shell/bar/volume/DeviceMixer.qml
+++ /dev/null
@@ -1,65 +0,0 @@
-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 c431c30..9a47ba4 100644
--- a/shell/bar/volume/VolumeCard.qml
+++ b/shell/bar/volume/VolumeCard.qml
@@ -2,7 +2,6 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
-import QtQuick.Controls
import Quickshell.Widgets
import Quickshell.Services.Pipewire
import qs.widgets
@@ -13,34 +12,34 @@ Loader {
active: node != null
required property PwNode node
- property string label: node.nickname
+ property string label: node.nickname === "" ? node.description : node.nickname
- sourceComponent: WrapperRectangle {
- id: comp
- color: ShellSettings.colors.surface_container_translucent
- radius: 12
+ property Component leftWidget
+
+ PwObjectTracker {
+ id: tracker
+ objects: [root.node]
+ }
+
+ sourceComponent: WrapperItem {
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
@@ -49,8 +48,6 @@ 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
@@ -64,42 +61,6 @@ 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
deleted file mode 100644
index bcb4f48..0000000
--- a/shell/bar/volume/VolumeControl.qml
+++ /dev/null
@@ -1,36 +0,0 @@
-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 20b51ef..f4b6785 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,14 +14,19 @@ StyledMouseArea {
required property var bar
property bool showMenu: false
+ property PwNode sink: Pipewire.defaultAudioSink
IconImage {
id: icon
- source: "root:resources/volume/volume-full.svg"
-
- anchors {
- fill: parent
- margins: 2
+ 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";
}
}
@@ -32,66 +37,95 @@ StyledMouseArea {
show: root.showMenu
onClosed: root.showMenu = false
- implicitWidth: 300
+ implicitWidth: 275
implicitHeight: container.implicitHeight + (2 * 8)
- property PwNode sink: Pipewire.defaultAudioSink
- property real entryHeight: 45
+ property real entryHeight: 40
ColumnLayout {
id: container
- spacing: 4
+ spacing: 2
anchors {
fill: parent
- margins: 8
+ margins: 4
}
// Default Audio
VolumeCard {
- node: menu.sink
+ id: defaultCard
+ node: root.sink
Layout.fillWidth: true
Layout.preferredHeight: menu.entryHeight
- }
- Rectangle {
- color: ShellSettings.colors.active_translucent
- radius: height / 2
- Layout.leftMargin: 3
- Layout.rightMargin: 3
- Layout.fillWidth: true
- Layout.preferredHeight: 2
+ 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";
+ }
+ }
+ }
}
// Application Mixer
- Loader {
- id: sinkLoader
- active: menu.sink
+ PwNodeLinkTracker {
+ id: linkTracker
+ node: root.sink
+ }
+
+ StyledListView {
+ id: appList
+ visible: linkTracker.linkGroups.length !== 0
+ spacing: 2
+ model: linkTracker.linkGroups
+ clip: true
Layout.fillWidth: true
- Layout.preferredHeight: 5 * menu.entryHeight
+ Layout.preferredHeight: {
+ const entryHeight = Math.min(5, linkTracker.linkGroups.length);
- PwNodeLinkTracker {
- id: linkTracker
- node: menu.sink
+ return entryHeight * (menu.entryHeight + appList.spacing);
}
- sourceComponent: ListView {
- anchors.fill: parent
- spacing: 6
- model: linkTracker.linkGroups
+ delegate: VolumeCard {
+ id: appCard
+ node: modelData.source
+ label: node.properties["media.name"] ?? ""
+ width: ListView.view.width
+ height: menu.entryHeight
- delegate: Loader {
- id: nodeLoader
- active: modelData.source != null
- width: ListView.view.width
- height: menu.entryHeight
+ required property PwLinkGroup modelData
- required property PwLinkGroup modelData
+ leftWidget: StyledMouseArea {
+ onClicked: appCard.node.audio.muted = !appCard.node.audio.muted
- sourceComponent: VolumeCard {
- node: nodeLoader.modelData.source
- label: node.properties["media.name"] ?? ""
+ 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
}
}
}
diff --git a/shell/launcher/Controller.qml b/shell/launcher/Controller.qml
index e56f366..099d407 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 / 2.75
+ topMargin: screen.height / 3
}
ColumnLayout {
diff --git a/shell/resources/mask.png b/shell/resources/mask.png
deleted file mode 100644
index e6cac94..0000000
Binary files a/shell/resources/mask.png and /dev/null differ
diff --git a/shell/resources/volume/microphone-full.svg b/shell/resources/volume/microphone-full.svg
deleted file mode 100644
index 8d1a116..0000000
--- a/shell/resources/volume/microphone-full.svg
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/shell/resources/volume/microphone-mute.svg b/shell/resources/volume/microphone-mute.svg
deleted file mode 100644
index 8c2d3b5..0000000
--- a/shell/resources/volume/microphone-mute.svg
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/shell/resources/volume/volume-full.svg b/shell/resources/volume/volume-full.svg
deleted file mode 100644
index 19bbfb1..0000000
--- a/shell/resources/volume/volume-full.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/shell/resources/volume/volume-low.svg b/shell/resources/volume/volume-low.svg
deleted file mode 100644
index 69e95e5..0000000
--- a/shell/resources/volume/volume-low.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/shell/resources/volume/volume-mute.svg b/shell/resources/volume/volume-mute.svg
deleted file mode 100644
index 4b67e0c..0000000
--- a/shell/resources/volume/volume-mute.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/shell/resources/volume/volume-off.svg b/shell/resources/volume/volume-off.svg
deleted file mode 100644
index f975a8b..0000000
--- a/shell/resources/volume/volume-off.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/shell/shell.qml b/shell/shell.qml
index 8527c2d..7986317 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 "volume-osd" as VolumeOSD
+import "volosd" as VolumeOSD
import "settings" as Settings
import "launcher" as Launcher
import "lockscreen" as LockScreen
diff --git a/shell/volume-osd/Controller.qml b/shell/volosd/Controller.qml
similarity index 73%
rename from shell/volume-osd/Controller.qml
rename to shell/volosd/Controller.qml
index 5e6bd3e..2b7f45e 100644
--- a/shell/volume-osd/Controller.qml
+++ b/shell/volosd/Controller.qml
@@ -21,7 +21,11 @@ Scope {
target: Pipewire.defaultAudioSink?.audio
function onVolumeChanged() {
- console.log("Volume Changed, showing OSD.");
+ root.shouldShowOsd = true;
+ hideTimer.restart();
+ }
+
+ function onMutedChanged() {
root.shouldShowOsd = true;
hideTimer.restart();
}
@@ -53,6 +57,8 @@ Scope {
// radius: 8
RowLayout {
+ spacing: 10
+
anchors {
fill: parent
leftMargin: 10
@@ -61,15 +67,25 @@ Scope {
IconImage {
implicitSize: 30
- source: "root:resources/volume/volume-full.svg"
+ 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";
+ }
}
Rectangle {
id: sliderBackground
Layout.fillWidth: true
implicitHeight: 10
- radius: 20
- color: ShellSettings.colors.inactive
+ radius: height / 2
+ color: "transparent"
+ border.color: ShellSettings.colors.active_translucent
+ border.width: 1
layer.enabled: true
layer.effect: OpacityMask {
@@ -83,13 +99,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
new file mode 100644
index 0000000..c53f7f9
--- /dev/null
+++ b/shell/widgets/StyledListView.qml
@@ -0,0 +1,53 @@
+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 11b653a..d7b59c8 100644
--- a/shell/widgets/StyledSlider.qml
+++ b/shell/widgets/StyledSlider.qml
@@ -7,15 +7,18 @@ import ".."
Slider {
id: slider
- implicitHeight: 8
+ implicitHeight: 7
+
property var accentColor: ShellSettings.colors.active
background: Rectangle {
id: sliderContainer
width: slider.availableWidth
height: slider.implicitHeight
- color: ShellSettings.colors.inactive
- radius: 4
+ color: "transparent"
+ border.color: ShellSettings.colors.active_translucent
+ border.width: 1
+ radius: 5
anchors.verticalCenter: parent.verticalCenter
layer.enabled: true
@@ -39,7 +42,7 @@ Slider {
id: fill
width: slider.handle.width / 2 + slider.visualPosition * (sliderContainer.width - slider.handle.width)
height: sliderContainer.height
- color: Qt.color(slider.accentColor ?? "purple").darker(1.2)
+ color: ShellSettings.colors.active_translucent
}
}