bastd.ui.tabs

UI functionality for creating tab style buttons.

 1# Released under the MIT License. See LICENSE for details.
 2#
 3"""UI functionality for creating tab style buttons."""
 4
 5from __future__ import annotations
 6
 7from dataclasses import dataclass
 8from typing import TYPE_CHECKING, TypeVar, Generic
 9
10import ba
11
12if TYPE_CHECKING:
13    from typing import Any, Callable
14
15
16@dataclass
17class Tab:
18    """Info for an individual tab in a TabRow"""
19    button: ba.Widget
20    position: tuple[float, float]
21    size: tuple[float, float]
22
23
24T = TypeVar('T')
25
26
27class TabRow(Generic[T]):
28    """Encapsulates a row of tab-styled buttons.
29
30    Tabs are indexed by id which is an arbitrary user-provided type.
31    """
32
33    def __init__(self,
34                 parent: ba.Widget,
35                 tabdefs: list[tuple[T, ba.Lstr]],
36                 pos: tuple[float, float],
37                 size: tuple[float, float],
38                 on_select_call: Callable[[T], None] | None = None) -> None:
39        if not tabdefs:
40            raise ValueError('At least one tab def is required')
41        self.tabs: dict[T, Tab] = {}
42        tab_pos_v = pos[1]
43        tab_button_width = float(size[0]) / len(tabdefs)
44        tab_spacing = (250.0 - tab_button_width) * 0.06
45        h = pos[0]
46        for tab_id, tab_label in tabdefs:
47            pos = (h + tab_spacing * 0.5, tab_pos_v)
48            size = (tab_button_width - tab_spacing, 50.0)
49            btn = ba.buttonwidget(parent=parent,
50                                  position=pos,
51                                  autoselect=True,
52                                  button_type='tab',
53                                  size=size,
54                                  label=tab_label,
55                                  enable_sound=False,
56                                  on_activate_call=ba.Call(
57                                      self._tick_and_call, on_select_call,
58                                      tab_id))
59            h += tab_button_width
60            self.tabs[tab_id] = Tab(button=btn, position=pos, size=size)
61
62    def update_appearance(self, selected_tab_id: T) -> None:
63        """Update appearances to make the provided tab appear selected."""
64        for tab_id, tab in self.tabs.items():
65            if tab_id == selected_tab_id:
66                ba.buttonwidget(edit=tab.button,
67                                color=(0.5, 0.4, 0.93),
68                                textcolor=(0.85, 0.75, 0.95))  # lit
69            else:
70                ba.buttonwidget(edit=tab.button,
71                                color=(0.52, 0.48, 0.63),
72                                textcolor=(0.65, 0.6, 0.7))  # unlit
73
74    def _tick_and_call(self, call: Callable[[Any], None] | None,
75                       arg: Any) -> None:
76        ba.playsound(ba.getsound('click01'))
77        if call is not None:
78            call(arg)
@dataclass
class Tab:
17@dataclass
18class Tab:
19    """Info for an individual tab in a TabRow"""
20    button: ba.Widget
21    position: tuple[float, float]
22    size: tuple[float, float]

Info for an individual tab in a TabRow

