Dotfiles/desktopenvs/hyprlua/audio-panel/ui/scroll_row.py

67 lines
2.4 KiB
Python

"""ScrollRow — a horizontally-scrolling row of id-keyed cards, diffed in place
on every refresh instead of torn down and rebuilt. That matters here for two
reasons: rebuilding would fight an in-progress VolumeMeter drag (see that
widget's own drag-guard), and it would tear down/respawn each card's live
`parec` peak-monitor process on every single `pactl subscribe` event instead
of only when a device/app actually appears or disappears.
"""
from __future__ import annotations
import gi
gi.require_version("Gtk", "4.0")
from gi.repository import Gtk # noqa: E402
class ScrollRow(Gtk.ScrolledWindow):
def __init__(self, make_card, empty_text: str = "Nothing here") -> None:
super().__init__()
self.set_policy(Gtk.PolicyType.EXTERNAL, Gtk.PolicyType.NEVER)
self.add_css_class("sb-scroll-row")
self._make_card = make_card
self._row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=14)
self._row.add_css_class("sb-row")
self.set_child(self._row)
self._cards: dict = {}
self._empty_label = Gtk.Label(label=empty_text)
self._empty_label.add_css_class("sb-empty")
self._active = False
def sync(self, items: list[dict], *extra) -> None:
seen = set()
for item in items:
iid = item["id"]
seen.add(iid)
card = self._cards.get(iid)
if card is None:
card = self._make_card(item, *extra)
self._cards[iid] = card
self._row.append(card)
if self._active:
card.start_peak()
else:
card.update(item, *extra)
for iid in [i for i in self._cards if i not in seen]:
card = self._cards.pop(iid)
card.stop_peak()
self._row.remove(card)
self._sync_empty_state()
def _sync_empty_state(self) -> None:
empty = not self._cards
if empty and self._empty_label.get_parent() is None:
self._row.append(self._empty_label)
elif not empty and self._empty_label.get_parent() is not None:
self._row.remove(self._empty_label)
def activate(self) -> None:
self._active = True
for card in self._cards.values():
card.start_peak()
def deactivate(self) -> None:
self._active = False
for card in self._cards.values():
card.stop_peak()