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.
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.