218 lines
7.5 KiB
JavaScript
218 lines
7.5 KiB
JavaScript
import Audio from 'resource:///com/github/Aylur/ags/service/audio.js';
|
|
import Variable from 'resource:///com/github/Aylur/ags/variable.js';
|
|
import Widget from 'resource:///com/github/Aylur/ags/widget.js';
|
|
const { Box, Button, Icon, Label, Revealer, Scrollable, Slider, Stack } = Widget;
|
|
import { MaterialIcon } from '../../.commonwidgets/materialicon.js';
|
|
import { setupCursorHover } from '../../.widgetutils/cursorhover.js';
|
|
import { iconExists } from '../../.miscutils/icons.js';
|
|
|
|
const AppVolume = (stream) => Box({
|
|
className: 'sidebar-volmixer-stream spacing-h-10',
|
|
children: [
|
|
Icon({
|
|
className: 'sidebar-volmixer-stream-appicon',
|
|
vpack: 'center',
|
|
tooltipText: stream.stream.name,
|
|
setup: (self) => {
|
|
self.hook(stream, (self) => {
|
|
self.icon = stream.stream.name.toLowerCase();
|
|
})
|
|
},
|
|
}),
|
|
Box({
|
|
hexpand: true,
|
|
vpack: 'center',
|
|
vertical: true,
|
|
className: 'spacing-v-5',
|
|
children: [
|
|
Label({
|
|
xalign: 0,
|
|
maxWidthChars: 1,
|
|
truncate: 'end',
|
|
label: stream.description,
|
|
className: 'txt-small',
|
|
setup: (self) => self.hook(stream, (self) => {
|
|
self.label = `${stream.stream.name} • ${stream.description}`
|
|
})
|
|
}),
|
|
Slider({
|
|
drawValue: false,
|
|
hpack: 'fill',
|
|
className: 'sidebar-volmixer-stream-slider',
|
|
value: stream.volume,
|
|
min: 0, max: 1,
|
|
onChange: ({ value }) => {
|
|
stream.volume = value;
|
|
},
|
|
setup: (self) => self.hook(stream, (self) => {
|
|
self.value = stream.volume;
|
|
})
|
|
}),
|
|
// Box({
|
|
// homogeneous: true,
|
|
// className: 'test',
|
|
// children: [AnimatedSlider({
|
|
// className: 'sidebar-volmixer-stream-slider',
|
|
// value: stream.volume,
|
|
// })],
|
|
// })
|
|
]
|
|
})
|
|
]
|
|
});
|
|
|
|
const AudioDevices = (input = false) => {
|
|
const dropdownShown = Variable(false);
|
|
const DeviceStream = (stream) => Button({
|
|
tooltipText: stream.description,
|
|
child: Box({
|
|
className: 'txt spacing-h-10',
|
|
children: [
|
|
iconExists(stream.iconName) ? Icon({
|
|
className: 'txt-norm symbolic-icon',
|
|
icon: stream.iconName,
|
|
}) : MaterialIcon(input ? 'mic_external_on' : 'media_output', 'norm'),
|
|
Label({
|
|
hexpand: true,
|
|
xalign: 0,
|
|
className: 'txt-small',
|
|
truncate: 'end',
|
|
maxWidthChars: 1,
|
|
label: stream.description,
|
|
}),
|
|
],
|
|
}),
|
|
onClicked: (self) => {
|
|
if (input) Audio.microphone = stream;
|
|
else Audio.speaker = stream;
|
|
dropdownShown.value = false;
|
|
},
|
|
setup: setupCursorHover,
|
|
})
|
|
const activeDevice = Button({
|
|
onClicked: () => { dropdownShown.value = !dropdownShown.value; },
|
|
child: Box({
|
|
className: 'txt spacing-h-10',
|
|
children: [
|
|
MaterialIcon(input ? 'mic_external_on' : 'media_output', 'norm'),
|
|
Label({
|
|
hexpand: true,
|
|
xalign: 0,
|
|
className: 'txt-small',
|
|
truncate: 'end',
|
|
maxWidthChars: 1,
|
|
label: `${input ? '[In]' : '[Out]'}`,
|
|
setup: (self) => self.hook(Audio, (self) => {
|
|
self.label = `${input ? '[In]' : '[Out]'} ${input ? Audio.microphone.description : Audio.speaker.description}`;
|
|
})
|
|
}),
|
|
Label({
|
|
className: `icon-material txt-norm`,
|
|
setup: (self) => self.hook(dropdownShown, (self) => {
|
|
self.label = dropdownShown.value ? 'expand_less' : 'expand_more';
|
|
})
|
|
})
|
|
],
|
|
}),
|
|
setup: setupCursorHover,
|
|
});
|
|
const deviceSelector = Revealer({
|
|
transition: 'slide_down',
|
|
revealChild: dropdownShown.bind("value"),
|
|
transitionDuration: userOptions.animations.durationSmall,
|
|
child: Box({
|
|
vertical: true,
|
|
children: [
|
|
Box({ className: 'separator-line margin-top-5 margin-bottom-5' }),
|
|
Box({
|
|
vertical: true,
|
|
className: 'spacing-v-5 margin-top-5',
|
|
attribute: {
|
|
'updateStreams': (self) => {
|
|
const streams = input ? Audio.microphones : Audio.speakers;
|
|
self.children = streams.map(stream => DeviceStream(stream));
|
|
},
|
|
},
|
|
setup: (self) => self
|
|
.hook(Audio, self.attribute.updateStreams, 'stream-added')
|
|
.hook(Audio, self.attribute.updateStreams, 'stream-removed')
|
|
,
|
|
}),
|
|
]
|
|
})
|
|
})
|
|
return Box({
|
|
hpack: 'fill',
|
|
className: 'sidebar-volmixer-deviceselector',
|
|
vertical: true,
|
|
children: [
|
|
activeDevice,
|
|
deviceSelector,
|
|
]
|
|
})
|
|
}
|
|
|
|
export default (props) => {
|
|
const emptyContent = Box({
|
|
homogeneous: true,
|
|
children: [Box({
|
|
vertical: true,
|
|
vpack: 'center',
|
|
className: 'txt spacing-v-10',
|
|
children: [
|
|
Box({
|
|
vertical: true,
|
|
className: 'spacing-v-5 txt-subtext',
|
|
children: [
|
|
MaterialIcon('brand_awareness', 'gigantic'),
|
|
Label({ label: 'No audio source', className: 'txt-small' }),
|
|
]
|
|
}),
|
|
]
|
|
})]
|
|
});
|
|
const appList = Scrollable({
|
|
vexpand: true,
|
|
child: Box({
|
|
attribute: {
|
|
'updateStreams': (self) => {
|
|
const streams = Audio.apps;
|
|
self.children = streams.map(stream => AppVolume(stream));
|
|
},
|
|
},
|
|
vertical: true,
|
|
className: 'spacing-v-5',
|
|
setup: (self) => self
|
|
.hook(Audio, self.attribute.updateStreams, 'stream-added')
|
|
.hook(Audio, self.attribute.updateStreams, 'stream-removed')
|
|
,
|
|
})
|
|
})
|
|
const devices = Box({
|
|
vertical: true,
|
|
className: 'spacing-v-5',
|
|
children: [
|
|
AudioDevices(false),
|
|
AudioDevices(true),
|
|
]
|
|
})
|
|
const mainContent = Stack({
|
|
children: {
|
|
'empty': emptyContent,
|
|
'list': appList,
|
|
},
|
|
setup: (self) => self.hook(Audio, (self) => {
|
|
self.shown = (Audio.apps.length > 0 ? 'list' : 'empty')
|
|
}),
|
|
})
|
|
return Box({
|
|
...props,
|
|
className: 'spacing-v-5',
|
|
vertical: true,
|
|
children: [
|
|
mainContent,
|
|
devices,
|
|
]
|
|
});
|
|
}
|