bauiv1lib.characterpicker

Provides a picker for characters.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides a picker for characters."""
  4
  5from __future__ import annotations
  6
  7import math
  8from typing import TYPE_CHECKING
  9
 10from typing_extensions import override
 11
 12from bauiv1lib.popup import PopupWindow
 13import bauiv1 as bui
 14
 15if TYPE_CHECKING:
 16    from typing import Any, Sequence
 17
 18
 19class CharacterPicker(PopupWindow):
 20    """Popup window for selecting characters."""
 21
 22    def __init__(
 23        self,
 24        parent: bui.Widget,
 25        position: tuple[float, float] = (0.0, 0.0),
 26        delegate: Any = None,
 27        scale: float | None = None,
 28        offset: tuple[float, float] = (0.0, 0.0),
 29        tint_color: Sequence[float] = (1.0, 1.0, 1.0),
 30        tint2_color: Sequence[float] = (1.0, 1.0, 1.0),
 31        selected_character: str | None = None,
 32    ):
 33        # pylint: disable=too-many-locals
 34        from bascenev1lib.actor import spazappearance
 35
 36        assert bui.app.classic is not None
 37
 38        del parent  # unused here
 39        uiscale = bui.app.ui_v1.uiscale
 40        if scale is None:
 41            scale = (
 42                1.85
 43                if uiscale is bui.UIScale.SMALL
 44                else 1.65
 45                if uiscale is bui.UIScale.MEDIUM
 46                else 1.23
 47            )
 48
 49        self._delegate = delegate
 50        self._transitioning_out = False
 51
 52        # make a list of spaz icons
 53        self._spazzes = spazappearance.get_appearances()
 54        self._spazzes.sort()
 55        self._icon_textures = [
 56            bui.gettexture(bui.app.classic.spaz_appearances[s].icon_texture)
 57            for s in self._spazzes
 58        ]
 59        self._icon_tint_textures = [
 60            bui.gettexture(
 61                bui.app.classic.spaz_appearances[s].icon_mask_texture
 62            )
 63            for s in self._spazzes
 64        ]
 65
 66        count = len(self._spazzes)
 67
 68        columns = 3
 69        rows = int(math.ceil(float(count) / columns))
 70
 71        button_width = 100
 72        button_height = 100
 73        button_buffer_h = 10
 74        button_buffer_v = 15
 75
 76        self._width = 10 + columns * (button_width + 2 * button_buffer_h) * (
 77            1.0 / 0.95
 78        ) * (1.0 / 0.8)
 79        self._height = self._width * (
 80            0.8 if uiscale is bui.UIScale.SMALL else 1.06
 81        )
 82
 83        self._scroll_width = self._width * 0.8
 84        self._scroll_height = self._height * 0.8
 85        self._scroll_position = (
 86            (self._width - self._scroll_width) * 0.5,
 87            (self._height - self._scroll_height) * 0.5,
 88        )
 89
 90        # Creates our _root_widget.
 91        super().__init__(
 92            position=position,
 93            size=(self._width, self._height),
 94            scale=scale,
 95            bg_color=(0.5, 0.5, 0.5),
 96            offset=offset,
 97            focus_position=self._scroll_position,
 98            focus_size=(self._scroll_width, self._scroll_height),
 99        )
100
101        self._scrollwidget = bui.scrollwidget(
102            parent=self.root_widget,
103            size=(self._scroll_width, self._scroll_height),
104            color=(0.55, 0.55, 0.55),
105            highlight=False,
106            position=self._scroll_position,
107        )
108        bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
109
110        self._sub_width = self._scroll_width * 0.95
111        self._sub_height = (
112            5 + rows * (button_height + 2 * button_buffer_v) + 100
113        )
114        self._subcontainer = bui.containerwidget(
115            parent=self._scrollwidget,
116            size=(self._sub_width, self._sub_height),
117            background=False,
118        )
119        index = 0
120        mask_texture = bui.gettexture('characterIconMask')
121        for y in range(rows):
122            for x in range(columns):
123                pos = (
124                    x * (button_width + 2 * button_buffer_h) + button_buffer_h,
125                    self._sub_height
126                    - (y + 1) * (button_height + 2 * button_buffer_v)
127                    + 12,
128                )
129                btn = bui.buttonwidget(
130                    parent=self._subcontainer,
131                    button_type='square',
132                    size=(button_width, button_height),
133                    autoselect=True,
134                    texture=self._icon_textures[index],
135                    tint_texture=self._icon_tint_textures[index],
136                    mask_texture=mask_texture,
137                    label='',
138                    color=(1, 1, 1),
139                    tint_color=tint_color,
140                    tint2_color=tint2_color,
141                    on_activate_call=bui.Call(
142                        self._select_character, self._spazzes[index]
143                    ),
144                    position=pos,
145                )
146                bui.widget(edit=btn, show_buffer_top=60, show_buffer_bottom=60)
147                if self._spazzes[index] == selected_character:
148                    bui.containerwidget(
149                        edit=self._subcontainer,
150                        selected_child=btn,
151                        visible_child=btn,
152                    )
153                name = bui.Lstr(
154                    translate=('characterNames', self._spazzes[index])
155                )
156                bui.textwidget(
157                    parent=self._subcontainer,
158                    text=name,
159                    position=(pos[0] + button_width * 0.5, pos[1] - 12),
160                    size=(0, 0),
161                    scale=0.5,
162                    maxwidth=button_width,
163                    draw_controller=btn,
164                    h_align='center',
165                    v_align='center',
166                    color=(0.8, 0.8, 0.8, 0.8),
167                )
168                index += 1
169
170                if index >= count:
171                    break
172            if index >= count:
173                break
174        self._get_more_characters_button = btn = bui.buttonwidget(
175            parent=self._subcontainer,
176            size=(self._sub_width * 0.8, 60),
177            position=(self._sub_width * 0.1, 30),
178            label=bui.Lstr(resource='editProfileWindow.getMoreCharactersText'),
179            on_activate_call=self._on_store_press,
180            color=(0.6, 0.6, 0.6),
181            textcolor=(0.8, 0.8, 0.8),
182            autoselect=True,
183        )
184        bui.widget(edit=btn, show_buffer_top=30, show_buffer_bottom=30)
185
186    def _on_store_press(self) -> None:
187        from bauiv1lib.account import show_sign_in_prompt
188        from bauiv1lib.store.browser import StoreBrowserWindow
189
190        plus = bui.app.plus
191        assert plus is not None
192
193        if plus.get_v1_account_state() != 'signed_in':
194            show_sign_in_prompt()
195            return
196        self._transition_out()
197        StoreBrowserWindow(
198            modal=True,
199            show_tab=StoreBrowserWindow.TabID.CHARACTERS,
200            origin_widget=self._get_more_characters_button,
201        )
202
203    def _select_character(self, character: str) -> None:
204        if self._delegate is not None:
205            self._delegate.on_character_picker_pick(character)
206        self._transition_out()
207
208    def _transition_out(self) -> None:
209        if not self._transitioning_out:
210            self._transitioning_out = True
211            bui.containerwidget(edit=self.root_widget, transition='out_scale')
212
213    @override
214    def on_popup_cancel(self) -> None:
215        bui.getsound('swish').play()
216        self._transition_out()
class CharacterPicker(bauiv1lib.popup.PopupWindow):
 20class CharacterPicker(PopupWindow):
 21    """Popup window for selecting characters."""
 22
 23    def __init__(
 24        self,
 25        parent: bui.Widget,
 26        position: tuple[float, float] = (0.0, 0.0),
 27        delegate: Any = None,
 28        scale: float | None = None,
 29        offset: tuple[float, float] = (0.0, 0.0),
 30        tint_color: Sequence[float] = (1.0, 1.0, 1.0),
 31        tint2_color: Sequence[float] = (1.0, 1.0, 1.0),
 32        selected_character: str | None = None,
 33    ):
 34        # pylint: disable=too-many-locals
 35        from bascenev1lib.actor import spazappearance
 36
 37        assert bui.app.classic is not None
 38
 39        del parent  # unused here
 40        uiscale = bui.app.ui_v1.uiscale
 41        if scale is None:
 42            scale = (
 43                1.85
 44                if uiscale is bui.UIScale.SMALL
 45                else 1.65
 46                if uiscale is bui.UIScale.MEDIUM
 47                else 1.23
 48            )
 49
 50        self._delegate = delegate
 51        self._transitioning_out = False
 52
 53        # make a list of spaz icons
 54        self._spazzes = spazappearance.get_appearances()
 55        self._spazzes.sort()
 56        self._icon_textures = [
 57            bui.gettexture(bui.app.classic.spaz_appearances[s].icon_texture)
 58            for s in self._spazzes
 59        ]
 60        self._icon_tint_textures = [
 61            bui.gettexture(
 62                bui.app.classic.spaz_appearances[s].icon_mask_texture
 63            )
 64            for s in self._spazzes
 65        ]
 66
 67        count = len(self._spazzes)
 68
 69        columns = 3
 70        rows = int(math.ceil(float(count) / columns))
 71
 72        button_width = 100
 73        button_height = 100
 74        button_buffer_h = 10
 75        button_buffer_v = 15
 76
 77        self._width = 10 + columns * (button_width + 2 * button_buffer_h) * (
 78            1.0 / 0.95
 79        ) * (1.0 / 0.8)
 80        self._height = self._width * (
 81            0.8 if uiscale is bui.UIScale.SMALL else 1.06
 82        )
 83
 84        self._scroll_width = self._width * 0.8
 85        self._scroll_height = self._height * 0.8
 86        self._scroll_position = (
 87            (self._width - self._scroll_width) * 0.5,
 88            (self._height - self._scroll_height) * 0.5,
 89        )
 90
 91        # Creates our _root_widget.
 92        super().__init__(
 93            position=position,
 94            size=(self._width, self._height),
 95            scale=scale,
 96            bg_color=(0.5, 0.5, 0.5),
 97            offset=offset,
 98            focus_position=self._scroll_position,
 99            focus_size=(self._scroll_width, self._scroll_height),
100        )
101
102        self._scrollwidget = bui.scrollwidget(
103            parent=self.root_widget,
104            size=(self._scroll_width, self._scroll_height),
105            color=(0.55, 0.55, 0.55),
106            highlight=False,
107            position=self._scroll_position,
108        )
109        bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
110
111        self._sub_width = self._scroll_width * 0.95
112        self._sub_height = (
113            5 + rows * (button_height + 2 * button_buffer_v) + 100
114        )
115        self._subcontainer = bui.containerwidget(
116            parent=self._scrollwidget,
117            size=(self._sub_width, self._sub_height),
118            background=False,
119        )
120        index = 0
121        mask_texture = bui.gettexture('characterIconMask')
122        for y in range(rows):
123            for x in range(columns):
124                pos = (
125                    x * (button_width + 2 * button_buffer_h) + button_buffer_h,
126                    self._sub_height
127                    - (y + 1) * (button_height + 2 * button_buffer_v)
128                    + 12,
129                )
130                btn = bui.buttonwidget(
131                    parent=self._subcontainer,
132                    button_type='square',
133                    size=(button_width, button_height),
134                    autoselect=True,
135                    texture=self._icon_textures[index],
136                    tint_texture=self._icon_tint_textures[index],
137                    mask_texture=mask_texture,
138                    label='',
139                    color=(1, 1, 1),
140                    tint_color=tint_color,
141                    tint2_color=tint2_color,
142                    on_activate_call=bui.Call(
143                        self._select_character, self._spazzes[index]
144                    ),
145                    position=pos,
146                )
147                bui.widget(edit=btn, show_buffer_top=60, show_buffer_bottom=60)
148                if self._spazzes[index] == selected_character:
149                    bui.containerwidget(
150                        edit=self._subcontainer,
151                        selected_child=btn,
152                        visible_child=btn,
153                    )
154                name = bui.Lstr(
155                    translate=('characterNames', self._spazzes[index])
156                )
157                bui.textwidget(
158                    parent=self._subcontainer,
159                    text=name,
160                    position=(pos[0] + button_width * 0.5, pos[1] - 12),
161                    size=(0, 0),
162                    scale=0.5,
163                    maxwidth=button_width,
164                    draw_controller=btn,
165                    h_align='center',
166                    v_align='center',
167                    color=(0.8, 0.8, 0.8, 0.8),
168                )
169                index += 1
170
171                if index >= count:
172                    break
173            if index >= count:
174                break
175        self._get_more_characters_button = btn = bui.buttonwidget(
176            parent=self._subcontainer,
177            size=(self._sub_width * 0.8, 60),
178            position=(self._sub_width * 0.1, 30),
179            label=bui.Lstr(resource='editProfileWindow.getMoreCharactersText'),
180            on_activate_call=self._on_store_press,
181            color=(0.6, 0.6, 0.6),
182            textcolor=(0.8, 0.8, 0.8),
183            autoselect=True,
184        )
185        bui.widget(edit=btn, show_buffer_top=30, show_buffer_bottom=30)
186
187    def _on_store_press(self) -> None:
188        from bauiv1lib.account import show_sign_in_prompt
189        from bauiv1lib.store.browser import StoreBrowserWindow
190
191        plus = bui.app.plus
192        assert plus is not None
193
194        if plus.get_v1_account_state() != 'signed_in':
195            show_sign_in_prompt()
196            return
197        self._transition_out()
198        StoreBrowserWindow(
199            modal=True,
200            show_tab=StoreBrowserWindow.TabID.CHARACTERS,
201            origin_widget=self._get_more_characters_button,
202        )
203
204    def _select_character(self, character: str) -> None:
205        if self._delegate is not None:
206            self._delegate.on_character_picker_pick(character)
207        self._transition_out()
208
209    def _transition_out(self) -> None:
210        if not self._transitioning_out:
211            self._transitioning_out = True
212            bui.containerwidget(edit=self.root_widget, transition='out_scale')
213
214    @override
215    def on_popup_cancel(self) -> None:
216        bui.getsound('swish').play()
217        self._transition_out()

Popup window for selecting characters.

CharacterPicker( parent: _bauiv1.Widget, position: tuple[float, float] = (0.0, 0.0), delegate: Any = None, scale: float | None = None, offset: tuple[float, float] = (0.0, 0.0), tint_color: Sequence[float] = (1.0, 1.0, 1.0), tint2_color: Sequence[float] = (1.0, 1.0, 1.0), selected_character: str | None = None)
 23    def __init__(
 24        self,
 25        parent: bui.Widget,
 26        position: tuple[float, float] = (0.0, 0.0),
 27        delegate: Any = None,
 28        scale: float | None = None,
 29        offset: tuple[float, float] = (0.0, 0.0),
 30        tint_color: Sequence[float] = (1.0, 1.0, 1.0),
 31        tint2_color: Sequence[float] = (1.0, 1.0, 1.0),
 32        selected_character: str | None = None,
 33    ):
 34        # pylint: disable=too-many-locals
 35        from bascenev1lib.actor import spazappearance
 36
 37        assert bui.app.classic is not None
 38
 39        del parent  # unused here
 40        uiscale = bui.app.ui_v1.uiscale
 41        if scale is None:
 42            scale = (
 43                1.85
 44                if uiscale is bui.UIScale.SMALL
 45                else 1.65
 46                if uiscale is bui.UIScale.MEDIUM
 47                else 1.23
 48            )
 49
 50        self._delegate = delegate
 51        self._transitioning_out = False
 52
 53        # make a list of spaz icons
 54        self._spazzes = spazappearance.get_appearances()
 55        self._spazzes.sort()
 56        self._icon_textures = [
 57            bui.gettexture(bui.app.classic.spaz_appearances[s].icon_texture)
 58            for s in self._spazzes
 59        ]
 60        self._icon_tint_textures = [
 61            bui.gettexture(
 62                bui.app.classic.spaz_appearances[s].icon_mask_texture
 63            )
 64            for s in self._spazzes
 65        ]
 66
 67        count = len(self._spazzes)
 68
 69        columns = 3
 70        rows = int(math.ceil(float(count) / columns))
 71
 72        button_width = 100
 73        button_height = 100
 74        button_buffer_h = 10
 75        button_buffer_v = 15
 76
 77        self._width = 10 + columns * (button_width + 2 * button_buffer_h) * (
 78            1.0 / 0.95
 79        ) * (1.0 / 0.8)
 80        self._height = self._width * (
 81            0.8 if uiscale is bui.UIScale.SMALL else 1.06
 82        )
 83
 84        self._scroll_width = self._width * 0.8
 85        self._scroll_height = self._height * 0.8
 86        self._scroll_position = (
 87            (self._width - self._scroll_width) * 0.5,
 88            (self._height - self._scroll_height) * 0.5,
 89        )
 90
 91        # Creates our _root_widget.
 92        super().__init__(
 93            position=position,
 94            size=(self._width, self._height),
 95            scale=scale,
 96            bg_color=(0.5, 0.5, 0.5),
 97            offset=offset,
 98            focus_position=self._scroll_position,
 99            focus_size=(self._scroll_width, self._scroll_height),
100        )
101
102        self._scrollwidget = bui.scrollwidget(
103            parent=self.root_widget,
104            size=(self._scroll_width, self._scroll_height),
105            color=(0.55, 0.55, 0.55),
106            highlight=False,
107            position=self._scroll_position,
108        )
109        bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
110
111        self._sub_width = self._scroll_width * 0.95
112        self._sub_height = (
113            5 + rows * (button_height + 2 * button_buffer_v) + 100
114        )
115        self._subcontainer = bui.containerwidget(
116            parent=self._scrollwidget,
117            size=(self._sub_width, self._sub_height),
118            background=False,
119        )
120        index = 0
121        mask_texture = bui.gettexture('characterIconMask')
122        for y in range(rows):
123            for x in range(columns):
124                pos = (
125                    x * (button_width + 2 * button_buffer_h) + button_buffer_h,
126                    self._sub_height
127                    - (y + 1) * (button_height + 2 * button_buffer_v)
128                    + 12,
129                )
130                btn = bui.buttonwidget(
131                    parent=self._subcontainer,
132                    button_type='square',
133                    size=(button_width, button_height),
134                    autoselect=True,
135                    texture=self._icon_textures[index],
136                    tint_texture=self._icon_tint_textures[index],
137                    mask_texture=mask_texture,
138                    label='',
139                    color=(1, 1, 1),
140                    tint_color=tint_color,
141                    tint2_color=tint2_color,
142                    on_activate_call=bui.Call(
143                        self._select_character, self._spazzes[index]
144                    ),
145                    position=pos,
146                )
147                bui.widget(edit=btn, show_buffer_top=60, show_buffer_bottom=60)
148                if self._spazzes[index] == selected_character:
149                    bui.containerwidget(
150                        edit=self._subcontainer,
151                        selected_child=btn,
152                        visible_child=btn,
153                    )
154                name = bui.Lstr(
155                    translate=('characterNames', self._spazzes[index])
156                )
157                bui.textwidget(
158                    parent=self._subcontainer,
159                    text=name,
160                    position=(pos[0] + button_width * 0.5, pos[1] - 12),
161                    size=(0, 0),
162                    scale=0.5,
163                    maxwidth=button_width,
164                    draw_controller=btn,
165                    h_align='center',
166                    v_align='center',
167                    color=(0.8, 0.8, 0.8, 0.8),
168                )
169                index += 1
170
171                if index >= count:
172                    break
173            if index >= count:
174                break
175        self._get_more_characters_button = btn = bui.buttonwidget(
176            parent=self._subcontainer,
177            size=(self._sub_width * 0.8, 60),
178            position=(self._sub_width * 0.1, 30),
179            label=bui.Lstr(resource='editProfileWindow.getMoreCharactersText'),
180            on_activate_call=self._on_store_press,
181            color=(0.6, 0.6, 0.6),
182            textcolor=(0.8, 0.8, 0.8),
183            autoselect=True,
184        )
185        bui.widget(edit=btn, show_buffer_top=30, show_buffer_bottom=30)
@override
def on_popup_cancel(self) -> None:
214    @override
215    def on_popup_cancel(self) -> None:
216        bui.getsound('swish').play()
217        self._transition_out()

Called when the popup is canceled.

Cancels can occur due to clicking outside the window, hitting escape, etc.