Source code for bauiv1lib.characterpicker

# Released under the MIT License. See LICENSE for details.
#
"""Provides a picker for characters."""

from __future__ import annotations

import math
from typing import TYPE_CHECKING, override

from bauiv1lib.popup import PopupWindow
import bauiv1 as bui

if TYPE_CHECKING:
    from typing import Any, Sequence


[docs] class CharacterPickerDelegate: """Delegate for character-picker."""
[docs] def on_character_picker_pick(self, character: str) -> None: """Called when a character is selected.""" raise NotImplementedError()
[docs] def on_character_picker_get_more_press(self) -> None: """Called when the 'get more characters' button is pressed.""" raise NotImplementedError()
[docs] class CharacterPicker(PopupWindow): """Popup window for selecting characters.""" def __init__( self, parent: bui.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, ): # pylint: disable=too-many-locals # pylint: disable=too-many-positional-arguments from bascenev1lib.actor import spazappearance assert bui.app.classic is not None del parent # unused here uiscale = bui.app.ui_v1.uiscale if scale is None: scale = ( 1.85 if uiscale is bui.UIScale.SMALL else 1.65 if uiscale is bui.UIScale.MEDIUM else 1.23 ) self._delegate = delegate self._transitioning_out = False # make a list of spaz icons self._spazzes = spazappearance.get_appearances() self._spazzes.sort() self._icon_textures = [ bui.gettexture(bui.app.classic.spaz_appearances[s].icon_texture) for s in self._spazzes ] self._icon_tint_textures = [ bui.gettexture( bui.app.classic.spaz_appearances[s].icon_mask_texture ) for s in self._spazzes ] count = len(self._spazzes) columns = 3 rows = int(math.ceil(float(count) / columns)) button_width = 100 button_height = 100 button_buffer_h = 10 button_buffer_v = 15 self._width = 10 + columns * (button_width + 2 * button_buffer_h) * ( 1.0 / 0.95 ) * (1.0 / 0.8) self._height = self._width * ( 0.8 if uiscale is bui.UIScale.SMALL else 1.06 ) self._scroll_width = self._width * 0.8 self._scroll_height = self._height * 0.8 self._scroll_position = ( (self._width - self._scroll_width) * 0.5, (self._height - self._scroll_height) * 0.5, ) # Creates our _root_widget. super().__init__( position=position, size=(self._width, self._height), scale=scale, bg_color=(0.5, 0.5, 0.5), offset=offset, focus_position=self._scroll_position, focus_size=(self._scroll_width, self._scroll_height), ) self._scrollwidget = bui.scrollwidget( parent=self.root_widget, size=(self._scroll_width, self._scroll_height), color=(0.55, 0.55, 0.55), highlight=False, position=self._scroll_position, ) bui.containerwidget(edit=self._scrollwidget, claims_left_right=True) self._sub_width = self._scroll_width * 0.95 self._sub_height = ( 5 + rows * (button_height + 2 * button_buffer_v) + 100 ) self._subcontainer = bui.containerwidget( parent=self._scrollwidget, size=(self._sub_width, self._sub_height), background=False, ) index = 0 mask_texture = bui.gettexture('characterIconMask') for y in range(rows): for x in range(columns): pos = ( x * (button_width + 2 * button_buffer_h) + button_buffer_h, self._sub_height - (y + 1) * (button_height + 2 * button_buffer_v) + 12, ) btn = bui.buttonwidget( parent=self._subcontainer, button_type='square', size=(button_width, button_height), autoselect=True, texture=self._icon_textures[index], tint_texture=self._icon_tint_textures[index], mask_texture=mask_texture, label='', color=(1, 1, 1), tint_color=tint_color, tint2_color=tint2_color, on_activate_call=bui.Call( self._select_character, self._spazzes[index] ), position=pos, ) bui.widget(edit=btn, show_buffer_top=60, show_buffer_bottom=60) if self._spazzes[index] == selected_character: bui.containerwidget( edit=self._subcontainer, selected_child=btn, visible_child=btn, ) name = bui.Lstr( translate=('characterNames', self._spazzes[index]) ) bui.textwidget( parent=self._subcontainer, text=name, position=(pos[0] + button_width * 0.5, pos[1] - 12), size=(0, 0), scale=0.5, maxwidth=button_width, draw_controller=btn, h_align='center', v_align='center', color=(0.8, 0.8, 0.8, 0.8), ) index += 1 if index >= count: break if index >= count: break self._get_more_characters_button = btn = bui.buttonwidget( parent=self._subcontainer, size=(self._sub_width * 0.8, 60), position=(self._sub_width * 0.1, 30), label=bui.Lstr(resource='editProfileWindow.getMoreCharactersText'), on_activate_call=self._on_store_press, color=(0.6, 0.6, 0.6), textcolor=(0.8, 0.8, 0.8), autoselect=True, ) bui.widget(edit=btn, show_buffer_top=30, show_buffer_bottom=30) def _on_store_press(self) -> None: from bauiv1lib.account.signin import show_sign_in_prompt plus = bui.app.plus assert plus is not None if plus.get_v1_account_state() != 'signed_in': show_sign_in_prompt() return if self._delegate is not None: self._delegate.on_character_picker_get_more_press() self._transition_out() def _select_character(self, character: str) -> None: if self._delegate is not None: self._delegate.on_character_picker_pick(character) self._transition_out() def _transition_out(self) -> None: if not self._transitioning_out: self._transitioning_out = True bui.containerwidget(edit=self.root_widget, transition='out_scale')
[docs] @override def on_popup_cancel(self) -> None: bui.getsound('swish').play() self._transition_out()
# Docs-generation hack; import some stuff that we likely only forward-declared # in our actual source code so that docs tools can find it. from typing import (Coroutine, Any, Literal, Callable, Generator, Awaitable, Sequence, Self) import asyncio from concurrent.futures import Future from pathlib import Path from enum import Enum