[AGS] Bar: Done (WiFi still missing, will be added at some later point)
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
.audio-box {
|
||||
min-width: 320px;
|
||||
}
|
184
config/astal/components/QuickActions/modules/Audio/Audio.tsx
Normal file
184
config/astal/components/QuickActions/modules/Audio/Audio.tsx
Normal file
@@ -0,0 +1,184 @@
|
||||
import { bind, Binding } from "astal";
|
||||
import { Gtk } from "astal/gtk4";
|
||||
import AstalWp from "gi://AstalWp";
|
||||
|
||||
const wp = AstalWp.get_default()!;
|
||||
|
||||
const AudioModule = () => {
|
||||
const setVolumeSpeaker = (volume: number) => {
|
||||
wp.defaultSpeaker.set_volume(volume / 100);
|
||||
};
|
||||
|
||||
const setVolumeMicrophone = (volume: number) => {
|
||||
wp.defaultMicrophone.set_volume(volume / 100);
|
||||
};
|
||||
|
||||
const speakerSelector = SinkSelectPopover(AstalWp.MediaClass.AUDIO_SPEAKER);
|
||||
const micSelector = SinkSelectPopover(AstalWp.MediaClass.AUDIO_MICROPHONE);
|
||||
|
||||
return (
|
||||
<box cssClasses={["audio-box"]} vertical>
|
||||
<box hexpand vexpand>
|
||||
<button
|
||||
onClicked={() =>
|
||||
wp.defaultSpeaker.set_mute(
|
||||
!wp.defaultSpeaker.get_mute(),
|
||||
)
|
||||
}
|
||||
tooltipText={"Mute audio output"}
|
||||
child={
|
||||
<image
|
||||
iconName={bind(wp.defaultSpeaker, "volumeIcon")}
|
||||
marginEnd={3}
|
||||
></image>
|
||||
}
|
||||
></button>
|
||||
<label
|
||||
label={bind(wp.defaultSpeaker, "volume").as(
|
||||
v => Math.round(100 * v) + "%",
|
||||
)}
|
||||
></label>
|
||||
<slider
|
||||
value={bind(wp.defaultSpeaker, "volume").as(v => 100 * v)}
|
||||
max={100}
|
||||
min={0}
|
||||
step={1}
|
||||
hexpand
|
||||
vexpand
|
||||
onChangeValue={self => setVolumeSpeaker(self.value)}
|
||||
></slider>
|
||||
<button
|
||||
cssClasses={["sink-select-button"]}
|
||||
tooltipText={"Pick audio output"}
|
||||
child={
|
||||
<box>
|
||||
<image iconName={"speaker-symbolic"}></image>
|
||||
{speakerSelector}
|
||||
</box>
|
||||
}
|
||||
onClicked={() => speakerSelector.popup()}
|
||||
></button>
|
||||
</box>
|
||||
<box hexpand vexpand>
|
||||
<button
|
||||
onClicked={() =>
|
||||
wp.defaultMicrophone.set_mute(
|
||||
!wp.defaultMicrophone.get_mute(),
|
||||
)
|
||||
}
|
||||
tooltipText={"Mute audio input"}
|
||||
child={
|
||||
<image
|
||||
iconName={bind(wp.defaultMicrophone, "volumeIcon")}
|
||||
marginEnd={3}
|
||||
></image>
|
||||
}
|
||||
></button>
|
||||
<label
|
||||
label={bind(wp.defaultMicrophone, "volume").as(
|
||||
v => Math.round(100 * v) + "%",
|
||||
)}
|
||||
></label>
|
||||
<slider
|
||||
value={bind(wp.defaultMicrophone, "volume").as(
|
||||
v => 100 * v,
|
||||
)}
|
||||
max={100}
|
||||
min={0}
|
||||
step={1}
|
||||
hexpand
|
||||
vexpand
|
||||
onChangeValue={self => setVolumeMicrophone(self.value)}
|
||||
></slider>
|
||||
<button
|
||||
cssClasses={["sink-select-button"]}
|
||||
tooltipText={"Select audio input"}
|
||||
child={
|
||||
<box>
|
||||
<image iconName={"microphone"}></image>
|
||||
{micSelector}
|
||||
</box>
|
||||
}
|
||||
onClicked={() => micSelector.popup()}
|
||||
></button>
|
||||
</box>
|
||||
</box>
|
||||
);
|
||||
};
|
||||
|
||||
const SinkPicker = (type: AstalWp.MediaClass) => {
|
||||
const devices = bind(wp, "endpoints");
|
||||
|
||||
return (
|
||||
<box vertical>
|
||||
<label
|
||||
label={`Available Audio ${type === AstalWp.MediaClass.AUDIO_SPEAKER ? "Output" : type === AstalWp.MediaClass.AUDIO_MICROPHONE ? "Input" : ""} Devices`}
|
||||
></label>
|
||||
<Gtk.Separator marginBottom={5} marginTop={3}></Gtk.Separator>
|
||||
<box vertical cssClasses={["sink-picker"]}>
|
||||
{devices.as(d => {
|
||||
return d.map(device => {
|
||||
if (device.get_media_class() !== type) {
|
||||
return <box cssClasses={[ 'empty' ]}></box>;
|
||||
}
|
||||
return (
|
||||
<button
|
||||
cssClasses={bind(device, "id").as(id => {
|
||||
if (
|
||||
id ===
|
||||
(type ===
|
||||
AstalWp.MediaClass.AUDIO_SPEAKER
|
||||
? wp.defaultSpeaker.id
|
||||
: type ===
|
||||
AstalWp.MediaClass
|
||||
.AUDIO_MICROPHONE
|
||||
? wp.defaultMicrophone.id
|
||||
: "")
|
||||
) {
|
||||
return [
|
||||
"sink-option",
|
||||
"currently-selected-sink-option",
|
||||
];
|
||||
} else {
|
||||
return ["sink-option"];
|
||||
}
|
||||
})}
|
||||
child={
|
||||
<box halign={Gtk.Align.START}>
|
||||
<image
|
||||
iconName={bind(device, "icon").as(
|
||||
icon => icon,
|
||||
)}
|
||||
marginEnd={3}
|
||||
></image>
|
||||
<label
|
||||
label={bind(
|
||||
device,
|
||||
"description",
|
||||
).as(t => t ?? "")}
|
||||
></label>
|
||||
</box>
|
||||
}
|
||||
onClicked={() => {
|
||||
device.set_is_default(true);
|
||||
}}
|
||||
></button>
|
||||
);
|
||||
});
|
||||
})}
|
||||
</box>
|
||||
</box>
|
||||
);
|
||||
};
|
||||
|
||||
const SinkSelectPopover = (type: AstalWp.MediaClass) => {
|
||||
const popover = new Gtk.Popover();
|
||||
|
||||
popover.set_child(SinkPicker(type));
|
||||
|
||||
return popover;
|
||||
};
|
||||
|
||||
export default {
|
||||
AudioModule,
|
||||
};
|
Reference in New Issue
Block a user