"""Header: logo, clock and a live ticker.""" from __future__ import annotations from typing import TYPE_CHECKING, cast from rich.cells import cell_len from rich.console import Group, RenderableType from rich.text import Text from textual.widget import Widget from .common import ( SPECTRUM_STOPS, clock_time, date_label, gradient, marquee, state_glyph, ) if TYPE_CHECKING: from ..app import BeefyPlayerApp LOGO = ( "▄▀▀▀ ▄▀▀▄ █▄ ▄█ ▄▀▀▄", "▀▀▀▄ █ █ █ █ █ █▀▀█", "▄▄▄▀ ▀▄▄▀ █ █ █ █", ) MIDDLE = ("SOMA FM RADIO", "LISTENER SUPPORTED", "SAN FRANCISCO") class HeaderBar(Widget): can_focus = False def render(self) -> RenderableType: app = cast("BeefyPlayerApp", self.app) width = max(24, self.size.width) height = max(1, self.size.height) state = app.playing_state glyph, colour = state_glyph(state) logo_width = max(cell_len(row) for row in LOGO) middle_width = max(cell_len(row) for row in MIDDLE) right = (clock_time(), date_label(), f"vol {app.volume}%") right_width = max(cell_len(item) for item in right) show_middle = width >= logo_width + middle_width + 8 show_right = width >= logo_width + right_width + 8 if show_middle and show_right: show_middle = width >= logo_width + 4 + middle_width + 2 + right_width lines: list[Text] = [] for index, row in enumerate(LOGO): line = Text(no_wrap=True) for column, char in enumerate(row): position = ( column / max(1, logo_width - 1) + index * 0.055 ) % 1.0 line.append( char, style=f"bold {gradient(SPECTRUM_STOPS, position)}" ) if show_middle: line.append(" " * 4) middle = MIDDLE[index] line.append(middle, style="dim") # Keep the right block on one column even though the middle # lines differ in length ("SOMA FM RADIO" is shorter than # "LISTENER SUPPORTED"). line.append(" " * max(0, middle_width - cell_len(middle))) if show_right: used = logo_width if show_middle: used += 4 + middle_width # Right-align the block *and* every item inside it: the clock, # the date and the volume each keep their own length, so pad # the shortest ones instead of left-aligning the whole block. pad = max(1, width - used - right_width) item = right[index] line.append(" " * pad) line.append(" " * max(0, right_width - cell_len(item))) style = "bold" if index == 0 else "dim" line.append(item, style=style) lines.append(line) if height >= 4: ticker = Text(no_wrap=True) prefix = f"{glyph} {state.upper():<8} " ticker.append(prefix, style=f"bold {colour}") station = app.current if app.notice: text = app.notice text_style = "italic yellow" elif station is not None: song = station.now_playing if song is not None: text = f"{station.title} · {song.display_artist} — {song.display_title}" else: text = f"{station.title} · {station.description}" text_style = "" else: text = "Pick a station — press enter to tune in" text_style = "italic dim" ticker.append(marquee(text, max(4, width - cell_len(prefix))), style=text_style) lines.append(ticker) return Group(*lines[:height])