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

125 lines
5.1 KiB
Python

"""General Settings tab: system-wide defaults (default output/input device)
and this panel's own display settings, plus a read-only line confirming
what audio server pactl is actually talking to (pipewire-pulse vs a legacy
PulseAudio daemon) — the whole point of the "build for the future" ask."""
from __future__ import annotations
import gi
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk # noqa: E402
import config
class GeneralTab(Gtk.Box):
def __init__(self, backend, on_hologram_changed=None) -> None:
super().__init__(orientation=Gtk.Orientation.VERTICAL, spacing=16)
self.add_css_class("sb-tab-page")
self.add_css_class("sb-general")
self._backend = backend
self._on_hologram_changed = on_hologram_changed
self._sink_names: list[str] = []
self._source_names: list[str] = []
self._guard = False
self.append(self._field("Default Output Device", self._build_output_dropdown()))
self.append(self._field("Default Input Device", self._build_input_dropdown()))
if on_hologram_changed is not None:
self.append(self._build_hologram_row())
self._server_lbl = Gtk.Label(xalign=0.0)
self._server_lbl.add_css_class("sb-server-info")
self.append(self._server_lbl)
@staticmethod
def _field(title: str, widget: Gtk.Widget) -> Gtk.Widget:
col = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
col.add_css_class("sb-general-field")
label = Gtk.Label(label=title, xalign=0.0)
label.add_css_class("sb-field-label")
col.append(label)
col.append(widget)
return col
def _build_output_dropdown(self) -> Gtk.DropDown:
self._output_dropdown = Gtk.DropDown()
self._output_dropdown.add_css_class("sb-device-dropdown")
self._output_dropdown.connect("notify::selected", self._on_output_selected)
return self._output_dropdown
def _build_input_dropdown(self) -> Gtk.DropDown:
self._input_dropdown = Gtk.DropDown()
self._input_dropdown.add_css_class("sb-device-dropdown")
self._input_dropdown.connect("notify::selected", self._on_input_selected)
return self._input_dropdown
def _build_hologram_row(self) -> Gtk.Widget:
row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
row.add_css_class("sb-general-field")
label = Gtk.Label(label="Hologram Effects", xalign=0.0, hexpand=True)
label.add_css_class("sb-field-label")
row.append(label)
switch = Gtk.Switch(valign=Gtk.Align.CENTER)
switch.set_active(config.hologram_enabled())
switch.connect("state-set", self._on_hologram_toggled)
row.append(switch)
return row
def _on_hologram_toggled(self, _switch: Gtk.Switch, value: bool) -> bool:
config.set_hologram_enabled(value)
if self._on_hologram_changed is not None:
self._on_hologram_changed(value)
return False
# -- data -----------------------------------------------------------------------
def refresh(self) -> None:
def got_server(info: dict) -> None:
name = info.get("server_name") or "unknown"
version = info.get("server_version") or ""
self._server_lbl.set_label(f"Audio server: {name} {version}".rstrip())
def got_sinks(sinks: list[dict]) -> None:
def got_sources(sources: list[dict]) -> None:
self._sync_dropdown(self._output_dropdown, sinks, info.get("default_sink", ""), "_sink_names")
self._sync_dropdown(self._input_dropdown, sources, info.get("default_source", ""), "_source_names")
self._backend.list_sources(got_sources)
self._backend.list_sinks(got_sinks)
self._backend.get_server_info(got_server)
def _sync_dropdown(self, dropdown: Gtk.DropDown, devices: list[dict],
default_name: str, cache_attr: str) -> None:
names = [d["name"] for d in devices]
if names != getattr(self, cache_attr):
setattr(self, cache_attr, names)
self._guard = True
dropdown.set_model(Gtk.StringList.new([d["description"] for d in devices]))
self._guard = False
idx = names.index(default_name) if default_name in names else None
if idx is not None and dropdown.get_selected() != idx:
self._guard = True
dropdown.set_selected(idx)
self._guard = False
def _on_output_selected(self, dropdown: Gtk.DropDown, _pspec) -> None:
if self._guard:
return
idx = dropdown.get_selected()
if 0 <= idx < len(self._sink_names):
self._backend.set_default_sink(self._sink_names[idx])
def _on_input_selected(self, dropdown: Gtk.DropDown, _pspec) -> None:
if self._guard:
return
idx = dropdown.get_selected()
if 0 <= idx < len(self._source_names):
self._backend.set_default_source(self._source_names[idx])
def activate(self) -> None:
self.refresh()
def deactivate(self) -> None:
pass