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

1"""Visible Grid ↔ List toggle for the catalog screen. 

2 

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""" 

7 

8from __future__ import annotations 

9 

10from pathlib import Path 

11from typing import ClassVar 

12 

13from textual import events 

14from textual.binding import Binding, BindingType 

15from textual.content import Content 

16from textual.widgets import Static 

17 

18from lilbee.cli.tui import messages as msg 

19 

20_CSS_FILE = Path(__file__).parent / "grid_list_toggle.tcss" 

21 

22 

23class GridListToggle(Static, can_focus=True): 

24 """Flip the catalog between grid and list views; mirrors the ``v`` binding.""" 

25 

26 DEFAULT_CSS: ClassVar[str] = _CSS_FILE.read_text(encoding="utf-8") 

27 

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 ] 

34 

35 def __init__(self) -> None: 

36 super().__init__(id="grid-list-toggle") 

37 self._is_grid: bool = True 

38 

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() 

44 

45 def on_mount(self) -> None: 

46 self._refresh() 

47 

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)) 

53 

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") 

59 

60 def on_click(self, event: events.Click) -> None: 

61 event.stop() 

62 self._call_screen_toggle() 

63 

64 def action_flip(self) -> None: 

65 self._call_screen_toggle() 

66 

67 def action_select_grid(self) -> None: 

68 if not self._is_grid: 

69 self._call_screen_toggle() 

70 

71 def action_select_list(self) -> None: 

72 if self._is_grid: 

73 self._call_screen_toggle() 

74 

75 def _call_screen_toggle(self) -> None: 

76 from lilbee.cli.tui.screens.catalog import CatalogScreen 

77 

78 screen = self.screen 

79 if isinstance(screen, CatalogScreen): 

80 screen.action_toggle_view()