from __future__ import annotations from typing import List, Tuple from PIL import ImageDraw from .base import SelectableList from .item import ListItem class ListView(SelectableList): """Compact scrolling list – shows up to *max_visible* items at once. The visible window is kept centred on the selected item and scrolls as the selection moves. ``font_size`` is forwarded to :pymeth:`ListItem.render` so the underlying render function can use it (e.g. for PIL ``draw.text``). Pass ``None`` to let render functions use their own default. """ def __init__(self, *args, font_size: int | None = None, **kwargs): super().__init__(*args, **kwargs) self.font_size = font_size def _get_visible_window(self) -> Tuple[List[ListItem], int]: """Return (visible_items, start_offset).""" total = len(self._items) if total <= self.max_visible: return self._items, 0 start = max(0, self.selected_index - self.max_visible // 2) start = min(start, total - self.max_visible) end = start + self.max_visible return self._items[start:end], start def render(self, draw: ImageDraw.ImageDraw, y_start: int) -> None: if not self._items: return visible_items, start = self._get_visible_window() fs = self.font_size or 10 kwargs = {} if self.font_size is None else {'font_size': self.font_size} for idx, item in enumerate(visible_items): actual_idx = start + idx y_pos = y_start + idx * self.item_height if actual_idx == self.selected_index: self._draw_highlight(draw, self.x_offset, y_pos + fs // 2, font_size=fs) item.render(draw, (self.x_offset, y_pos), fill='white', **kwargs) else: item.render(draw, (self.x_offset, y_pos), fill='black', **kwargs)