bauiv1lib.iconpicker

Provides a picker for icons.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides a picker for icons."""
  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 IconPickerDelegate:
 18    """Delegate for character-picker."""
 19
 20    def on_icon_picker_pick(self, icon: str) -> None:
 21        """Called when a character is selected."""
 22        raise NotImplementedError()
 23
 24    def on_icon_picker_get_more_press(self) -> None:
 25        """Called when the 'get more characters' button is pressed."""
 26        raise NotImplementedError()
 27
 28
 29class IconPicker(PopupWindow):
 30    """Picker for icons."""
 31
 32    def __init__(
 33        self,
 34        parent: bui.Widget,
 35        position: tuple[float, float] = (0.0, 0.0),
 36        delegate: IconPickerDelegate | None = None,
 37        scale: float | None = None,
 38        *,
 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_icon: str | None = None,
 43    ):
 44        # pylint: disable=too-many-locals
 45        del parent  # unused here
 46        del tint_color  # unused_here
 47        del tint2_color  # unused here
 48        assert bui.app.classic is not None
 49        uiscale = bui.app.ui_v1.uiscale
 50        if scale is None:
 51            scale = (
 52                1.85
 53                if uiscale is bui.UIScale.SMALL
 54                else 1.65 if uiscale is bui.UIScale.MEDIUM else 1.23
 55            )
 56
 57        self._delegate = delegate
 58        self._transitioning_out = False
 59
 60        assert bui.app.classic is not None
 61        self._icons = [
 62            bui.charstr(bui.SpecialChar.LOGO)
 63        ] + bui.app.classic.accounts.get_purchased_icons()
 64        count = len(self._icons)
 65        columns = 4
 66        rows = int(math.ceil(float(count) / columns))
 67
 68        button_width = 50
 69        button_height = 50
 70        button_buffer_h = 10
 71        button_buffer_v = 5
 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        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                    + 0,
124                )
125                btn = bui.buttonwidget(
126                    parent=self._subcontainer,
127                    button_type='square',
128                    size=(button_width, button_height),
129                    autoselect=True,
130                    text_scale=1.2,
131                    label='',
132                    color=(0.65, 0.65, 0.65),
133                    on_activate_call=bui.Call(
134                        self._select_icon, self._icons[index]
135                    ),
136                    position=pos,
137                )
138                bui.textwidget(
139                    parent=self._subcontainer,
140                    h_align='center',
141                    v_align='center',
142                    size=(0, 0),
143                    position=(pos[0] + 0.5 * button_width - 1, pos[1] + 15),
144                    draw_controller=btn,
145                    text=self._icons[index],
146                    scale=1.8,
147                )
148                bui.widget(edit=btn, show_buffer_top=60, show_buffer_bottom=60)
149                if self._icons[index] == selected_icon:
150                    bui.containerwidget(
151                        edit=self._subcontainer,
152                        selected_child=btn,
153                        visible_child=btn,
154                    )
155                index += 1
156
157                if index >= count:
158                    break
159            if index >= count:
160                break
161        self._get_more_icons_button = btn = bui.buttonwidget(
162            parent=self._subcontainer,
163            size=(self._sub_width * 0.8, 60),
164            position=(self._sub_width * 0.1, 30),
165            label=bui.Lstr(resource='editProfileWindow.getMoreIconsText'),
166            on_activate_call=self._on_store_press,
167            color=(0.6, 0.6, 0.6),
168            textcolor=(0.8, 0.8, 0.8),
169            autoselect=True,
170        )
171        bui.widget(edit=btn, show_buffer_top=30, show_buffer_bottom=30)
172
173    def _on_store_press(self) -> None:
174        from bauiv1lib.account import show_sign_in_prompt
175
176        plus = bui.app.plus
177        assert plus is not None
178
179        if plus.get_v1_account_state() != 'signed_in':
180            show_sign_in_prompt()
181            return
182
183        if self._delegate is not None:
184            self._delegate.on_icon_picker_get_more_press()
185
186        self._transition_out()
187
188    def _select_icon(self, icon: str) -> None:
189        if self._delegate is not None:
190            self._delegate.on_icon_picker_pick(icon)
191        self._transition_out()
192
193    def _transition_out(self) -> None:
194        if not self._transitioning_out:
195            self._transitioning_out = True
196            bui.containerwidget(edit=self.root_widget, transition='out_scale')
197
198    @override
199    def on_popup_cancel(self) -> None:
200        bui.getsound('swish').play()
201        self._transition_out()
class IconPickerDelegate:
18class IconPickerDelegate:
19    """Delegate for character-picker."""
20
21    def on_icon_picker_pick(self, icon: str) -> None:
22        """Called when a character is selected."""
23        raise NotImplementedError()
24
25    def on_icon_picker_get_more_press(self) -> None:
26        """Called when the 'get more characters' button is pressed."""
27        raise NotImplementedError()

Delegate for character-picker.

def on_icon_picker_pick(self, icon: str) -> None:
21    def on_icon_picker_pick(self, icon: str) -> None:
22        """Called when a character is selected."""
23        raise NotImplementedError()

Called when a character is selected.

def on_icon_picker_get_more_press(self) -> None:
25    def on_icon_picker_get_more_press(self) -> None:
26        """Called when the 'get more characters' button is pressed."""
27        raise NotImplementedError()

Called when the 'get more characters' button is pressed.

class IconPicker(bauiv1lib.popup.PopupWindow):
 30class IconPicker(PopupWindow):
 31    """Picker for icons."""
 32
 33    def __init__(
 34        self,
 35        parent: bui.Widget,
 36        position: tuple[float, float] = (0.0, 0.0),
 37        delegate: IconPickerDelegate | None = None,
 38        scale: float | None = None,
 39        *,
 40        offset: tuple[float, float] = (0.0, 0.0),
 41        tint_color: Sequence[float] = (1.0, 1.0, 1.0),
 42        tint2_color: Sequence[float] = (1.0, 1.0, 1.0),
 43        selected_icon: str | None = None,
 44    ):
 45        # pylint: disable=too-many-locals
 46        del parent  # unused here
 47        del tint_color  # unused_here
 48        del tint2_color  # unused here
 49        assert bui.app.classic is not None
 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        assert bui.app.classic is not None
 62        self._icons = [
 63            bui.charstr(bui.SpecialChar.LOGO)
 64        ] + bui.app.classic.accounts.get_purchased_icons()
 65        count = len(self._icons)
 66        columns = 4
 67        rows = int(math.ceil(float(count) / columns))
 68
 69        button_width = 50
 70        button_height = 50
 71        button_buffer_h = 10
 72        button_buffer_v = 5
 73
 74        self._width = 10 + columns * (button_width + 2 * button_buffer_h) * (
 75            1.0 / 0.95
 76        ) * (1.0 / 0.8)
 77        self._height = self._width * (
 78            0.8 if uiscale is bui.UIScale.SMALL else 1.06
 79        )
 80
 81        self._scroll_width = self._width * 0.8
 82        self._scroll_height = self._height * 0.8
 83        self._scroll_position = (
 84            (self._width - self._scroll_width) * 0.5,
 85            (self._height - self._scroll_height) * 0.5,
 86        )
 87
 88        # creates our _root_widget
 89        super().__init__(
 90            position=position,
 91            size=(self._width, self._height),
 92            scale=scale,
 93            bg_color=(0.5, 0.5, 0.5),
 94            offset=offset,
 95            focus_position=self._scroll_position,
 96            focus_size=(self._scroll_width, self._scroll_height),
 97        )
 98
 99        self._scrollwidget = bui.scrollwidget(
100            parent=self.root_widget,
101            size=(self._scroll_width, self._scroll_height),
102            color=(0.55, 0.55, 0.55),
103            highlight=False,
104            position=self._scroll_position,
105        )
106        bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
107
108        self._sub_width = self._scroll_width * 0.95
109        self._sub_height = (
110            5 + rows * (button_height + 2 * button_buffer_v) + 100
111        )
112        self._subcontainer = bui.containerwidget(
113            parent=self._scrollwidget,
114            size=(self._sub_width, self._sub_height),
115            background=False,
116        )
117        index = 0
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                    + 0,
125                )
126                btn = bui.buttonwidget(
127                    parent=self._subcontainer,
128                    button_type='square',
129                    size=(button_width, button_height),
130                    autoselect=True,
131                    text_scale=1.2,
132                    label='',
133                    color=(0.65, 0.65, 0.65),
134                    on_activate_call=bui.Call(
135                        self._select_icon, self._icons[index]
136                    ),
137                    position=pos,
138                )
139                bui.textwidget(
140                    parent=self._subcontainer,
141                    h_align='center',
142                    v_align='center',
143                    size=(0, 0),
144                    position=(pos[0] + 0.5 * button_width - 1, pos[1] + 15),
145                    draw_controller=btn,
146                    text=self._icons[index],
147                    scale=1.8,
148                )
149                bui.widget(edit=btn, show_buffer_top=60, show_buffer_bottom=60)
150                if self._icons[index] == selected_icon:
151                    bui.containerwidget(
152                        edit=self._subcontainer,
153                        selected_child=btn,
154                        visible_child=btn,
155                    )
156                index += 1
157
158                if index >= count:
159                    break
160            if index >= count:
161                break
162        self._get_more_icons_button = btn = bui.buttonwidget(
163            parent=self._subcontainer,
164            size=(self._sub_width * 0.8, 60),
165            position=(self._sub_width * 0.1, 30),
166            label=bui.Lstr(resource='editProfileWindow.getMoreIconsText'),
167            on_activate_call=self._on_store_press,
168            color=(0.6, 0.6, 0.6),
169            textcolor=(0.8, 0.8, 0.8),
170            autoselect=True,
171        )
172        bui.widget(edit=btn, show_buffer_top=30, show_buffer_bottom=30)
173
174    def _on_store_press(self) -> None:
175        from bauiv1lib.account import show_sign_in_prompt
176
177        plus = bui.app.plus
178        assert plus is not None
179
180        if plus.get_v1_account_state() != 'signed_in':
181            show_sign_in_prompt()
182            return
183
184        if self._delegate is not None:
185            self._delegate.on_icon_picker_get_more_press()
186
187        self._transition_out()
188
189    def _select_icon(self, icon: str) -> None:
190        if self._delegate is not None:
191            self._delegate.on_icon_picker_pick(icon)
192        self._transition_out()
193
194    def _transition_out(self) -> None:
195        if not self._transitioning_out:
196            self._transitioning_out = True
197            bui.containerwidget(edit=self.root_widget, transition='out_scale')
198
199    @override
200    def on_popup_cancel(self) -> None:
201        bui.getsound('swish').play()
202        self._transition_out()

Picker for icons.

IconPicker( parent: _bauiv1.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)
 33    def __init__(
 34        self,
 35        parent: bui.Widget,
 36        position: tuple[float, float] = (0.0, 0.0),
 37        delegate: IconPickerDelegate | None = None,
 38        scale: float | None = None,
 39        *,
 40        offset: tuple[float, float] = (0.0, 0.0),
 41        tint_color: Sequence[float] = (1.0, 1.0, 1.0),
 42        tint2_color: Sequence[float] = (1.0, 1.0, 1.0),
 43        selected_icon: str | None = None,
 44    ):
 45        # pylint: disable=too-many-locals
 46        del parent  # unused here
 47        del tint_color  # unused_here
 48        del tint2_color  # unused here
 49        assert bui.app.classic is not None
 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        assert bui.app.classic is not None
 62        self._icons = [
 63            bui.charstr(bui.SpecialChar.LOGO)
 64        ] + bui.app.classic.accounts.get_purchased_icons()
 65        count = len(self._icons)
 66        columns = 4
 67        rows = int(math.ceil(float(count) / columns))
 68
 69        button_width = 50
 70        button_height = 50
 71        button_buffer_h = 10
 72        button_buffer_v = 5
 73
 74        self._width = 10 + columns * (button_width + 2 * button_buffer_h) * (
 75            1.0 / 0.95
 76        ) * (1.0 / 0.8)
 77        self._height = self._width * (
 78            0.8 if uiscale is bui.UIScale.SMALL else 1.06
 79        )
 80
 81        self._scroll_width = self._width * 0.8
 82        self._scroll_height = self._height * 0.8
 83        self._scroll_position = (
 84            (self._width - self._scroll_width) * 0.5,
 85            (self._height - self._scroll_height) * 0.5,
 86        )
 87
 88        # creates our _root_widget
 89        super().__init__(
 90            position=position,
 91            size=(self._width, self._height),
 92            scale=scale,
 93            bg_color=(0.5, 0.5, 0.5),
 94            offset=offset,
 95            focus_position=self._scroll_position,
 96            focus_size=(self._scroll_width, self._scroll_height),
 97        )
 98
 99        self._scrollwidget = bui.scrollwidget(
100            parent=self.root_widget,
101            size=(self._scroll_width, self._scroll_height),
102            color=(0.55, 0.55, 0.55),
103            highlight=False,
104            position=self._scroll_position,
105        )
106        bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
107
108        self._sub_width = self._scroll_width * 0.95
109        self._sub_height = (
110            5 + rows * (button_height + 2 * button_buffer_v) + 100
111        )
112        self._subcontainer = bui.containerwidget(
113            parent=self._scrollwidget,
114            size=(self._sub_width, self._sub_height),
115            background=False,
116        )
117        index = 0
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                    + 0,
125                )
126                btn = bui.buttonwidget(
127                    parent=self._subcontainer,
128                    button_type='square',
129                    size=(button_width, button_height),
130                    autoselect=True,
131                    text_scale=1.2,
132                    label='',
133                    color=(0.65, 0.65, 0.65),
134                    on_activate_call=bui.Call(
135                        self._select_icon, self._icons[index]
136                    ),
137                    position=pos,
138                )
139                bui.textwidget(
140                    parent=self._subcontainer,
141                    h_align='center',
142                    v_align='center',
143                    size=(0, 0),
144                    position=(pos[0] + 0.5 * button_width - 1, pos[1] + 15),
145                    draw_controller=btn,
146                    text=self._icons[index],
147                    scale=1.8,
148                )
149                bui.widget(edit=btn, show_buffer_top=60, show_buffer_bottom=60)
150                if self._icons[index] == selected_icon:
151                    bui.containerwidget(
152                        edit=self._subcontainer,
153                        selected_child=btn,
154                        visible_child=btn,
155                    )
156                index += 1
157
158                if index >= count:
159                    break
160            if index >= count:
161                break
162        self._get_more_icons_button = btn = bui.buttonwidget(
163            parent=self._subcontainer,
164            size=(self._sub_width * 0.8, 60),
165            position=(self._sub_width * 0.1, 30),
166            label=bui.Lstr(resource='editProfileWindow.getMoreIconsText'),
167            on_activate_call=self._on_store_press,
168            color=(0.6, 0.6, 0.6),
169            textcolor=(0.8, 0.8, 0.8),
170            autoselect=True,
171        )
172        bui.widget(edit=btn, show_buffer_top=30, show_buffer_bottom=30)
@override
def on_popup_cancel(self) -> None:
199    @override
200    def on_popup_cancel(self) -> None:
201        bui.getsound('swish').play()
202        self._transition_out()

Called when the popup is canceled.

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