feat(ags audio): make it a stack and add inputs
All checks were successful
Discord / discord commits (push) Has been skipped

This commit is contained in:
matt1432 2024-12-07 19:39:31 -05:00
parent 0455271294
commit da07a1a69f
3 changed files with 173 additions and 94 deletions

View file

@ -2,6 +2,29 @@
@use '../../style/colors'; @use '../../style/colors';
.widget.audio { .widget.audio {
.header {
.header-btn {
font-size: 30px;
margin: 5px;
transition: background 400ms;
&.active {
background: colors.$window_bg_color;
}
}
}
scrollable {
margin: 5px;
min-height: 400px;
box {
.item-btn {
margin: 3px;
}
}
}
.stream { .stream {
margin: 5px; margin: 5px;
padding: 10px; padding: 10px;

View file

@ -1,12 +1,14 @@
import { bind } from 'astal'; import { bind, Variable } from 'astal';
import { Gtk, Widget } from 'astal/gtk3'; import { Gtk, Widget } from 'astal/gtk3';
import AstalWp from 'gi://AstalWp'; import AstalWp from 'gi://AstalWp';
import { RadioButton } from '../misc/subclasses';
import Separator from '../misc/separator'; import Separator from '../misc/separator';
import Streams from './streams';
const ICON_SEP = 6;
export default () => { export default () => {
const audio = AstalWp.get_default()?.get_audio(); const audio = AstalWp.get_default()?.get_audio();
@ -17,100 +19,63 @@ export default () => {
// TODO: make a stack to have outputs, inputs and currently playing apps // TODO: make a stack to have outputs, inputs and currently playing apps
// TODO: figure out ports and profiles // TODO: figure out ports and profiles
const Shown = Variable<string>('outputs');
const stack = (
<stack
shown={bind(Shown)}
transitionType={Gtk.StackTransitionType.SLIDE_LEFT_RIGHT}
>
<scrollable name="outputs" hscroll={Gtk.PolicyType.NEVER}>
<box vertical>
{bind(audio, 'speakers').as(Streams)}
</box>
</scrollable>
<scrollable name="inputs" hscroll={Gtk.PolicyType.NEVER}>
<box vertical>
{bind(audio, 'microphones').as(Streams)}
</box>
</scrollable>
</stack>
) as Widget.Stack;
const StackButton = ({ label = '', iconName = '' }) => (
<button
cursor="pointer"
className={bind(Shown).as((shown) =>
`header-btn${shown === label ? ' active' : ''}`)}
onButtonReleaseEvent={() => {
Shown.set(label);
}}
>
<box halign={Gtk.Align.CENTER}>
<icon icon={iconName} />
<Separator size={ICON_SEP} />
<label label={label} valign={Gtk.Align.CENTER} />
</box>
</button>
) as Widget.Button;
return ( return (
<box vertical className="widget audio"> <box
className="audio widget"
{bind(audio, 'speakers').as((speakers) => { vertical
const widgets = speakers.map((speaker, i) => ( >
<box className="stream" vertical> <box
className="header"
<box className="title"> homogeneous
>
<RadioButton <StackButton label="outputs" iconName="audio-speakers-symbolic" />
css="margin-top: 1px;" <StackButton label="inputs" iconName="audio-input-microphone-symbolic" />
</box>
active={bind(speaker, 'isDefault')}
onRealize={(self) => {
if (i !== 0) {
self.group = (((widgets[0] as Widget.Box)
.get_children()[0] as Widget.Box)
.get_children()[0] as RadioButton);
}
}}
onButtonReleaseEvent={() => {
speaker.isDefault = true;
}}
/>
<Separator size={8} />
<label label={bind(speaker, 'description')} />
</box>
<Separator size={4} vertical />
<box className="body">
<button
cursor="pointer"
className="toggle"
valign={Gtk.Align.END}
onButtonReleaseEvent={() => {
speaker.mute = !speaker.mute;
}}
>
<icon
icon={bind(speaker, 'mute').as((isMuted) => isMuted ?
'audio-volume-muted-symbolic' :
'audio-speakers-symbolic')}
/>
</button>
<Separator size={4} />
{/*
FIXME: lockChannels not working
TODO: have two sliders when lockChannels === false
*/}
<button
cursor="pointer"
className="toggle"
valign={Gtk.Align.END}
onButtonReleaseEvent={() => {
speaker.lockChannels = !speaker.lockChannels;
}}
>
<icon
icon={bind(speaker, 'lockChannels').as((locked) => locked ?
'channel-secure-symbolic' :
'channel-insecure-symbolic')}
/>
</button>
<slider
hexpand
halign={Gtk.Align.FILL}
drawValue
value={bind(speaker, 'volume')}
onDragged={(self) => {
speaker.set_volume(self.value);
}}
/>
</box>
</box>
));
return widgets;
})}
{stack}
</box> </box>
); );
}; };

View file

@ -0,0 +1,91 @@
import { bind } from 'astal';
import { Gtk } from 'astal/gtk3';
import AstalWp from 'gi://AstalWp';
import { RadioButton } from '../misc/subclasses';
import Separator from '../misc/separator';
export default (streams: AstalWp.Endpoint[]) => {
let group: RadioButton | undefined;
return streams.map((stream) => (
<box className="stream" vertical>
<box className="title">
<RadioButton
cursor="pointer"
css="margin-top: 1px;"
active={bind(stream, 'isDefault')}
onRealize={(self) => {
if (!group) {
group = self;
}
else {
self.group = group;
}
}}
onButtonReleaseEvent={() => {
stream.isDefault = true;
}}
/>
<Separator size={8} />
<label label={bind(stream, 'description')} />
</box>
<Separator size={4} vertical />
<box className="body">
<button
cursor="pointer"
className="toggle"
valign={Gtk.Align.END}
onButtonReleaseEvent={() => {
stream.mute = !stream.mute;
}}
>
<icon
icon={bind(stream, 'mute').as((isMuted) => {
if (stream.mediaClass === AstalWp.MediaClass.AUDIO_MICROPHONE) {
return isMuted ?
'audio-input-microphone-muted-symbolic' :
'audio-input-microphone-symbolic';
}
else {
return isMuted ?
'audio-volume-muted-symbolic' :
'audio-speakers-symbolic';
}
})}
/>
</button>
<Separator size={4} />
<slider
hexpand
halign={Gtk.Align.FILL}
drawValue
cursor="pointer"
value={bind(stream, 'volume')}
onDragged={(self) => {
stream.set_volume(self.value);
}}
/>
</box>
</box>
));
};