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';
|
@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;
|
||||||
|
|
|
@ -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="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
|
<box
|
||||||
icon={bind(speaker, 'mute').as((isMuted) => isMuted ?
|
className="header"
|
||||||
'audio-volume-muted-symbolic' :
|
homogeneous
|
||||||
'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
|
<StackButton label="outputs" iconName="audio-speakers-symbolic" />
|
||||||
icon={bind(speaker, 'lockChannels').as((locked) => locked ?
|
<StackButton label="inputs" iconName="audio-input-microphone-symbolic" />
|
||||||
'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>
|
||||||
|
|
||||||
|
{stack}
|
||||||
</box>
|
</box>
|
||||||
));
|
|
||||||
|
|
||||||
return widgets;
|
|
||||||
})}
|
|
||||||
|
|
||||||
</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