bastd.ui.colorpicker
Provides popup windows for choosing colors.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides popup windows for choosing colors.""" 4 5from __future__ import annotations 6 7from typing import TYPE_CHECKING 8 9import ba 10from bastd.ui.popup import PopupWindow 11 12if TYPE_CHECKING: 13 from typing import Any, Sequence 14 15 16class ColorPicker(PopupWindow): 17 """A popup UI to select from a set of colors. 18 19 Passes the color to the delegate's color_picker_selected_color() method. 20 """ 21 22 def __init__( 23 self, 24 parent: ba.Widget, 25 position: tuple[float, float], 26 initial_color: Sequence[float] = (1.0, 1.0, 1.0), 27 delegate: Any = None, 28 scale: float | None = None, 29 offset: tuple[float, float] = (0.0, 0.0), 30 tag: Any = '', 31 ): 32 # pylint: disable=too-many-locals 33 from ba.internal import get_player_colors 34 35 c_raw = get_player_colors() 36 assert len(c_raw) == 16 37 self.colors = [c_raw[0:4], c_raw[4:8], c_raw[8:12], c_raw[12:16]] 38 39 uiscale = ba.app.ui.uiscale 40 if scale is None: 41 scale = ( 42 2.3 43 if uiscale is ba.UIScale.SMALL 44 else 1.65 45 if uiscale is ba.UIScale.MEDIUM 46 else 1.23 47 ) 48 self._parent = parent 49 self._position = position 50 self._scale = scale 51 self._offset = offset 52 self._delegate = delegate 53 self._transitioning_out = False 54 self._tag = tag 55 self._initial_color = initial_color 56 57 # Create our _root_widget. 58 PopupWindow.__init__( 59 self, 60 position=position, 61 size=(210, 240), 62 scale=scale, 63 focus_position=(10, 10), 64 focus_size=(190, 220), 65 bg_color=(0.5, 0.5, 0.5), 66 offset=offset, 67 ) 68 rows: list[list[ba.Widget]] = [] 69 closest_dist = 9999.0 70 closest = (0, 0) 71 for y in range(4): 72 row: list[ba.Widget] = [] 73 rows.append(row) 74 for x in range(4): 75 color = self.colors[y][x] 76 dist = ( 77 abs(color[0] - initial_color[0]) 78 + abs(color[1] - initial_color[1]) 79 + abs(color[2] - initial_color[2]) 80 ) 81 if dist < closest_dist: 82 closest = (x, y) 83 closest_dist = dist 84 btn = ba.buttonwidget( 85 parent=self.root_widget, 86 position=(22 + 45 * x, 185 - 45 * y), 87 size=(35, 40), 88 label='', 89 button_type='square', 90 on_activate_call=ba.WeakCall(self._select, x, y), 91 autoselect=True, 92 color=color, 93 extra_touch_border_scale=0.0, 94 ) 95 row.append(btn) 96 other_button = ba.buttonwidget( 97 parent=self.root_widget, 98 position=(105 - 60, 13), 99 color=(0.7, 0.7, 0.7), 100 text_scale=0.5, 101 textcolor=(0.8, 0.8, 0.8), 102 size=(120, 30), 103 label=ba.Lstr( 104 resource='otherText', 105 fallback_resource='coopSelectWindow.customText', 106 ), 107 autoselect=True, 108 on_activate_call=ba.WeakCall(self._select_other), 109 ) 110 111 # Custom colors are limited to pro currently. 112 if not ba.app.accounts_v1.have_pro(): 113 ba.imagewidget( 114 parent=self.root_widget, 115 position=(50, 12), 116 size=(30, 30), 117 texture=ba.gettexture('lock'), 118 draw_controller=other_button, 119 ) 120 121 # If their color is close to one of our swatches, select it. 122 # Otherwise select 'other'. 123 if closest_dist < 0.03: 124 ba.containerwidget( 125 edit=self.root_widget, 126 selected_child=rows[closest[1]][closest[0]], 127 ) 128 else: 129 ba.containerwidget( 130 edit=self.root_widget, selected_child=other_button 131 ) 132 133 def get_tag(self) -> Any: 134 """Return this popup's tag.""" 135 return self._tag 136 137 def _select_other(self) -> None: 138 from bastd.ui import purchase 139 140 # Requires pro. 141 if not ba.app.accounts_v1.have_pro(): 142 purchase.PurchaseWindow(items=['pro']) 143 self._transition_out() 144 return 145 ColorPickerExact( 146 parent=self._parent, 147 position=self._position, 148 initial_color=self._initial_color, 149 delegate=self._delegate, 150 scale=self._scale, 151 offset=self._offset, 152 tag=self._tag, 153 ) 154 155 # New picker now 'owns' the delegate; we shouldn't send it any 156 # more messages. 157 self._delegate = None 158 self._transition_out() 159 160 def _select(self, x: int, y: int) -> None: 161 if self._delegate: 162 self._delegate.color_picker_selected_color(self, self.colors[y][x]) 163 ba.timer(0.05, self._transition_out, timetype=ba.TimeType.REAL) 164 165 def _transition_out(self) -> None: 166 if not self._transitioning_out: 167 self._transitioning_out = True 168 if self._delegate is not None: 169 self._delegate.color_picker_closing(self) 170 ba.containerwidget(edit=self.root_widget, transition='out_scale') 171 172 def on_popup_cancel(self) -> None: 173 if not self._transitioning_out: 174 ba.playsound(ba.getsound('swish')) 175 self._transition_out() 176 177 178class ColorPickerExact(PopupWindow): 179 """pops up a ui to select from a set of colors. 180 passes the color to the delegate's color_picker_selected_color() method""" 181 182 def __init__( 183 self, 184 parent: ba.Widget, 185 position: tuple[float, float], 186 initial_color: Sequence[float] = (1.0, 1.0, 1.0), 187 delegate: Any = None, 188 scale: float | None = None, 189 offset: tuple[float, float] = (0.0, 0.0), 190 tag: Any = '', 191 ): 192 # pylint: disable=too-many-locals 193 del parent # Unused var. 194 from ba.internal import get_player_colors 195 196 c_raw = get_player_colors() 197 assert len(c_raw) == 16 198 self.colors = [c_raw[0:4], c_raw[4:8], c_raw[8:12], c_raw[12:16]] 199 200 uiscale = ba.app.ui.uiscale 201 if scale is None: 202 scale = ( 203 2.3 204 if uiscale is ba.UIScale.SMALL 205 else 1.65 206 if uiscale is ba.UIScale.MEDIUM 207 else 1.23 208 ) 209 self._delegate = delegate 210 self._transitioning_out = False 211 self._tag = tag 212 self._color = list(initial_color) 213 self._last_press_time = ba.time( 214 ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS 215 ) 216 self._last_press_color_name: str | None = None 217 self._last_press_increasing: bool | None = None 218 self._change_speed = 1.0 219 width = 180.0 220 height = 240.0 221 222 # Creates our _root_widget. 223 PopupWindow.__init__( 224 self, 225 position=position, 226 size=(width, height), 227 scale=scale, 228 focus_position=(10, 10), 229 focus_size=(width - 20, height - 20), 230 bg_color=(0.5, 0.5, 0.5), 231 offset=offset, 232 ) 233 self._swatch = ba.imagewidget( 234 parent=self.root_widget, 235 position=(width * 0.5 - 50, height - 70), 236 size=(100, 70), 237 texture=ba.gettexture('buttonSquare'), 238 color=(1, 0, 0), 239 ) 240 x = 50 241 y = height - 90 242 self._label_r: ba.Widget 243 self._label_g: ba.Widget 244 self._label_b: ba.Widget 245 for color_name, color_val in [ 246 ('r', (1, 0.15, 0.15)), 247 ('g', (0.15, 1, 0.15)), 248 ('b', (0.15, 0.15, 1)), 249 ]: 250 txt = ba.textwidget( 251 parent=self.root_widget, 252 position=(x - 10, y), 253 size=(0, 0), 254 h_align='center', 255 color=color_val, 256 v_align='center', 257 text='0.12', 258 ) 259 setattr(self, '_label_' + color_name, txt) 260 for b_label, bhval, binc in [('-', 30, False), ('+', 75, True)]: 261 ba.buttonwidget( 262 parent=self.root_widget, 263 position=(x + bhval, y - 15), 264 scale=0.8, 265 repeat=True, 266 text_scale=1.3, 267 size=(40, 40), 268 label=b_label, 269 autoselect=True, 270 enable_sound=False, 271 on_activate_call=ba.WeakCall( 272 self._color_change_press, color_name, binc 273 ), 274 ) 275 y -= 42 276 277 btn = ba.buttonwidget( 278 parent=self.root_widget, 279 position=(width * 0.5 - 40, 10), 280 size=(80, 30), 281 text_scale=0.6, 282 color=(0.6, 0.6, 0.6), 283 textcolor=(0.7, 0.7, 0.7), 284 label=ba.Lstr(resource='doneText'), 285 on_activate_call=ba.WeakCall(self._transition_out), 286 autoselect=True, 287 ) 288 ba.containerwidget(edit=self.root_widget, start_button=btn) 289 290 # Unlike the swatch picker, we stay open and constantly push our 291 # color to the delegate, so start doing that. 292 self._update_for_color() 293 294 # noinspection PyUnresolvedReferences 295 def _update_for_color(self) -> None: 296 if not self.root_widget: 297 return 298 ba.imagewidget(edit=self._swatch, color=self._color) 299 300 # We generate these procedurally, so pylint misses them. 301 # FIXME: create static attrs instead. 302 # pylint: disable=consider-using-f-string 303 ba.textwidget(edit=self._label_r, text='%.2f' % self._color[0]) 304 ba.textwidget(edit=self._label_g, text='%.2f' % self._color[1]) 305 ba.textwidget(edit=self._label_b, text='%.2f' % self._color[2]) 306 if self._delegate is not None: 307 self._delegate.color_picker_selected_color(self, self._color) 308 309 def _color_change_press(self, color_name: str, increasing: bool) -> None: 310 # If we get rapid-fire presses, eventually start moving faster. 311 current_time = ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS) 312 since_last = current_time - self._last_press_time 313 if ( 314 since_last < 200 315 and self._last_press_color_name == color_name 316 and self._last_press_increasing == increasing 317 ): 318 self._change_speed += 0.25 319 else: 320 self._change_speed = 1.0 321 self._last_press_time = current_time 322 self._last_press_color_name = color_name 323 self._last_press_increasing = increasing 324 325 color_index = ('r', 'g', 'b').index(color_name) 326 offs = int(self._change_speed) * (0.01 if increasing else -0.01) 327 self._color[color_index] = max( 328 0.0, min(1.0, self._color[color_index] + offs) 329 ) 330 self._update_for_color() 331 332 def get_tag(self) -> Any: 333 """Return this popup's tag value.""" 334 return self._tag 335 336 def _transition_out(self) -> None: 337 if not self._transitioning_out: 338 self._transitioning_out = True 339 if self._delegate is not None: 340 self._delegate.color_picker_closing(self) 341 ba.containerwidget(edit=self.root_widget, transition='out_scale') 342 343 def on_popup_cancel(self) -> None: 344 if not self._transitioning_out: 345 ba.playsound(ba.getsound('swish')) 346 self._transition_out()
17class ColorPicker(PopupWindow): 18 """A popup UI to select from a set of colors. 19 20 Passes the color to the delegate's color_picker_selected_color() method. 21 """ 22 23 def __init__( 24 self, 25 parent: ba.Widget, 26 position: tuple[float, float], 27 initial_color: Sequence[float] = (1.0, 1.0, 1.0), 28 delegate: Any = None, 29 scale: float | None = None, 30 offset: tuple[float, float] = (0.0, 0.0), 31 tag: Any = '', 32 ): 33 # pylint: disable=too-many-locals 34 from ba.internal import get_player_colors 35 36 c_raw = get_player_colors() 37 assert len(c_raw) == 16 38 self.colors = [c_raw[0:4], c_raw[4:8], c_raw[8:12], c_raw[12:16]] 39 40 uiscale = ba.app.ui.uiscale 41 if scale is None: 42 scale = ( 43 2.3 44 if uiscale is ba.UIScale.SMALL 45 else 1.65 46 if uiscale is ba.UIScale.MEDIUM 47 else 1.23 48 ) 49 self._parent = parent 50 self._position = position 51 self._scale = scale 52 self._offset = offset 53 self._delegate = delegate 54 self._transitioning_out = False 55 self._tag = tag 56 self._initial_color = initial_color 57 58 # Create our _root_widget. 59 PopupWindow.__init__( 60 self, 61 position=position, 62 size=(210, 240), 63 scale=scale, 64 focus_position=(10, 10), 65 focus_size=(190, 220), 66 bg_color=(0.5, 0.5, 0.5), 67 offset=offset, 68 ) 69 rows: list[list[ba.Widget]] = [] 70 closest_dist = 9999.0 71 closest = (0, 0) 72 for y in range(4): 73 row: list[ba.Widget] = [] 74 rows.append(row) 75 for x in range(4): 76 color = self.colors[y][x] 77 dist = ( 78 abs(color[0] - initial_color[0]) 79 + abs(color[1] - initial_color[1]) 80 + abs(color[2] - initial_color[2]) 81 ) 82 if dist < closest_dist: 83 closest = (x, y) 84 closest_dist = dist 85 btn = ba.buttonwidget( 86 parent=self.root_widget, 87 position=(22 + 45 * x, 185 - 45 * y), 88 size=(35, 40), 89 label='', 90 button_type='square', 91 on_activate_call=ba.WeakCall(self._select, x, y), 92 autoselect=True, 93 color=color, 94 extra_touch_border_scale=0.0, 95 ) 96 row.append(btn) 97 other_button = ba.buttonwidget( 98 parent=self.root_widget, 99 position=(105 - 60, 13), 100 color=(0.7, 0.7, 0.7), 101 text_scale=0.5, 102 textcolor=(0.8, 0.8, 0.8), 103 size=(120, 30), 104 label=ba.Lstr( 105 resource='otherText', 106 fallback_resource='coopSelectWindow.customText', 107 ), 108 autoselect=True, 109 on_activate_call=ba.WeakCall(self._select_other), 110 ) 111 112 # Custom colors are limited to pro currently. 113 if not ba.app.accounts_v1.have_pro(): 114 ba.imagewidget( 115 parent=self.root_widget, 116 position=(50, 12), 117 size=(30, 30), 118 texture=ba.gettexture('lock'), 119 draw_controller=other_button, 120 ) 121 122 # If their color is close to one of our swatches, select it. 123 # Otherwise select 'other'. 124 if closest_dist < 0.03: 125 ba.containerwidget( 126 edit=self.root_widget, 127 selected_child=rows[closest[1]][closest[0]], 128 ) 129 else: 130 ba.containerwidget( 131 edit=self.root_widget, selected_child=other_button 132 ) 133 134 def get_tag(self) -> Any: 135 """Return this popup's tag.""" 136 return self._tag 137 138 def _select_other(self) -> None: 139 from bastd.ui import purchase 140 141 # Requires pro. 142 if not ba.app.accounts_v1.have_pro(): 143 purchase.PurchaseWindow(items=['pro']) 144 self._transition_out() 145 return 146 ColorPickerExact( 147 parent=self._parent, 148 position=self._position, 149 initial_color=self._initial_color, 150 delegate=self._delegate, 151 scale=self._scale, 152 offset=self._offset, 153 tag=self._tag, 154 ) 155 156 # New picker now 'owns' the delegate; we shouldn't send it any 157 # more messages. 158 self._delegate = None 159 self._transition_out() 160 161 def _select(self, x: int, y: int) -> None: 162 if self._delegate: 163 self._delegate.color_picker_selected_color(self, self.colors[y][x]) 164 ba.timer(0.05, self._transition_out, timetype=ba.TimeType.REAL) 165 166 def _transition_out(self) -> None: 167 if not self._transitioning_out: 168 self._transitioning_out = True 169 if self._delegate is not None: 170 self._delegate.color_picker_closing(self) 171 ba.containerwidget(edit=self.root_widget, transition='out_scale') 172 173 def on_popup_cancel(self) -> None: 174 if not self._transitioning_out: 175 ba.playsound(ba.getsound('swish')) 176 self._transition_out()
A popup UI to select from a set of colors.
Passes the color to the delegate's color_picker_selected_color() method.
ColorPicker( parent: _ba.Widget, position: tuple[float, float], initial_color: Sequence[float] = (1.0, 1.0, 1.0), delegate: Any = None, scale: float | None = None, offset: tuple[float, float] = (0.0, 0.0), tag: Any = '')
23 def __init__( 24 self, 25 parent: ba.Widget, 26 position: tuple[float, float], 27 initial_color: Sequence[float] = (1.0, 1.0, 1.0), 28 delegate: Any = None, 29 scale: float | None = None, 30 offset: tuple[float, float] = (0.0, 0.0), 31 tag: Any = '', 32 ): 33 # pylint: disable=too-many-locals 34 from ba.internal import get_player_colors 35 36 c_raw = get_player_colors() 37 assert len(c_raw) == 16 38 self.colors = [c_raw[0:4], c_raw[4:8], c_raw[8:12], c_raw[12:16]] 39 40 uiscale = ba.app.ui.uiscale 41 if scale is None: 42 scale = ( 43 2.3 44 if uiscale is ba.UIScale.SMALL 45 else 1.65 46 if uiscale is ba.UIScale.MEDIUM 47 else 1.23 48 ) 49 self._parent = parent 50 self._position = position 51 self._scale = scale 52 self._offset = offset 53 self._delegate = delegate 54 self._transitioning_out = False 55 self._tag = tag 56 self._initial_color = initial_color 57 58 # Create our _root_widget. 59 PopupWindow.__init__( 60 self, 61 position=position, 62 size=(210, 240), 63 scale=scale, 64 focus_position=(10, 10), 65 focus_size=(190, 220), 66 bg_color=(0.5, 0.5, 0.5), 67 offset=offset, 68 ) 69 rows: list[list[ba.Widget]] = [] 70 closest_dist = 9999.0 71 closest = (0, 0) 72 for y in range(4): 73 row: list[ba.Widget] = [] 74 rows.append(row) 75 for x in range(4): 76 color = self.colors[y][x] 77 dist = ( 78 abs(color[0] - initial_color[0]) 79 + abs(color[1] - initial_color[1]) 80 + abs(color[2] - initial_color[2]) 81 ) 82 if dist < closest_dist: 83 closest = (x, y) 84 closest_dist = dist 85 btn = ba.buttonwidget( 86 parent=self.root_widget, 87 position=(22 + 45 * x, 185 - 45 * y), 88 size=(35, 40), 89 label='', 90 button_type='square', 91 on_activate_call=ba.WeakCall(self._select, x, y), 92 autoselect=True, 93 color=color, 94 extra_touch_border_scale=0.0, 95 ) 96 row.append(btn) 97 other_button = ba.buttonwidget( 98 parent=self.root_widget, 99 position=(105 - 60, 13), 100 color=(0.7, 0.7, 0.7), 101 text_scale=0.5, 102 textcolor=(0.8, 0.8, 0.8), 103 size=(120, 30), 104 label=ba.Lstr( 105 resource='otherText', 106 fallback_resource='coopSelectWindow.customText', 107 ), 108 autoselect=True, 109 on_activate_call=ba.WeakCall(self._select_other), 110 ) 111 112 # Custom colors are limited to pro currently. 113 if not ba.app.accounts_v1.have_pro(): 114 ba.imagewidget( 115 parent=self.root_widget, 116 position=(50, 12), 117 size=(30, 30), 118 texture=ba.gettexture('lock'), 119 draw_controller=other_button, 120 ) 121 122 # If their color is close to one of our swatches, select it. 123 # Otherwise select 'other'. 124 if closest_dist < 0.03: 125 ba.containerwidget( 126 edit=self.root_widget, 127 selected_child=rows[closest[1]][closest[0]], 128 ) 129 else: 130 ba.containerwidget( 131 edit=self.root_widget, selected_child=other_button 132 )
179class ColorPickerExact(PopupWindow): 180 """pops up a ui to select from a set of colors. 181 passes the color to the delegate's color_picker_selected_color() method""" 182 183 def __init__( 184 self, 185 parent: ba.Widget, 186 position: tuple[float, float], 187 initial_color: Sequence[float] = (1.0, 1.0, 1.0), 188 delegate: Any = None, 189 scale: float | None = None, 190 offset: tuple[float, float] = (0.0, 0.0), 191 tag: Any = '', 192 ): 193 # pylint: disable=too-many-locals 194 del parent # Unused var. 195 from ba.internal import get_player_colors 196 197 c_raw = get_player_colors() 198 assert len(c_raw) == 16 199 self.colors = [c_raw[0:4], c_raw[4:8], c_raw[8:12], c_raw[12:16]] 200 201 uiscale = ba.app.ui.uiscale 202 if scale is None: 203 scale = ( 204 2.3 205 if uiscale is ba.UIScale.SMALL 206 else 1.65 207 if uiscale is ba.UIScale.MEDIUM 208 else 1.23 209 ) 210 self._delegate = delegate 211 self._transitioning_out = False 212 self._tag = tag 213 self._color = list(initial_color) 214 self._last_press_time = ba.time( 215 ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS 216 ) 217 self._last_press_color_name: str | None = None 218 self._last_press_increasing: bool | None = None 219 self._change_speed = 1.0 220 width = 180.0 221 height = 240.0 222 223 # Creates our _root_widget. 224 PopupWindow.__init__( 225 self, 226 position=position, 227 size=(width, height), 228 scale=scale, 229 focus_position=(10, 10), 230 focus_size=(width - 20, height - 20), 231 bg_color=(0.5, 0.5, 0.5), 232 offset=offset, 233 ) 234 self._swatch = ba.imagewidget( 235 parent=self.root_widget, 236 position=(width * 0.5 - 50, height - 70), 237 size=(100, 70), 238 texture=ba.gettexture('buttonSquare'), 239 color=(1, 0, 0), 240 ) 241 x = 50 242 y = height - 90 243 self._label_r: ba.Widget 244 self._label_g: ba.Widget 245 self._label_b: ba.Widget 246 for color_name, color_val in [ 247 ('r', (1, 0.15, 0.15)), 248 ('g', (0.15, 1, 0.15)), 249 ('b', (0.15, 0.15, 1)), 250 ]: 251 txt = ba.textwidget( 252 parent=self.root_widget, 253 position=(x - 10, y), 254 size=(0, 0), 255 h_align='center', 256 color=color_val, 257 v_align='center', 258 text='0.12', 259 ) 260 setattr(self, '_label_' + color_name, txt) 261 for b_label, bhval, binc in [('-', 30, False), ('+', 75, True)]: 262 ba.buttonwidget( 263 parent=self.root_widget, 264 position=(x + bhval, y - 15), 265 scale=0.8, 266 repeat=True, 267 text_scale=1.3, 268 size=(40, 40), 269 label=b_label, 270 autoselect=True, 271 enable_sound=False, 272 on_activate_call=ba.WeakCall( 273 self._color_change_press, color_name, binc 274 ), 275 ) 276 y -= 42 277 278 btn = ba.buttonwidget( 279 parent=self.root_widget, 280 position=(width * 0.5 - 40, 10), 281 size=(80, 30), 282 text_scale=0.6, 283 color=(0.6, 0.6, 0.6), 284 textcolor=(0.7, 0.7, 0.7), 285 label=ba.Lstr(resource='doneText'), 286 on_activate_call=ba.WeakCall(self._transition_out), 287 autoselect=True, 288 ) 289 ba.containerwidget(edit=self.root_widget, start_button=btn) 290 291 # Unlike the swatch picker, we stay open and constantly push our 292 # color to the delegate, so start doing that. 293 self._update_for_color() 294 295 # noinspection PyUnresolvedReferences 296 def _update_for_color(self) -> None: 297 if not self.root_widget: 298 return 299 ba.imagewidget(edit=self._swatch, color=self._color) 300 301 # We generate these procedurally, so pylint misses them. 302 # FIXME: create static attrs instead. 303 # pylint: disable=consider-using-f-string 304 ba.textwidget(edit=self._label_r, text='%.2f' % self._color[0]) 305 ba.textwidget(edit=self._label_g, text='%.2f' % self._color[1]) 306 ba.textwidget(edit=self._label_b, text='%.2f' % self._color[2]) 307 if self._delegate is not None: 308 self._delegate.color_picker_selected_color(self, self._color) 309 310 def _color_change_press(self, color_name: str, increasing: bool) -> None: 311 # If we get rapid-fire presses, eventually start moving faster. 312 current_time = ba.time(ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS) 313 since_last = current_time - self._last_press_time 314 if ( 315 since_last < 200 316 and self._last_press_color_name == color_name 317 and self._last_press_increasing == increasing 318 ): 319 self._change_speed += 0.25 320 else: 321 self._change_speed = 1.0 322 self._last_press_time = current_time 323 self._last_press_color_name = color_name 324 self._last_press_increasing = increasing 325 326 color_index = ('r', 'g', 'b').index(color_name) 327 offs = int(self._change_speed) * (0.01 if increasing else -0.01) 328 self._color[color_index] = max( 329 0.0, min(1.0, self._color[color_index] + offs) 330 ) 331 self._update_for_color() 332 333 def get_tag(self) -> Any: 334 """Return this popup's tag value.""" 335 return self._tag 336 337 def _transition_out(self) -> None: 338 if not self._transitioning_out: 339 self._transitioning_out = True 340 if self._delegate is not None: 341 self._delegate.color_picker_closing(self) 342 ba.containerwidget(edit=self.root_widget, transition='out_scale') 343 344 def on_popup_cancel(self) -> None: 345 if not self._transitioning_out: 346 ba.playsound(ba.getsound('swish')) 347 self._transition_out()
pops up a ui to select from a set of colors. passes the color to the delegate's color_picker_selected_color() method
ColorPickerExact( parent: _ba.Widget, position: tuple[float, float], initial_color: Sequence[float] = (1.0, 1.0, 1.0), delegate: Any = None, scale: float | None = None, offset: tuple[float, float] = (0.0, 0.0), tag: Any = '')
183 def __init__( 184 self, 185 parent: ba.Widget, 186 position: tuple[float, float], 187 initial_color: Sequence[float] = (1.0, 1.0, 1.0), 188 delegate: Any = None, 189 scale: float | None = None, 190 offset: tuple[float, float] = (0.0, 0.0), 191 tag: Any = '', 192 ): 193 # pylint: disable=too-many-locals 194 del parent # Unused var. 195 from ba.internal import get_player_colors 196 197 c_raw = get_player_colors() 198 assert len(c_raw) == 16 199 self.colors = [c_raw[0:4], c_raw[4:8], c_raw[8:12], c_raw[12:16]] 200 201 uiscale = ba.app.ui.uiscale 202 if scale is None: 203 scale = ( 204 2.3 205 if uiscale is ba.UIScale.SMALL 206 else 1.65 207 if uiscale is ba.UIScale.MEDIUM 208 else 1.23 209 ) 210 self._delegate = delegate 211 self._transitioning_out = False 212 self._tag = tag 213 self._color = list(initial_color) 214 self._last_press_time = ba.time( 215 ba.TimeType.REAL, ba.TimeFormat.MILLISECONDS 216 ) 217 self._last_press_color_name: str | None = None 218 self._last_press_increasing: bool | None = None 219 self._change_speed = 1.0 220 width = 180.0 221 height = 240.0 222 223 # Creates our _root_widget. 224 PopupWindow.__init__( 225 self, 226 position=position, 227 size=(width, height), 228 scale=scale, 229 focus_position=(10, 10), 230 focus_size=(width - 20, height - 20), 231 bg_color=(0.5, 0.5, 0.5), 232 offset=offset, 233 ) 234 self._swatch = ba.imagewidget( 235 parent=self.root_widget, 236 position=(width * 0.5 - 50, height - 70), 237 size=(100, 70), 238 texture=ba.gettexture('buttonSquare'), 239 color=(1, 0, 0), 240 ) 241 x = 50 242 y = height - 90 243 self._label_r: ba.Widget 244 self._label_g: ba.Widget 245 self._label_b: ba.Widget 246 for color_name, color_val in [ 247 ('r', (1, 0.15, 0.15)), 248 ('g', (0.15, 1, 0.15)), 249 ('b', (0.15, 0.15, 1)), 250 ]: 251 txt = ba.textwidget( 252 parent=self.root_widget, 253 position=(x - 10, y), 254 size=(0, 0), 255 h_align='center', 256 color=color_val, 257 v_align='center', 258 text='0.12', 259 ) 260 setattr(self, '_label_' + color_name, txt) 261 for b_label, bhval, binc in [('-', 30, False), ('+', 75, True)]: 262 ba.buttonwidget( 263 parent=self.root_widget, 264 position=(x + bhval, y - 15), 265 scale=0.8, 266 repeat=True, 267 text_scale=1.3, 268 size=(40, 40), 269 label=b_label, 270 autoselect=True, 271 enable_sound=False, 272 on_activate_call=ba.WeakCall( 273 self._color_change_press, color_name, binc 274 ), 275 ) 276 y -= 42 277 278 btn = ba.buttonwidget( 279 parent=self.root_widget, 280 position=(width * 0.5 - 40, 10), 281 size=(80, 30), 282 text_scale=0.6, 283 color=(0.6, 0.6, 0.6), 284 textcolor=(0.7, 0.7, 0.7), 285 label=ba.Lstr(resource='doneText'), 286 on_activate_call=ba.WeakCall(self._transition_out), 287 autoselect=True, 288 ) 289 ba.containerwidget(edit=self.root_widget, start_button=btn) 290 291 # Unlike the swatch picker, we stay open and constantly push our 292 # color to the delegate, so start doing that. 293 self._update_for_color()