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

Popup window for selecting characters.

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

Called when the popup is canceled.

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