bauiv1lib.settings.keyboard
Keyboard settings related UI functionality.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Keyboard settings related UI functionality.""" 4 5from __future__ import annotations 6 7from typing import TYPE_CHECKING, override 8 9from bauiv1lib.popup import PopupMenuWindow 10import bauiv1 as bui 11import bascenev1 as bs 12 13if TYPE_CHECKING: 14 from typing import Any 15 16 from bauiv1lib.popup import PopupWindow 17 18 19class ConfigKeyboardWindow(bui.MainWindow): 20 """Window for configuring keyboards.""" 21 22 def __init__( 23 self, 24 c: bs.InputDevice, 25 transition: str | None = 'in_right', 26 origin_widget: bui.Widget | None = None, 27 ): 28 self._r = 'configKeyboardWindow' 29 self._input = c 30 self._name = self._input.name 31 self._unique_id = self._input.unique_identifier 32 dname_raw = self._name 33 if self._unique_id != '#1': 34 dname_raw += ' ' + self._unique_id.replace('#', 'P') 35 self._displayname = bui.Lstr(translate=('inputDeviceNames', dname_raw)) 36 self._width = 700 37 if self._unique_id != '#1': 38 self._height = 480 39 else: 40 self._height = 375 41 self._spacing = 40 42 assert bui.app.classic is not None 43 uiscale = bui.app.ui_v1.uiscale 44 super().__init__( 45 root_widget=bui.containerwidget( 46 size=(self._width, self._height), 47 scale=( 48 1.4 49 if uiscale is bui.UIScale.SMALL 50 else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0 51 ), 52 stack_offset=(0, 5) if uiscale is bui.UIScale.SMALL else (0, 0), 53 transition=transition, 54 ), 55 transition=transition, 56 origin_widget=origin_widget, 57 ) 58 59 self._settings: dict[str, int] = {} 60 self._get_config_mapping() 61 62 self._rebuild_ui() 63 64 @override 65 def get_main_window_state(self) -> bui.MainWindowState: 66 # Support recreating our window for back/refresh purposes. 67 cls = type(self) 68 69 # Pull things from self here; if we do it within the lambda 70 # we'll keep self alive which is bad. 71 inputdevice = self._input 72 73 return bui.BasicMainWindowState( 74 create_call=lambda transition, origin_widget: cls( 75 transition=transition, 76 origin_widget=origin_widget, 77 c=inputdevice, 78 ) 79 ) 80 81 def _get_config_mapping(self, default: bool = False) -> None: 82 for button in [ 83 'buttonJump', 84 'buttonPunch', 85 'buttonBomb', 86 'buttonPickUp', 87 'buttonStart', 88 'buttonStart2', 89 'buttonUp', 90 'buttonDown', 91 'buttonLeft', 92 'buttonRight', 93 ]: 94 assert bui.app.classic is not None 95 self._settings[button] = ( 96 bui.app.classic.get_input_device_mapped_value( 97 self._input, button, default 98 ) 99 ) 100 101 def _rebuild_ui(self, is_reset: bool = False) -> None: 102 assert bui.app.classic is not None 103 104 for widget in self._root_widget.get_children(): 105 widget.delete() 106 107 # b_off = 0 if self._unique_id != '#1' else 9 108 cancel_button = bui.buttonwidget( 109 parent=self._root_widget, 110 autoselect=True, 111 position=(38, self._height - 85), 112 size=(170, 60), 113 label=bui.Lstr(resource='cancelText'), 114 scale=0.9, 115 on_activate_call=self.main_window_back, 116 ) 117 save_button = bui.buttonwidget( 118 parent=self._root_widget, 119 autoselect=True, 120 position=(self._width - 190, self._height - 85), 121 size=(180, 60), 122 label=bui.Lstr(resource='saveText'), 123 scale=0.9, 124 text_scale=0.9, 125 on_activate_call=self._save, 126 ) 127 bui.containerwidget( 128 edit=self._root_widget, 129 cancel_button=cancel_button, 130 start_button=save_button, 131 ) 132 133 v = self._height - 74.0 134 bui.textwidget( 135 parent=self._root_widget, 136 position=(self._width * 0.5, v + 15), 137 size=(0, 0), 138 text=bui.Lstr( 139 resource=f'{self._r}.configuringText', 140 subs=[('${DEVICE}', self._displayname)], 141 ), 142 color=bui.app.ui_v1.title_color, 143 h_align='center', 144 v_align='center', 145 maxwidth=270, 146 scale=0.83, 147 ) 148 v -= 20 149 150 if self._unique_id != '#1': 151 v -= 20 152 v -= self._spacing 153 bui.textwidget( 154 parent=self._root_widget, 155 position=(0, v + 19), 156 size=(self._width, 50), 157 text=bui.Lstr(resource=f'{self._r}.keyboard2NoteText'), 158 scale=0.7, 159 maxwidth=self._width * 0.75, 160 max_height=110, 161 color=bui.app.ui_v1.infotextcolor, 162 h_align='center', 163 v_align='top', 164 ) 165 v -= 40 166 v -= 10 167 v -= self._spacing * 2.2 168 v += 25 169 v -= 42 170 h_offs = 160 171 dist = 70 172 d_color = (0.4, 0.4, 0.8) 173 self._capture_button( 174 pos=(h_offs, v + 0.95 * dist), 175 color=d_color, 176 button='buttonUp', 177 texture=bui.gettexture('upButton'), 178 scale=1.0, 179 ) 180 self._capture_button( 181 pos=(h_offs - 1.2 * dist, v), 182 color=d_color, 183 button='buttonLeft', 184 texture=bui.gettexture('leftButton'), 185 scale=1.0, 186 ) 187 self._capture_button( 188 pos=(h_offs + 1.2 * dist, v), 189 color=d_color, 190 button='buttonRight', 191 texture=bui.gettexture('rightButton'), 192 scale=1.0, 193 ) 194 self._capture_button( 195 pos=(h_offs, v - 0.95 * dist), 196 color=d_color, 197 button='buttonDown', 198 texture=bui.gettexture('downButton'), 199 scale=1.0, 200 ) 201 202 if self._unique_id == '#2': 203 self._capture_button( 204 pos=(self._width * 0.5, v + 0.1 * dist), 205 color=(0.4, 0.4, 0.6), 206 button='buttonStart', 207 texture=bui.gettexture('startButton'), 208 scale=0.8, 209 ) 210 211 h_offs = self._width - 160 212 213 self._capture_button( 214 pos=(h_offs, v + 0.95 * dist), 215 color=(0.6, 0.4, 0.8), 216 button='buttonPickUp', 217 texture=bui.gettexture('buttonPickUp'), 218 scale=1.0, 219 ) 220 self._capture_button( 221 pos=(h_offs - 1.2 * dist, v), 222 color=(0.7, 0.5, 0.1), 223 button='buttonPunch', 224 texture=bui.gettexture('buttonPunch'), 225 scale=1.0, 226 ) 227 self._capture_button( 228 pos=(h_offs + 1.2 * dist, v), 229 color=(0.5, 0.2, 0.1), 230 button='buttonBomb', 231 texture=bui.gettexture('buttonBomb'), 232 scale=1.0, 233 ) 234 self._capture_button( 235 pos=(h_offs, v - 0.95 * dist), 236 color=(0.2, 0.5, 0.2), 237 button='buttonJump', 238 texture=bui.gettexture('buttonJump'), 239 scale=1.0, 240 ) 241 242 self._more_button = bui.buttonwidget( 243 parent=self._root_widget, 244 autoselect=True, 245 label='...', 246 text_scale=0.9, 247 color=(0.45, 0.4, 0.5), 248 textcolor=(0.65, 0.6, 0.7), 249 position=(self._width * 0.5 - 65, 30), 250 size=(130, 40), 251 on_activate_call=self._do_more, 252 ) 253 254 if is_reset: 255 bui.containerwidget( 256 edit=self._root_widget, 257 selected_child=self._more_button, 258 ) 259 260 def _pretty_button_name(self, button_name: str) -> bui.Lstr: 261 button_id = self._settings[button_name] 262 if button_id == -1: 263 return bs.Lstr(resource='configGamepadWindow.unsetText') 264 return self._input.get_button_name(button_id) 265 266 def _capture_button( 267 self, 268 pos: tuple[float, float], 269 color: tuple[float, float, float], 270 texture: bui.Texture, 271 button: str, 272 scale: float = 1.0, 273 ) -> None: 274 base_size = 79 275 btn = bui.buttonwidget( 276 parent=self._root_widget, 277 autoselect=True, 278 position=( 279 pos[0] - base_size * 0.5 * scale, 280 pos[1] - base_size * 0.5 * scale, 281 ), 282 size=(base_size * scale, base_size * scale), 283 texture=texture, 284 label='', 285 color=color, 286 ) 287 288 # Do this deferred so it shows up on top of other buttons. (ew.) 289 def doit() -> None: 290 if not self._root_widget: 291 return 292 uiscale = 0.66 * scale * 2.0 293 maxwidth = 76.0 * scale 294 txt = bui.textwidget( 295 parent=self._root_widget, 296 position=(pos[0] + 0.0 * scale, pos[1] - (57.0 - 18.0) * scale), 297 color=(1, 1, 1, 0.3), 298 size=(0, 0), 299 h_align='center', 300 v_align='top', 301 scale=uiscale, 302 maxwidth=maxwidth, 303 text=self._pretty_button_name(button), 304 ) 305 bui.buttonwidget( 306 edit=btn, 307 autoselect=True, 308 on_activate_call=bui.Call( 309 AwaitKeyboardInputWindow, button, txt, self._settings 310 ), 311 ) 312 313 bui.pushcall(doit) 314 315 def _reset(self) -> None: 316 from bauiv1lib.confirm import ConfirmWindow 317 318 assert bui.app.classic is not None 319 320 # efro note: I think it's ok to reset without a confirm here 321 # because the user can see pretty clearly what changes and can 322 # cancel out of the keyboard settings edit if they want. 323 if bool(False): 324 ConfirmWindow( 325 # TODO: Implement a translation string for this! 326 'Are you sure you want to reset your button mapping?', 327 self._do_reset, 328 width=480, 329 height=95, 330 ) 331 else: 332 self._do_reset() 333 334 def _do_reset(self) -> None: 335 """Resets the input's mapping settings.""" 336 self._settings = {} 337 self._get_config_mapping(default=True) 338 self._rebuild_ui(is_reset=True) 339 bui.getsound('gunCocking').play() 340 341 def _do_more(self) -> None: 342 """Show a burger menu with extra settings.""" 343 # pylint: disable=cyclic-import 344 choices: list[str] = [ 345 'reset', 346 ] 347 choices_display: list[bui.Lstr] = [ 348 bui.Lstr(resource='settingsWindowAdvanced.resetText'), 349 ] 350 351 uiscale = bui.app.ui_v1.uiscale 352 PopupMenuWindow( 353 position=self._more_button.get_screen_space_center(), 354 scale=( 355 2.3 356 if uiscale is bui.UIScale.SMALL 357 else 1.65 if uiscale is bui.UIScale.MEDIUM else 1.23 358 ), 359 width=150, 360 choices=choices, 361 choices_display=choices_display, 362 current_choice='reset', 363 delegate=self, 364 ) 365 366 def popup_menu_selected_choice( 367 self, popup_window: PopupMenuWindow, choice: str 368 ) -> None: 369 """Called when a choice is selected in the popup.""" 370 del popup_window # unused 371 if choice == 'reset': 372 self._reset() 373 else: 374 print(f'invalid choice: {choice}') 375 376 def popup_menu_closing(self, popup_window: PopupWindow) -> None: 377 """Called when the popup is closing.""" 378 379 def _save(self) -> None: 380 # from bauiv1lib.settings.controls import ControlsSettingsWindow 381 382 # no-op if our underlying widget is dead or on its way out. 383 if not self._root_widget or self._root_widget.transitioning_out: 384 return 385 386 assert bui.app.classic is not None 387 # bui.containerwidget(edit=self._root_widget, transition='out_right') 388 bui.getsound('gunCocking').play() 389 390 # There's a chance the device disappeared; handle that gracefully. 391 if not self._input: 392 return 393 394 dst = bui.app.classic.get_input_device_config( 395 self._input, default=False 396 ) 397 dst2: dict[str, Any] = dst[0][dst[1]] 398 dst2.clear() 399 400 # Store any values that aren't -1. 401 for key, val in list(self._settings.items()): 402 if val != -1: 403 dst2[key] = val 404 405 # Send this config to the master-server so we can generate 406 # more defaults in the future. 407 if bui.app.classic is not None: 408 bui.app.classic.master_server_v1_post( 409 'controllerConfig', 410 { 411 'ua': bui.app.classic.legacy_user_agent_string, 412 'name': self._name, 413 'b': bui.app.env.engine_build_number, 414 'config': dst2, 415 'v': 2, 416 }, 417 ) 418 bui.app.config.apply_and_commit() 419 420 self.main_window_back() 421 # bui.app.ui_v1.set_main_window( 422 # ControlsSettingsWindow(transition='in_left'), 423 # from_window=self, 424 # is_back=True, 425 # ) 426 427 428class AwaitKeyboardInputWindow(bui.Window): 429 """Window for capturing a keypress.""" 430 431 def __init__(self, button: str, ui: bui.Widget, settings: dict): 432 self._capture_button = button 433 self._capture_key_ui = ui 434 self._settings = settings 435 436 width = 400 437 height = 150 438 assert bui.app.classic is not None 439 uiscale = bui.app.ui_v1.uiscale 440 super().__init__( 441 root_widget=bui.containerwidget( 442 size=(width, height), 443 transition='in_right', 444 scale=( 445 2.0 446 if uiscale is bui.UIScale.SMALL 447 else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 448 ), 449 ) 450 ) 451 bui.textwidget( 452 parent=self._root_widget, 453 position=(0, height - 60), 454 size=(width, 25), 455 text=bui.Lstr(resource='pressAnyKeyText'), 456 h_align='center', 457 v_align='top', 458 ) 459 460 self._counter = 5 461 self._count_down_text = bui.textwidget( 462 parent=self._root_widget, 463 h_align='center', 464 position=(0, height - 110), 465 size=(width, 25), 466 color=(1, 1, 1, 0.3), 467 text=str(self._counter), 468 ) 469 self._decrement_timer: bui.AppTimer | None = bui.AppTimer( 470 1.0, self._decrement, repeat=True 471 ) 472 bs.capture_keyboard_input(bui.WeakCall(self._button_callback)) 473 474 def __del__(self) -> None: 475 bs.release_keyboard_input() 476 477 def _die(self) -> None: 478 # This strong-refs us; killing it allows us to die now. 479 self._decrement_timer = None 480 if self._root_widget: 481 bui.containerwidget(edit=self._root_widget, transition='out_left') 482 483 def _button_callback(self, event: dict[str, Any]) -> None: 484 self._settings[self._capture_button] = event['button'] 485 if event['type'] == 'BUTTONDOWN': 486 bname = event['input_device'].get_button_name(event['button']) 487 bui.textwidget(edit=self._capture_key_ui, text=bname) 488 bui.getsound('gunCocking').play() 489 self._die() 490 491 def _decrement(self) -> None: 492 self._counter -= 1 493 if self._counter >= 1: 494 bui.textwidget(edit=self._count_down_text, text=str(self._counter)) 495 else: 496 self._die()
class
ConfigKeyboardWindow(bauiv1._uitypes.MainWindow):
20class ConfigKeyboardWindow(bui.MainWindow): 21 """Window for configuring keyboards.""" 22 23 def __init__( 24 self, 25 c: bs.InputDevice, 26 transition: str | None = 'in_right', 27 origin_widget: bui.Widget | None = None, 28 ): 29 self._r = 'configKeyboardWindow' 30 self._input = c 31 self._name = self._input.name 32 self._unique_id = self._input.unique_identifier 33 dname_raw = self._name 34 if self._unique_id != '#1': 35 dname_raw += ' ' + self._unique_id.replace('#', 'P') 36 self._displayname = bui.Lstr(translate=('inputDeviceNames', dname_raw)) 37 self._width = 700 38 if self._unique_id != '#1': 39 self._height = 480 40 else: 41 self._height = 375 42 self._spacing = 40 43 assert bui.app.classic is not None 44 uiscale = bui.app.ui_v1.uiscale 45 super().__init__( 46 root_widget=bui.containerwidget( 47 size=(self._width, self._height), 48 scale=( 49 1.4 50 if uiscale is bui.UIScale.SMALL 51 else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0 52 ), 53 stack_offset=(0, 5) if uiscale is bui.UIScale.SMALL else (0, 0), 54 transition=transition, 55 ), 56 transition=transition, 57 origin_widget=origin_widget, 58 ) 59 60 self._settings: dict[str, int] = {} 61 self._get_config_mapping() 62 63 self._rebuild_ui() 64 65 @override 66 def get_main_window_state(self) -> bui.MainWindowState: 67 # Support recreating our window for back/refresh purposes. 68 cls = type(self) 69 70 # Pull things from self here; if we do it within the lambda 71 # we'll keep self alive which is bad. 72 inputdevice = self._input 73 74 return bui.BasicMainWindowState( 75 create_call=lambda transition, origin_widget: cls( 76 transition=transition, 77 origin_widget=origin_widget, 78 c=inputdevice, 79 ) 80 ) 81 82 def _get_config_mapping(self, default: bool = False) -> None: 83 for button in [ 84 'buttonJump', 85 'buttonPunch', 86 'buttonBomb', 87 'buttonPickUp', 88 'buttonStart', 89 'buttonStart2', 90 'buttonUp', 91 'buttonDown', 92 'buttonLeft', 93 'buttonRight', 94 ]: 95 assert bui.app.classic is not None 96 self._settings[button] = ( 97 bui.app.classic.get_input_device_mapped_value( 98 self._input, button, default 99 ) 100 ) 101 102 def _rebuild_ui(self, is_reset: bool = False) -> None: 103 assert bui.app.classic is not None 104 105 for widget in self._root_widget.get_children(): 106 widget.delete() 107 108 # b_off = 0 if self._unique_id != '#1' else 9 109 cancel_button = bui.buttonwidget( 110 parent=self._root_widget, 111 autoselect=True, 112 position=(38, self._height - 85), 113 size=(170, 60), 114 label=bui.Lstr(resource='cancelText'), 115 scale=0.9, 116 on_activate_call=self.main_window_back, 117 ) 118 save_button = bui.buttonwidget( 119 parent=self._root_widget, 120 autoselect=True, 121 position=(self._width - 190, self._height - 85), 122 size=(180, 60), 123 label=bui.Lstr(resource='saveText'), 124 scale=0.9, 125 text_scale=0.9, 126 on_activate_call=self._save, 127 ) 128 bui.containerwidget( 129 edit=self._root_widget, 130 cancel_button=cancel_button, 131 start_button=save_button, 132 ) 133 134 v = self._height - 74.0 135 bui.textwidget( 136 parent=self._root_widget, 137 position=(self._width * 0.5, v + 15), 138 size=(0, 0), 139 text=bui.Lstr( 140 resource=f'{self._r}.configuringText', 141 subs=[('${DEVICE}', self._displayname)], 142 ), 143 color=bui.app.ui_v1.title_color, 144 h_align='center', 145 v_align='center', 146 maxwidth=270, 147 scale=0.83, 148 ) 149 v -= 20 150 151 if self._unique_id != '#1': 152 v -= 20 153 v -= self._spacing 154 bui.textwidget( 155 parent=self._root_widget, 156 position=(0, v + 19), 157 size=(self._width, 50), 158 text=bui.Lstr(resource=f'{self._r}.keyboard2NoteText'), 159 scale=0.7, 160 maxwidth=self._width * 0.75, 161 max_height=110, 162 color=bui.app.ui_v1.infotextcolor, 163 h_align='center', 164 v_align='top', 165 ) 166 v -= 40 167 v -= 10 168 v -= self._spacing * 2.2 169 v += 25 170 v -= 42 171 h_offs = 160 172 dist = 70 173 d_color = (0.4, 0.4, 0.8) 174 self._capture_button( 175 pos=(h_offs, v + 0.95 * dist), 176 color=d_color, 177 button='buttonUp', 178 texture=bui.gettexture('upButton'), 179 scale=1.0, 180 ) 181 self._capture_button( 182 pos=(h_offs - 1.2 * dist, v), 183 color=d_color, 184 button='buttonLeft', 185 texture=bui.gettexture('leftButton'), 186 scale=1.0, 187 ) 188 self._capture_button( 189 pos=(h_offs + 1.2 * dist, v), 190 color=d_color, 191 button='buttonRight', 192 texture=bui.gettexture('rightButton'), 193 scale=1.0, 194 ) 195 self._capture_button( 196 pos=(h_offs, v - 0.95 * dist), 197 color=d_color, 198 button='buttonDown', 199 texture=bui.gettexture('downButton'), 200 scale=1.0, 201 ) 202 203 if self._unique_id == '#2': 204 self._capture_button( 205 pos=(self._width * 0.5, v + 0.1 * dist), 206 color=(0.4, 0.4, 0.6), 207 button='buttonStart', 208 texture=bui.gettexture('startButton'), 209 scale=0.8, 210 ) 211 212 h_offs = self._width - 160 213 214 self._capture_button( 215 pos=(h_offs, v + 0.95 * dist), 216 color=(0.6, 0.4, 0.8), 217 button='buttonPickUp', 218 texture=bui.gettexture('buttonPickUp'), 219 scale=1.0, 220 ) 221 self._capture_button( 222 pos=(h_offs - 1.2 * dist, v), 223 color=(0.7, 0.5, 0.1), 224 button='buttonPunch', 225 texture=bui.gettexture('buttonPunch'), 226 scale=1.0, 227 ) 228 self._capture_button( 229 pos=(h_offs + 1.2 * dist, v), 230 color=(0.5, 0.2, 0.1), 231 button='buttonBomb', 232 texture=bui.gettexture('buttonBomb'), 233 scale=1.0, 234 ) 235 self._capture_button( 236 pos=(h_offs, v - 0.95 * dist), 237 color=(0.2, 0.5, 0.2), 238 button='buttonJump', 239 texture=bui.gettexture('buttonJump'), 240 scale=1.0, 241 ) 242 243 self._more_button = bui.buttonwidget( 244 parent=self._root_widget, 245 autoselect=True, 246 label='...', 247 text_scale=0.9, 248 color=(0.45, 0.4, 0.5), 249 textcolor=(0.65, 0.6, 0.7), 250 position=(self._width * 0.5 - 65, 30), 251 size=(130, 40), 252 on_activate_call=self._do_more, 253 ) 254 255 if is_reset: 256 bui.containerwidget( 257 edit=self._root_widget, 258 selected_child=self._more_button, 259 ) 260 261 def _pretty_button_name(self, button_name: str) -> bui.Lstr: 262 button_id = self._settings[button_name] 263 if button_id == -1: 264 return bs.Lstr(resource='configGamepadWindow.unsetText') 265 return self._input.get_button_name(button_id) 266 267 def _capture_button( 268 self, 269 pos: tuple[float, float], 270 color: tuple[float, float, float], 271 texture: bui.Texture, 272 button: str, 273 scale: float = 1.0, 274 ) -> None: 275 base_size = 79 276 btn = bui.buttonwidget( 277 parent=self._root_widget, 278 autoselect=True, 279 position=( 280 pos[0] - base_size * 0.5 * scale, 281 pos[1] - base_size * 0.5 * scale, 282 ), 283 size=(base_size * scale, base_size * scale), 284 texture=texture, 285 label='', 286 color=color, 287 ) 288 289 # Do this deferred so it shows up on top of other buttons. (ew.) 290 def doit() -> None: 291 if not self._root_widget: 292 return 293 uiscale = 0.66 * scale * 2.0 294 maxwidth = 76.0 * scale 295 txt = bui.textwidget( 296 parent=self._root_widget, 297 position=(pos[0] + 0.0 * scale, pos[1] - (57.0 - 18.0) * scale), 298 color=(1, 1, 1, 0.3), 299 size=(0, 0), 300 h_align='center', 301 v_align='top', 302 scale=uiscale, 303 maxwidth=maxwidth, 304 text=self._pretty_button_name(button), 305 ) 306 bui.buttonwidget( 307 edit=btn, 308 autoselect=True, 309 on_activate_call=bui.Call( 310 AwaitKeyboardInputWindow, button, txt, self._settings 311 ), 312 ) 313 314 bui.pushcall(doit) 315 316 def _reset(self) -> None: 317 from bauiv1lib.confirm import ConfirmWindow 318 319 assert bui.app.classic is not None 320 321 # efro note: I think it's ok to reset without a confirm here 322 # because the user can see pretty clearly what changes and can 323 # cancel out of the keyboard settings edit if they want. 324 if bool(False): 325 ConfirmWindow( 326 # TODO: Implement a translation string for this! 327 'Are you sure you want to reset your button mapping?', 328 self._do_reset, 329 width=480, 330 height=95, 331 ) 332 else: 333 self._do_reset() 334 335 def _do_reset(self) -> None: 336 """Resets the input's mapping settings.""" 337 self._settings = {} 338 self._get_config_mapping(default=True) 339 self._rebuild_ui(is_reset=True) 340 bui.getsound('gunCocking').play() 341 342 def _do_more(self) -> None: 343 """Show a burger menu with extra settings.""" 344 # pylint: disable=cyclic-import 345 choices: list[str] = [ 346 'reset', 347 ] 348 choices_display: list[bui.Lstr] = [ 349 bui.Lstr(resource='settingsWindowAdvanced.resetText'), 350 ] 351 352 uiscale = bui.app.ui_v1.uiscale 353 PopupMenuWindow( 354 position=self._more_button.get_screen_space_center(), 355 scale=( 356 2.3 357 if uiscale is bui.UIScale.SMALL 358 else 1.65 if uiscale is bui.UIScale.MEDIUM else 1.23 359 ), 360 width=150, 361 choices=choices, 362 choices_display=choices_display, 363 current_choice='reset', 364 delegate=self, 365 ) 366 367 def popup_menu_selected_choice( 368 self, popup_window: PopupMenuWindow, choice: str 369 ) -> None: 370 """Called when a choice is selected in the popup.""" 371 del popup_window # unused 372 if choice == 'reset': 373 self._reset() 374 else: 375 print(f'invalid choice: {choice}') 376 377 def popup_menu_closing(self, popup_window: PopupWindow) -> None: 378 """Called when the popup is closing.""" 379 380 def _save(self) -> None: 381 # from bauiv1lib.settings.controls import ControlsSettingsWindow 382 383 # no-op if our underlying widget is dead or on its way out. 384 if not self._root_widget or self._root_widget.transitioning_out: 385 return 386 387 assert bui.app.classic is not None 388 # bui.containerwidget(edit=self._root_widget, transition='out_right') 389 bui.getsound('gunCocking').play() 390 391 # There's a chance the device disappeared; handle that gracefully. 392 if not self._input: 393 return 394 395 dst = bui.app.classic.get_input_device_config( 396 self._input, default=False 397 ) 398 dst2: dict[str, Any] = dst[0][dst[1]] 399 dst2.clear() 400 401 # Store any values that aren't -1. 402 for key, val in list(self._settings.items()): 403 if val != -1: 404 dst2[key] = val 405 406 # Send this config to the master-server so we can generate 407 # more defaults in the future. 408 if bui.app.classic is not None: 409 bui.app.classic.master_server_v1_post( 410 'controllerConfig', 411 { 412 'ua': bui.app.classic.legacy_user_agent_string, 413 'name': self._name, 414 'b': bui.app.env.engine_build_number, 415 'config': dst2, 416 'v': 2, 417 }, 418 ) 419 bui.app.config.apply_and_commit() 420 421 self.main_window_back() 422 # bui.app.ui_v1.set_main_window( 423 # ControlsSettingsWindow(transition='in_left'), 424 # from_window=self, 425 # is_back=True, 426 # )
Window for configuring keyboards.
ConfigKeyboardWindow( c: _bascenev1.InputDevice, transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
23 def __init__( 24 self, 25 c: bs.InputDevice, 26 transition: str | None = 'in_right', 27 origin_widget: bui.Widget | None = None, 28 ): 29 self._r = 'configKeyboardWindow' 30 self._input = c 31 self._name = self._input.name 32 self._unique_id = self._input.unique_identifier 33 dname_raw = self._name 34 if self._unique_id != '#1': 35 dname_raw += ' ' + self._unique_id.replace('#', 'P') 36 self._displayname = bui.Lstr(translate=('inputDeviceNames', dname_raw)) 37 self._width = 700 38 if self._unique_id != '#1': 39 self._height = 480 40 else: 41 self._height = 375 42 self._spacing = 40 43 assert bui.app.classic is not None 44 uiscale = bui.app.ui_v1.uiscale 45 super().__init__( 46 root_widget=bui.containerwidget( 47 size=(self._width, self._height), 48 scale=( 49 1.4 50 if uiscale is bui.UIScale.SMALL 51 else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0 52 ), 53 stack_offset=(0, 5) if uiscale is bui.UIScale.SMALL else (0, 0), 54 transition=transition, 55 ), 56 transition=transition, 57 origin_widget=origin_widget, 58 ) 59 60 self._settings: dict[str, int] = {} 61 self._get_config_mapping() 62 63 self._rebuild_ui()
Create a MainWindow given a root widget and transition info.
Automatically handles in and out transitions on the provided widget, so there is no need to set transitions when creating it.
65 @override 66 def get_main_window_state(self) -> bui.MainWindowState: 67 # Support recreating our window for back/refresh purposes. 68 cls = type(self) 69 70 # Pull things from self here; if we do it within the lambda 71 # we'll keep self alive which is bad. 72 inputdevice = self._input 73 74 return bui.BasicMainWindowState( 75 create_call=lambda transition, origin_widget: cls( 76 transition=transition, 77 origin_widget=origin_widget, 78 c=inputdevice, 79 ) 80 )
Return a WindowState to recreate this window, if supported.
Inherited Members
- bauiv1._uitypes.MainWindow
- main_window_back_state
- main_window_close
- can_change_main_window
- main_window_back
- main_window_replace
- on_main_window_close
- bauiv1._uitypes.Window
- get_root_widget
class
AwaitKeyboardInputWindow(bauiv1._uitypes.Window):
429class AwaitKeyboardInputWindow(bui.Window): 430 """Window for capturing a keypress.""" 431 432 def __init__(self, button: str, ui: bui.Widget, settings: dict): 433 self._capture_button = button 434 self._capture_key_ui = ui 435 self._settings = settings 436 437 width = 400 438 height = 150 439 assert bui.app.classic is not None 440 uiscale = bui.app.ui_v1.uiscale 441 super().__init__( 442 root_widget=bui.containerwidget( 443 size=(width, height), 444 transition='in_right', 445 scale=( 446 2.0 447 if uiscale is bui.UIScale.SMALL 448 else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 449 ), 450 ) 451 ) 452 bui.textwidget( 453 parent=self._root_widget, 454 position=(0, height - 60), 455 size=(width, 25), 456 text=bui.Lstr(resource='pressAnyKeyText'), 457 h_align='center', 458 v_align='top', 459 ) 460 461 self._counter = 5 462 self._count_down_text = bui.textwidget( 463 parent=self._root_widget, 464 h_align='center', 465 position=(0, height - 110), 466 size=(width, 25), 467 color=(1, 1, 1, 0.3), 468 text=str(self._counter), 469 ) 470 self._decrement_timer: bui.AppTimer | None = bui.AppTimer( 471 1.0, self._decrement, repeat=True 472 ) 473 bs.capture_keyboard_input(bui.WeakCall(self._button_callback)) 474 475 def __del__(self) -> None: 476 bs.release_keyboard_input() 477 478 def _die(self) -> None: 479 # This strong-refs us; killing it allows us to die now. 480 self._decrement_timer = None 481 if self._root_widget: 482 bui.containerwidget(edit=self._root_widget, transition='out_left') 483 484 def _button_callback(self, event: dict[str, Any]) -> None: 485 self._settings[self._capture_button] = event['button'] 486 if event['type'] == 'BUTTONDOWN': 487 bname = event['input_device'].get_button_name(event['button']) 488 bui.textwidget(edit=self._capture_key_ui, text=bname) 489 bui.getsound('gunCocking').play() 490 self._die() 491 492 def _decrement(self) -> None: 493 self._counter -= 1 494 if self._counter >= 1: 495 bui.textwidget(edit=self._count_down_text, text=str(self._counter)) 496 else: 497 self._die()
Window for capturing a keypress.
AwaitKeyboardInputWindow(button: str, ui: _bauiv1.Widget, settings: dict)
432 def __init__(self, button: str, ui: bui.Widget, settings: dict): 433 self._capture_button = button 434 self._capture_key_ui = ui 435 self._settings = settings 436 437 width = 400 438 height = 150 439 assert bui.app.classic is not None 440 uiscale = bui.app.ui_v1.uiscale 441 super().__init__( 442 root_widget=bui.containerwidget( 443 size=(width, height), 444 transition='in_right', 445 scale=( 446 2.0 447 if uiscale is bui.UIScale.SMALL 448 else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 449 ), 450 ) 451 ) 452 bui.textwidget( 453 parent=self._root_widget, 454 position=(0, height - 60), 455 size=(width, 25), 456 text=bui.Lstr(resource='pressAnyKeyText'), 457 h_align='center', 458 v_align='top', 459 ) 460 461 self._counter = 5 462 self._count_down_text = bui.textwidget( 463 parent=self._root_widget, 464 h_align='center', 465 position=(0, height - 110), 466 size=(width, 25), 467 color=(1, 1, 1, 0.3), 468 text=str(self._counter), 469 ) 470 self._decrement_timer: bui.AppTimer | None = bui.AppTimer( 471 1.0, self._decrement, repeat=True 472 ) 473 bs.capture_keyboard_input(bui.WeakCall(self._button_callback))
Inherited Members
- bauiv1._uitypes.Window
- get_root_widget