"""Catalogue picker: switch between SomaFM, tilderadio and whatever comes next.""" from __future__ import annotations from rich.console import Group from rich.text import Text from textual.app import ComposeResult from textual.screen import ModalScreen from textual.widgets import Static from ..providers import PROVIDERS, available_providers from .common import SPECTRUM_STOPS, gradient, truncate class ProviderScreen(ModalScreen[str | None]): """A list of catalogues; enter selects, esc leaves everything as it was.""" def __init__(self, current_id: str = "") -> None: super().__init__() self.slugs: tuple[str, ...] = available_providers() self.current_id = current_id self.index = self.slugs.index(current_id) if current_id in self.slugs else 0 def compose(self) -> ComposeResult: yield Static(self._body(), id="providers") def _body(self) -> Group: header = Text() header.append("◤ ", style=f"bold {gradient(SPECTRUM_STOPS, 0.9)}") header.append("CATALOGUES", style=f"bold {gradient(SPECTRUM_STOPS, 0.55)}") header.append(" · pick a station catalogue", style="dim") name_width = ( max( ( len(PROVIDERS[slug].name) for slug in self.slugs if slug in PROVIDERS ), default=12, ) + 2 ) rows: list[Text] = [] for index, slug in enumerate(self.slugs): provider = PROVIDERS[slug] selected = index == self.index row = Text(no_wrap=True) row.append( "▶ " if selected else " ", style="bold magenta" if selected else "" ) row.append(f"{index + 1:>2} ", style="dim") row.append( f"{truncate(provider.name, name_width - 2):<{name_width}}", style="bold underline" if selected else "bold", ) row.append(truncate(provider.brand, 14), style="dim") if slug == self.current_id: row.append(" ●", style="bold green") rows.append(row) if selected: rows.append( Text( " " + truncate(provider.tagline, 54), style="italic dim", no_wrap=True, ) ) current = PROVIDERS.get(self.current_id) footer = Text() footer.append("\n") if current is not None: footer.append( f"now: {current.name} — {current.station_source}\n", style="dim" ) footer.append( "↑ ↓ choose · enter switch · esc cancel", style="dim", ) return Group(header, Text(""), *rows, footer) # -- interaction ----------------------------------------------------- # def _move(self, delta: int) -> None: self.index = (self.index + delta) % len(self.slugs) self.query_one("#providers", Static).update(self._body()) def on_key(self, event) -> None: key = event.key if key in ("up", "k", "shift+tab"): self._move(-1) elif key in ("down", "j", "tab"): self._move(1) elif key in ("enter", "space"): self.dismiss(self.slugs[self.index]) elif key == "escape" or key == "q": self.dismiss(None) elif key.isdigit() and key != "0": choice = int(key) - 1 if 0 <= choice < len(self.slugs): self.dismiss(self.slugs[choice]) else: return event.stop()