Coverage for src / lilbee / cli / tui / widgets / grid_list_toggle.py: 100%
47 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-05-15 20:55 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-05-15 20:55 +0000
1"""Visible Grid ↔ List toggle for the catalog screen.
3Click or focus+Enter on either half flips the view; keeps the existing
4``v`` keybinding. Renders as plain bold-vs-muted text with a dot
5divider, matching the Search/Chat toggle aesthetic.
6"""
8from __future__ import annotations
10from pathlib import Path
11from typing import ClassVar
13from textual import events
14from textual.binding import Binding, BindingType
15from textual.content import Content
16from textual.widgets import Static
18from lilbee.cli.tui import messages as msg
20_CSS_FILE = Path(__file__).parent / "grid_list_toggle.tcss"
23class GridListToggle(Static, can_focus=True):
24 """Flip the catalog between grid and list views; mirrors the ``v`` binding."""
26 DEFAULT_CSS: ClassVar[str] = _CSS_FILE.read_text(encoding="utf-8")
28 BINDINGS: ClassVar[list[BindingType]] = [
29 Binding("enter", "flip", "Toggle view", show=False),
30 Binding("space", "flip", "Toggle view", show=False),
31 Binding("left", "select_grid", "Grid view", show=False),
32 Binding("right", "select_list", "List view", show=False),
33 ]
35 def __init__(self) -> None:
36 super().__init__(id="grid-list-toggle")
37 self._is_grid: bool = True
39 def set_grid(self, is_grid: bool) -> None:
40 """Sync the active half to *is_grid* and repaint."""
41 self._is_grid = is_grid
42 if self.is_mounted:
43 self._refresh()
45 def on_mount(self) -> None:
46 self._refresh()
48 def _refresh(self) -> None:
49 grid_label = self._render_label(msg.CATALOG_VIEW_GRID, active=self._is_grid)
50 list_label = self._render_label(msg.CATALOG_VIEW_LIST, active=not self._is_grid)
51 divider = Content.styled(" · ", "$text-muted")
52 self.update(Content.assemble(grid_label, divider, list_label))
54 @staticmethod
55 def _render_label(label: str, *, active: bool) -> Content:
56 if active:
57 return Content.styled(label, "bold $primary")
58 return Content.styled(label, "$text-muted")
60 def on_click(self, event: events.Click) -> None:
61 event.stop()
62 self._call_screen_toggle()
64 def action_flip(self) -> None:
65 self._call_screen_toggle()
67 def action_select_grid(self) -> None:
68 if not self._is_grid:
69 self._call_screen_toggle()
71 def action_select_list(self) -> None:
72 if self._is_grid:
73 self._call_screen_toggle()
75 def _call_screen_toggle(self) -> None:
76 from lilbee.cli.tui.screens.catalog import CatalogScreen
78 screen = self.screen
79 if isinstance(screen, CatalogScreen):
80 screen.action_toggle_view()