Give Sound the whole story, and keep the buttons inside the card
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -1,70 +1,163 @@
|
||||
// Sound.
|
||||
//
|
||||
// One sectioned page, top to bottom: what plays, what listens, what each
|
||||
// application is doing, what the machine says when it wants attention, and the
|
||||
// card profiles underneath all of it. There are no tabs and nothing to apply --
|
||||
// every control here writes to PipeWire or to a desktop key that other
|
||||
// applications already honour, and lands the moment it is moved.
|
||||
//
|
||||
// The page used to end in a button that opened GNOME's Sound panel for device
|
||||
// profiles. It does not any more: SoundCards reads and writes them natively, so
|
||||
// there is nothing left on this subject that Panama hands to someone else.
|
||||
|
||||
import QtQuick
|
||||
import qs.config
|
||||
import qs.services
|
||||
|
||||
SettingsPage {
|
||||
id: root
|
||||
|
||||
title: "Sound"
|
||||
lede: "Live PipeWire output, input, and device selection."
|
||||
lede: "Live PipeWire — every change lands immediately, nothing to apply."
|
||||
|
||||
readonly property var currentOutput: AudioDevices.current(true)
|
||||
readonly property var currentInput: AudioDevices.current(false)
|
||||
|
||||
// Over-amplification is the whole range, not a second slider: the maximum
|
||||
// moves and the region past 100% is marked.
|
||||
readonly property real volumeMaximum: Settings.overAmplification ? 1.5 : 1
|
||||
|
||||
// The three services that read the machine rather than the graph do it once
|
||||
// per page open, the way the Displays page refreshes hardware brightness.
|
||||
Component.onCompleted: {
|
||||
SoundFeedback.refresh();
|
||||
SoundDefaults.refresh();
|
||||
SoundCards.refresh();
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Output"
|
||||
subtitle: AudioDevices.current(true)?.description ?? "No output device"
|
||||
subtitle: root.currentOutput
|
||||
? AudioDevices.label(root.currentOutput)
|
||||
: "No output device"
|
||||
|
||||
SoundDeviceList {
|
||||
width: parent.width
|
||||
output: true
|
||||
}
|
||||
|
||||
SoundVolumeRow {
|
||||
label: "Volume"
|
||||
node: root.currentOutput
|
||||
maximum: root.volumeMaximum
|
||||
}
|
||||
|
||||
AudioBalance {
|
||||
width: parent.width
|
||||
node: AudioDevices.current(true)
|
||||
node: root.currentOutput
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
setting: "overAmplification"
|
||||
divider: false
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Input"
|
||||
subtitle: AudioDevices.current(false)?.description ?? "No input device"
|
||||
subtitle: root.currentInput
|
||||
? AudioDevices.label(root.currentInput)
|
||||
: "No input device"
|
||||
|
||||
SoundDeviceList {
|
||||
width: parent.width
|
||||
output: false
|
||||
}
|
||||
|
||||
SoundVolumeRow {
|
||||
label: "Input volume"
|
||||
node: root.currentInput
|
||||
}
|
||||
|
||||
// Recording and playing back is the only honest microphone test: the
|
||||
// level meter above says something is arriving, not that it is you.
|
||||
ActionRow {
|
||||
label: "Test your microphone"
|
||||
detail: "Record a few seconds and play it back through the selected output"
|
||||
divider: captureRow.visible
|
||||
action: {
|
||||
if (SoundTest.micTestState === "recording")
|
||||
return "Recording…";
|
||||
if (SoundTest.micTestState === "playing")
|
||||
return "Playing back…";
|
||||
return "Record & play back";
|
||||
}
|
||||
enabled: SoundTest.micTestState === "idle" && !!root.currentInput
|
||||
onTriggered: SoundTest.startMicTest(root.currentInput, root.currentOutput)
|
||||
}
|
||||
|
||||
SoundCaptureRow {
|
||||
id: captureRow
|
||||
divider: false
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Applications"
|
||||
subtitle: "Control each application currently playing through PipeWire."
|
||||
subtitle: SoundRouting.lastError !== ""
|
||||
? SoundRouting.lastError
|
||||
: "Each application's own level, and where it plays. Applications playing sound appear here on their own."
|
||||
|
||||
ApplicationMixer {
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
// ── What the machine says when it wants attention ───────────────────────
|
||||
Text {
|
||||
width: parent.width
|
||||
text: "Alerts & feedback"
|
||||
color: Theme.fgMuted
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.DemiBold
|
||||
font.capitalization: Font.AllUppercase
|
||||
font.letterSpacing: 0.7
|
||||
topPadding: 6
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Sound feedback"
|
||||
subtitle: "Use the same event preferences as GTK and GNOME applications."
|
||||
title: "Alerts"
|
||||
subtitle: SoundFeedback.lastError
|
||||
|
||||
SoundThemeRow {}
|
||||
|
||||
SettingRow {
|
||||
label: "Event sounds"
|
||||
detail: "Play alerts and interface event sounds"
|
||||
controlWidth: 42
|
||||
detail: "Interface sounds from GTK and GNOME applications, and Panama's notification chime"
|
||||
controlWidth: 48
|
||||
|
||||
SettingsToggle {
|
||||
anchors.fill: parent
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
checked: SoundFeedback.eventSounds
|
||||
enabled: !SoundFeedback.busy
|
||||
onToggled: checked => SoundFeedback.setEventSounds(checked)
|
||||
}
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
setting: "volumeChangeBlip"
|
||||
}
|
||||
|
||||
SettingRow {
|
||||
label: "Input feedback"
|
||||
detail: "Play sounds for supported typing and input events"
|
||||
controlWidth: 42
|
||||
detail: "Key clicks and input event sounds in GTK applications"
|
||||
controlWidth: 48
|
||||
divider: false
|
||||
|
||||
SettingsToggle {
|
||||
anchors.fill: parent
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
checked: SoundFeedback.inputFeedback
|
||||
enabled: !SoundFeedback.busy
|
||||
onToggled: checked => SoundFeedback.setInputFeedback(checked)
|
||||
@@ -72,15 +165,56 @@ SettingsPage {
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
title: "Advanced sound"
|
||||
// ── The hardware underneath the devices ─────────────────────────────────
|
||||
Text {
|
||||
width: parent.width
|
||||
text: "Advanced"
|
||||
color: Theme.fgMuted
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.DemiBold
|
||||
font.capitalization: Font.AllUppercase
|
||||
font.letterSpacing: 0.7
|
||||
topPadding: 6
|
||||
}
|
||||
|
||||
ActionRow {
|
||||
label: "Device profiles"
|
||||
detail: "Open Fedora's complete device profile panel"
|
||||
divider: false
|
||||
action: "Open panel"
|
||||
onTriggered: SystemSettings.openGnomePanel("sound")
|
||||
SettingsCard {
|
||||
title: "Device profiles"
|
||||
subtitle: SoundCards.lastError !== ""
|
||||
? SoundCards.lastError
|
||||
: "What each sound card is configured to do. Ports that are not physically connected are named, not hidden."
|
||||
|
||||
Repeater {
|
||||
model: SoundCards.cards ?? []
|
||||
|
||||
OptionPickerRow {
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
label: String(modelData.description ?? modelData.name ?? "")
|
||||
detail: String(modelData.portHint ?? "")
|
||||
enabled: !SoundCards.busy
|
||||
divider: index < (SoundCards.cards?.length ?? 0) - 1
|
||||
current: modelData.activeProfile
|
||||
options: (modelData.profiles ?? []).map(profile => ({
|
||||
value: profile.name,
|
||||
label: String(profile.description ?? profile.name),
|
||||
detail: profile.available === false ? "Not available right now" : ""
|
||||
}))
|
||||
onPicked: value => SoundCards.setProfile(String(modelData.name), value)
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
visible: (SoundCards.cards?.length ?? 0) === 0
|
||||
text: SoundCards.busy ? "Reading device profiles…" : "No sound cards reported"
|
||||
color: Theme.fgDim
|
||||
font.family: Theme.fontFamily
|
||||
font.pixelSize: Theme.fontSize
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
topPadding: 12
|
||||
bottomPadding: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user