diff --git a/.qmllint.ini b/.qmllint.ini new file mode 100644 index 0000000..07b81e9 --- /dev/null +++ b/.qmllint.ini @@ -0,0 +1,33 @@ +[General] +DisableDefaultImports=false + +[Warnings] +AccessSingletonViaObject=warning +AttachedPropertyReuse=disable +BadSignalHandlerParameters=warning +CompilerWarnings=disable +Deprecated=warning +DuplicatePropertyBinding=warning +DuplicatedName=warning +ImportFailure=warning +IncompatibleType=warning +InheritanceCycle=warning +InvalidLintDirective=warning +LintPluginWarnings=disable +MissingProperty=warning +MissingType=warning +MultilineStrings=info +NonListProperty=warning +PrefixedImportType=warning +PropertyAliasCycles=warning +ReadOnlyProperty=warning +RequiredProperty=warning +RestrictedType=warning +TopLevelComponent=warning +UncreatableType=warning +UnqualifiedAccess=warning +UnresolvedType=warning +UnusedImports=info +UseProperFunction=warning +VarUsedBeforeDeclaration=warning +WithStatement=warning diff --git a/.qmlls.ini b/.qmlls.ini new file mode 100644 index 0000000..780b83a --- /dev/null +++ b/.qmlls.ini @@ -0,0 +1,2 @@ +[General] +no-cmake-calls=false diff --git a/assets/audio-volume-high.svg b/assets/audio-volume-high.svg new file mode 100644 index 0000000..05720a8 --- /dev/null +++ b/assets/audio-volume-high.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/assets/folder-music.svg b/assets/folder-music.svg new file mode 100644 index 0000000..7a0bb78 --- /dev/null +++ b/assets/folder-music.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/assets/mute.png b/assets/mute.png new file mode 100644 index 0000000..d34d1e1 Binary files /dev/null and b/assets/mute.png differ diff --git a/src/speaker.png b/assets/speaker.png similarity index 98% rename from src/speaker.png rename to assets/speaker.png index 450986d..8c8fec5 100644 Binary files a/src/speaker.png and b/assets/speaker.png differ diff --git a/assets/volume-mute.png b/assets/volume-mute.png new file mode 100644 index 0000000..409b8a1 Binary files /dev/null and b/assets/volume-mute.png differ diff --git a/src/AudioOutput.qml b/src/AudioOutput.qml index 6379cdf..c63b2f9 100644 --- a/src/AudioOutput.qml +++ b/src/AudioOutput.qml @@ -13,7 +13,7 @@ import "windows" // BUG: When controlling audio from outside of QS, the change is reflected, but if audio in pavucontrol is not 100% // QS can only change from 0 to whatever is set in pavucontrol Rectangle { - id: test + id: aoutput width: parent.width height: (icon.height + slider.height) * 1.5 anchors.bottomMargin: 5 @@ -28,13 +28,20 @@ Rectangle { objects: [sink] } + required property var popupAnchor + + AudioManager { + id: audioman + anchor.window: popupAnchor + } + MouseArea { id: audio_area anchors.fill: parent onClicked: { - AudioManager.visible = !AudioManager.visible; + audioman.visible = !audioman.visible; } onWheel: wheel => { @@ -52,7 +59,7 @@ Rectangle { // TODO: Make icon depend on sink type and volume level Image { id: icon - source: "speaker.png" + source: "root:/../assets/speaker.png" width: parent.width * (2 / 3) anchors.horizontalCenter: parent.horizontalCenter diff --git a/src/Bar.qml b/src/Bar.qml index 63aa0b5..e08cc15 100644 --- a/src/Bar.qml +++ b/src/Bar.qml @@ -1,14 +1,17 @@ import Quickshell // for ShellRoot and PanelWindow import QtQuick // for Text import Quickshell.Io // for process +import "windows" Scope { Variants { model: Quickshell.screens // the screen from the screens list will be injected into this property PanelWindow { + id: root property var modelData screen: modelData + anchors { top: true left: true @@ -17,7 +20,7 @@ Scope { margins.left: 2 width: 30 - color: "#00000000" + color: "transparent" // the ClockWidget type we just created // TODO: on click open a calendar view @@ -27,6 +30,7 @@ Scope { } AudioOutput { + popupAnchor: root anchors.top: clock.bottom anchors.horizontalCenter: parent.horizontalCenter } diff --git a/src/windows/AudioEntry.qml b/src/windows/AudioEntry.qml new file mode 100644 index 0000000..6d63155 --- /dev/null +++ b/src/windows/AudioEntry.qml @@ -0,0 +1,84 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell.Services.Pipewire + +RowLayout { + required property PwNode node + + // bind the node so we can read its properties + PwObjectTracker { + objects: [node] + } + + //Component.onCompleted: console.log(JSON.stringify(node.properties, null, 2)) + + Image { + source: { + const hasIcon = !!node.properties["application.icon-name"] ?? false; + console.log(hasIcon); + const getFallback = () => node.isStream ? "root:/../assets/folder-music.svg" : "root:/../assets/audio-volume-high.svg"; + const icon = hasIcon ? `image://icon/${node.properties["application.icon-name"]}` : getFallback(); + icon; + } + + //sourceSize.width: 40 + //sourceSize.height: 40 + fillMode: Image.PreserveAspectFit + + Layout.fillHeight: true + Layout.fillWidth: true + Layout.maximumWidth: 40 + Layout.maximumHeight: parent.height + + sourceSize.width: 40 + sourceSize.height: 40 + //width: 40 + //height: 40 + Component.onCompleted: console.log(`width: ${paintedWidth}, height: ${paintedHeight}`) + } + + ColumnLayout { + RowLayout { + id: b + spacing: 6 + Layout.preferredHeight: 30 + + Text { + text: node.properties["media.name"] ?? node.description + } + + Button { + visible: node.isSink + width: 10 + checkable: true + Image { + source: node.audio.muted ? "root:/../assets/mute.png" : "root:/../assets/speaker.png" + height: parent.height * (2 / 3) + + anchors.centerIn: parent + + fillMode: Image.PreserveAspectFit + } + onClicked: node.audio.muted = !node.audio.muted + } + + Button { + visible: node.isSink + text: "default" + } + } + RowLayout { + Label { + Layout.preferredWidth: 50 + text: `${Math.floor(node.audio.volume * 100)}%` + } + + Slider { + Layout.fillWidth: true + value: node.audio.volume + onValueChanged: node.audio.volume = value + } + } + } +} diff --git a/src/windows/AudioManager.qml b/src/windows/AudioManager.qml index f443b6e..4948da3 100644 --- a/src/windows/AudioManager.qml +++ b/src/windows/AudioManager.qml @@ -1,19 +1,60 @@ -pragma Singleton - import Quickshell +import Quickshell.Services.Pipewire import QtQuick +import QtQuick.Layouts +import QtQuick.Controls -Singleton { - FloatingWindow { - color: "transparent" - height: 300 - width: 600 +PopupWindow { + anchor { + rect.x: 30 + rect.y: 20 + } - Rectangle { + color: "transparent" + width: 500 + height: 300 + visible: false + + Rectangle { + anchors.fill: parent + border.color: "black" + border.width: 2 + radius: 5 + color: "white" + ScrollView { anchors.fill: parent - color: "#20ffffff" + contentWidth: availableWidth - // your content here + ColumnLayout { + anchors.fill: parent + anchors.margins: 10 + + PwNodeLinkTracker { + id: linkTracker + node: Pipewire.defaultAudioSink + } + + AudioEntry { + node: Pipewire.defaultAudioSink + } + + Rectangle { + height: 2 + color: "black" + Layout.fillWidth: true + radius: 10 + } + + Repeater { + // Show all sources, regardless of what sink they are assigned to + model: Pipewire.linkGroups + + AudioEntry { + required property PwLinkGroup modelData + node: modelData.source + } + } + } } } }