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

Update appearances to make the provided tab appear selected.