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