Tab( button: _ba.Widget, position: tuple[float, float], size: tuple[float, float])
class TabRow(typing.Generic[~T]):
28class TabRow(Generic[T]):
29    """Encapsulates a row of tab-styled buttons.
30
31    Tabs are indexed by id which is an arbitrary user-provided type.
32    """
33
34    def __init__(self,
35                 parent: ba.Widget,
36                 tabdefs: list[tuple[T, ba.Lstr]],
37                 pos: tuple[float, float],
38                 size: tuple[float, float],
39                 on_select_call: Callable[[T], None] | None = None) -> None:
40        if not tabdefs:
41            raise ValueError('At least one tab def is required')
42        self.tabs: dict[T, Tab] = {}
43        tab_pos_v = pos[1]
44        tab_button_width = float(size[0]) / len(tabdefs)
45        tab_spacing = (250.0 - tab_button_width) * 0.06
46        h = pos[0]
47        for tab_id, tab_label in tabdefs:
48            pos = (h + tab_spacing * 0.5, tab_pos_v)
49            size = (tab_button_width - tab_spacing, 50.0)
50            btn = ba.buttonwidget(parent=parent,
51                                  position=pos,
52                                  autoselect=True,
53                                  button_type='tab',
54                                  size=size,
55                                  label=tab_label,
56                                  enable_sound=False,
57                                  on_activate_call=ba.Call(
58                                      self._tick_and_call, on_select_call,
59                                      tab_id))
60            h += tab_button_width
61            self.tabs[tab_id] = Tab(button=btn, position=pos, size=size)
62
63    def update_appearance(self, selected_tab_id: T) -> None:
64        """Update appearances to make the provided tab appear selected."""
65        for tab_id, tab in self.tabs.items():
66            if tab_id == selected_tab_id:
67                ba.buttonwidget(edit=tab.button,
68                                color=(0.5, 0.4, 0.93),
69                                textcolor=(0.85, 0.75, 0.95))  # lit
70            else:
71                ba.buttonwidget(edit=tab.button,
72                                color=(0.52, 0.48, 0.63),
73                                textcolor=(0.65, 0.6, 0.7))  # unlit
74
75    def _tick_and_call(self, call: Callable[[Any], None] | None,
76                       arg: Any) -> None:
77        ba.playsound(ba.getsound('click01'))
78        if call is not None:
79            call(arg)

Encapsulates a row of tab-styled buttons.

Tabs are indexed by id which is an arbitrary user-provided type.

TabRow( parent: _ba.Widget, tabdefs: list[tuple[~T, ba._language.Lstr]], pos: tuple[float, float], size: tuple[float, float], on_select_call: Optional[Callable[[~T], NoneType]] = None)
34    def __init__(self,
35                 parent: ba.Widget,
36                 tabdefs: list[tuple[T, ba.Lstr]],
37                 pos: tuple[float, float],
38                 size: tuple[float, float],
39                 on_select_call: Callable[[T], None] | None = None) -> None:
40        if not tabdefs:
41            raise ValueError('At least one tab def is required')
42        self.tabs: dict[T, Tab] = {}
43        tab_pos_v = pos[1]
44        tab_button_width = float(size[0]) / len(tabdefs)
45        tab_spacing = (250.0 - tab_button_width) * 0.06
46        h = pos[0]
47        for tab_id, tab_label in tabdefs:
48            pos = (h + tab_spacing * 0.5, tab_pos_v)
49            size = (tab_button_width - tab_spacing, 50.0)
50            btn = ba.buttonwidget(parent=parent,
51                                  position=pos,
52                                  autoselect=True,
53                                  button_type='tab',
54                                  size=size,
55                                  label=tab_label,
56                                  enable_sound=False,
57                                  on_activate_call=ba.Call(
58                                      self._tick_and_call, on_select_call,
59                                      tab_id))
60            h += tab_button_width
61            self.tabs[tab_id] = Tab(button=btn, position=pos, size=size)
def update_appearance(self, selected_tab_id: ~T) -> None:
63    def update_appearance(self, selected_tab_id: T) -> None:
64        """Update appearances to make the provided tab appear selected."""
65        for tab_id, tab in self.tabs.items():
66            if tab_id == selected_tab_id:
67                ba.buttonwidget(edit=tab.button,
68                                color=(0.5, 0.4, 0.93),
69                                textcolor=(0.85, 0.75, 0.95))  # lit
70            else:
71                ba.buttonwidget(edit=tab.button,
72                                color=(0.52, 0.48, 0.63),
73                                textcolor=(0.65, 0.6, 0.7))  # unlit

Update appearances to make the provided tab appear selected.