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

Called when the popup is canceled.

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