Dotfiles/desktopenvs/hyprdrive/supersonic-booster/ui/app_card.py

62 lines
2.4 KiB
Python

"""ApplicationCard — one card in the Applications tab: everything MeterCard
gives every card (icon+name, live peak bar, volume meter/L-R pair, mute),
plus a dropdown to route this one app's playback stream to a different
output device (`pactl move-sink-input`)."""
from __future__ import annotations
import gi
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk # noqa: E402
from ui.card_base import MeterCard
class ApplicationCard(MeterCard):
def __init__(self, backend, peaks, item: dict, sinks: list[dict]) -> None:
self._sinks = sinks
self._sink_names: list[str] = []
super().__init__(backend, peaks, "sink-input", item)
def _build_extra(self, item: dict):
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=2)
label = Gtk.Label(label="Output", xalign=0.0)
label.add_css_class("sb-field-label")
box.append(label)
self._sink_dropdown = Gtk.DropDown()
self._sink_dropdown.add_css_class("sb-device-dropdown")
self._dropdown_guard = False
self._sink_dropdown.connect("notify::selected", self._on_sink_selected)
box.append(self._sink_dropdown)
self._sync_sink_dropdown(item, self._sinks)
return box
def _update_extra(self, item: dict, sinks: list[dict]) -> None:
self._sinks = sinks
self._sync_sink_dropdown(item, sinks)
def _sync_sink_dropdown(self, item: dict, sinks: list[dict]) -> None:
names = [s["name"] for s in sinks]
if names != self._sink_names:
self._sink_names = names
self._dropdown_guard = True
self._sink_dropdown.set_model(Gtk.StringList.new([s["description"] for s in sinks]))
self._dropdown_guard = False
current = item.get("device")
idx = next((i for i, s in enumerate(sinks) if s["id"] == current), None)
if idx is not None and self._sink_dropdown.get_selected() != idx:
self._dropdown_guard = True
self._sink_dropdown.set_selected(idx)
self._dropdown_guard = False
def _on_sink_selected(self, dropdown: Gtk.DropDown, _pspec) -> None:
if self._dropdown_guard:
return
idx = dropdown.get_selected()
if 0 <= idx < len(self._sink_names):
self._backend.move_sink_input(self._id, self._sink_names[idx])
def _peak_target(self, item: dict) -> list[str] | None:
return [f"--monitor-stream={item['id']}"]