# Released under the MIT License. See LICENSE for details.
#
"""Provides a picker for icons."""
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 IconPickerDelegate:
"""Delegate for character-picker."""
[docs]
def on_icon_picker_pick(self, icon: str) -> None:
"""Called when a character is selected."""
raise NotImplementedError()
[docs]
def on_icon_picker_get_more_press(self) -> None:
"""Called when the 'get more characters' button is pressed."""
raise NotImplementedError()
[docs]
class IconPicker(PopupWindow):
"""Picker for icons."""
def __init__(
self,
parent: bui.Widget,
position: tuple[float, float] = (0.0, 0.0),
delegate: IconPickerDelegate | 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_icon: str | None = None,
):
# pylint: disable=too-many-locals
del parent # unused here
del tint_color # unused_here
del tint2_color # unused here
assert bui.app.classic is not None
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
assert bui.app.classic is not None
self._icons = [
bui.charstr(bui.SpecialChar.LOGO)
] + bui.app.classic.accounts.get_purchased_icons()
count = len(self._icons)
columns = 4
rows = int(math.ceil(float(count) / columns))
button_width = 50
button_height = 50
button_buffer_h = 10
button_buffer_v = 5
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
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)
+ 0,
)
btn = bui.buttonwidget(
parent=self._subcontainer,
button_type='square',
size=(button_width, button_height),
autoselect=True,
text_scale=1.2,
label='',
color=(0.65, 0.65, 0.65),
on_activate_call=bui.Call(
self._select_icon, self._icons[index]
),
position=pos,
)
bui.textwidget(
parent=self._subcontainer,
h_align='center',
v_align='center',
size=(0, 0),
position=(pos[0] + 0.5 * button_width - 1, pos[1] + 15),
draw_controller=btn,
text=self._icons[index],
scale=1.8,
)
bui.widget(edit=btn, show_buffer_top=60, show_buffer_bottom=60)
if self._icons[index] == selected_icon:
bui.containerwidget(
edit=self._subcontainer,
selected_child=btn,
visible_child=btn,
)
index += 1
if index >= count:
break
if index >= count:
break
self._get_more_icons_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.getMoreIconsText'),
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_icon_picker_get_more_press()
self._transition_out()
def _select_icon(self, icon: str) -> None:
if self._delegate is not None:
self._delegate.on_icon_picker_pick(icon)
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-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