aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/modules/sound/SoundPage.qml
blob: d530e9d1a3eab114f4ae7ede9544e4ce2dfe37ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright (C) 2026 Danilo M. <danix@danix.xyz>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

import Quickshell
import Quickshell.Services.Pipewire
import QtQuick
import "../.."

Column {
    id: page

    required property var service
    signal back

    spacing: 16

    // Output and input, each with its own slider.
    Repeater {
        model: [
            { label: "Output", node: page.service.sink },
            { label: "Input", node: page.service.source },
        ]

        Column {
            required property var modelData
            readonly property var audio: modelData.node?.audio ?? null

            width: page.width
            spacing: 6

            Item {
                width: parent.width
                implicitHeight: name.implicitHeight

                Text {
                    id: name
                    anchors.left: parent.left
                    text: modelData.label
                    font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1; bold: true }
                    color: Theme.text
                }

                Text {
                    anchors.right: parent.right
                    text: !audio ? "—" : audio.muted ? "muted" : Math.round(audio.volume * 100) + "%"
                    font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1 }
                    color: audio?.muted ? Theme.red : Theme.subtext
                }
            }

            Text {
                width: parent.width
                elide: Text.ElideRight
                text: modelData.node?.description ?? modelData.node?.name ?? ""
                font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 }
                color: Theme.overlay
            }

            // Click or drag anywhere on the bar to set the level.
            Rectangle {
                width: parent.width
                height: 8
                radius: 4
                color: Theme.surface

                Rectangle {
                    height: parent.height
                    radius: parent.radius
                    width: parent.width * Math.min(audio?.volume ?? 0, 1)
                    color: audio?.muted ? Theme.red : Theme.accent
                    opacity: audio?.muted ? 0.5 : 1
                }

                MouseArea {
                    anchors.fill: parent
                    enabled: audio !== null
                    onPositionChanged: mouse => set(mouse.x)
                    onPressed: mouse => set(mouse.x)
                    function set(x) {
                        if (audio) audio.volume = Math.max(0, Math.min(1, x / width));
                    }
                }
            }
        }
    }

    Rectangle { width: parent.width; height: 1; color: Qt.alpha(Theme.text, 0.12) }

    // What is playing, with transport. Same Player singleton the OSD uses,
    // so playerctld's duplicate is already filtered out by dbusName.
    Column {
        width: parent.width
        spacing: 8
        visible: Player.active

        Text {
            width: parent.width
            elide: Text.ElideRight
            text: Player.title || "Nothing playing"
            font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 1; bold: true }
            color: Theme.text
        }

        Text {
            width: parent.width
            elide: Text.ElideRight
            visible: Player.artist !== ""
            text: Player.artist
            font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 3 }
            color: Theme.subtext
        }

        Row {
            spacing: 4
            TransportButton {
                glyph: ""
                enabled: Player.current?.canGoPrevious ?? false
                onClicked: Player.current?.previous()
            }
            TransportButton {
                glyph: Player.playing ? "" : ""
                enabled: Player.current?.canTogglePlaying ?? false
                onClicked: Player.current?.togglePlaying()
            }
            TransportButton {
                glyph: ""
                enabled: Player.current?.canGoNext ?? false
                onClicked: Player.current?.next()
            }
        }
    }
}