feat(ags audio): make it a stack and add inputs
All checks were successful
Discord / discord commits (push) Has been skipped
All checks were successful
Discord / discord commits (push) Has been skipped
This commit is contained in:
parent
0455271294
commit
da07a1a69f
3 changed files with 173 additions and 94 deletions
|
@ -2,6 +2,29 @@
|
|||
@use '../../style/colors';
|
||||
|
||||
.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 {
|
||||
margin: 5px;
|
||||
padding: 10px;
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { bind } from 'astal';
|
||||
import { bind, Variable } from 'astal';
|
||||
import { Gtk, Widget } from 'astal/gtk3';
|
||||
|
||||
import AstalWp from 'gi://AstalWp';
|
||||
|
||||
import { RadioButton } from '../misc/subclasses';
|
||||
import Separator from '../misc/separator';
|
||||
import Streams from './streams';
|
||||
|
||||
|
||||
const ICON_SEP = 6;
|
||||
|
||||
export default () => {
|
||||
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: 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 (
|
||||
<box vertical className="widget audio">
|
||||
|
||||
{bind(audio, 'speakers').as((speakers) => {
|
||||
const widgets = speakers.map((speaker, i) => (
|
||||
<box className="stream" vertical>
|
||||
|
||||
<box className="title">
|
||||
|
||||
<RadioButton
|
||||
css="margin-top: 1px;"
|
||||
|
||||
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;
|
||||
})}
|
||||
<box
|
||||
className="audio widget"
|
||||
vertical
|
||||
>
|
||||
<box
|
||||
className="header"
|
||||
homogeneous
|
||||
>
|
||||
<StackButton label="outputs" iconName="audio-speakers-symbolic" />
|
||||
<StackButton label="inputs" iconName="audio-input-microphone-symbolic" />
|
||||
</box>
|
||||
|
||||
{stack}
|
||||
</box>
|
||||
|
||||
|
||||
);
|
||||
};
|
||||
|
|
91
nixosModules/ags/config/widgets/audio/streams.tsx
Normal file
91
nixosModules/ags/config/widgets/audio/streams.tsx
Normal 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>
|
||||
));
|
||||
};
|
Loading…
Reference in a new issue