129 lines
4.5 KiB
Python
129 lines
4.5 KiB
Python
import asyncio
|
|
from ignis.widgets import Widget
|
|
from ignis.utils import Utils
|
|
from ignis.services.audio import AudioService, Stream
|
|
from typing import Literal
|
|
from ..menu import Menu
|
|
from ...shared_widgets import MaterialVolumeSlider
|
|
|
|
audio = AudioService.get_default()
|
|
|
|
AUDIO_TYPES = {
|
|
"speaker": {"menu_icon": "audio-headphones-symbolic", "menu_label": "Sound Output"},
|
|
"microphone": {
|
|
"menu_icon": "microphone-sensitivity-high-symbolic",
|
|
"menu_label": "Sound Input",
|
|
},
|
|
}
|
|
|
|
|
|
class DeviceItem(Widget.Button):
|
|
def __init__(self, stream: Stream, _type: Literal["speaker", "microphone"]):
|
|
super().__init__(
|
|
child=Widget.Box(
|
|
child=[
|
|
Widget.Icon(image="audio-card-symbolic"),
|
|
Widget.Label(
|
|
label=stream.description,
|
|
ellipsize="end",
|
|
max_width_chars=30,
|
|
halign="start",
|
|
css_classes=["volume-entry-label"],
|
|
),
|
|
Widget.Icon(
|
|
image="object-select-symbolic",
|
|
halign="end",
|
|
hexpand=True,
|
|
visible=stream.bind("is_default"),
|
|
),
|
|
]
|
|
),
|
|
css_classes=["volume-entry", "unset"],
|
|
hexpand=True,
|
|
setup=lambda self: stream.connect("removed", lambda x: self.unparent()),
|
|
on_click=lambda x: setattr(audio, _type, stream),
|
|
)
|
|
|
|
|
|
class DeviceMenu(Menu):
|
|
def __init__(self, _type: Literal["speaker", "microphone"], style: str = ""):
|
|
data = AUDIO_TYPES[_type]
|
|
|
|
super().__init__(
|
|
name=f"volume-{_type}",
|
|
child=[
|
|
Widget.Box(
|
|
child=[
|
|
Widget.Icon(image=data["menu_icon"], pixel_size=24),
|
|
Widget.Label(
|
|
label=data["menu_label"],
|
|
halign="start",
|
|
css_classes=["volume-entry-list-header-label"],
|
|
),
|
|
],
|
|
css_classes=["volume-entry-list-header-box"],
|
|
),
|
|
Widget.Box(
|
|
vertical=True,
|
|
setup=lambda self: audio.connect(
|
|
f"{_type}-added",
|
|
lambda x, stream: self.append(DeviceItem(stream, _type)),
|
|
),
|
|
),
|
|
Widget.Separator(css_classes=["volume-entry-list-separator"]),
|
|
Widget.Button(
|
|
child=Widget.Box(
|
|
child=[
|
|
Widget.Icon(image="preferences-system-symbolic"),
|
|
Widget.Label(
|
|
label="Sound Settings",
|
|
halign="start",
|
|
css_classes=["volume-entry-label"],
|
|
),
|
|
]
|
|
),
|
|
css_classes=["volume-entry", "unset"],
|
|
style="margin-bottom: 0;",
|
|
on_click=lambda x: asyncio.create_task(Utils.exec_sh_async("pavucontrol")),
|
|
),
|
|
],
|
|
)
|
|
|
|
self.box.add_css_class(f"volume-menubox-{_type}")
|
|
|
|
|
|
class VolumeSlider(Widget.Box):
|
|
def __init__(self, _type: Literal["speaker", "microphone"]):
|
|
stream = getattr(audio, _type)
|
|
|
|
icon = Widget.Button(
|
|
child=Widget.Icon(
|
|
image=stream.bind("icon_name"),
|
|
pixel_size=18,
|
|
),
|
|
css_classes=["material-slider-icon", "unset", "hover-surface"],
|
|
on_click=lambda x: stream.set_is_muted(not stream.is_muted),
|
|
)
|
|
|
|
device_menu = DeviceMenu(_type=_type)
|
|
|
|
scale = MaterialVolumeSlider(
|
|
stream=stream,
|
|
on_change=lambda x: stream.set_volume(x.value),
|
|
sensitive=stream.bind("is_muted", lambda value: not value),
|
|
)
|
|
|
|
arrow = Widget.Button(
|
|
child=Widget.Arrow(pixel_size=20, rotated=device_menu.bind("reveal_child")),
|
|
css_classes=["material-slider-arrow", "hover-surface"],
|
|
on_click=lambda x: device_menu.toggle(),
|
|
)
|
|
super().__init__(
|
|
vertical=True,
|
|
child=[
|
|
Widget.Box(child=[icon, scale, arrow]),
|
|
device_menu,
|
|
],
|
|
css_classes=[f"volume-mainbox-{_type}"],
|
|
)
|