bauiv1lib.settings.controls
Provides a top level control settings window.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides a top level control settings window.""" 4 5from __future__ import annotations 6 7from typing import override 8 9import bascenev1 as bs 10import bauiv1 as bui 11 12 13class ControlsSettingsWindow(bui.MainWindow): 14 """Top level control settings window.""" 15 16 def __init__( 17 self, 18 transition: str | None = 'in_right', 19 origin_widget: bui.Widget | None = None, 20 ): 21 # FIXME: should tidy up here. 22 # pylint: disable=too-many-statements 23 # pylint: disable=too-many-branches 24 # pylint: disable=too-many-locals 25 # pylint: disable=cyclic-import 26 27 self._have_selected_child = False 28 29 self._r = 'configControllersWindow' 30 uiscale = bui.app.ui_v1.uiscale 31 app = bui.app 32 assert app.classic is not None 33 34 spacing = 50.0 35 button_width = 350.0 36 width = 1200.0 if uiscale is bui.UIScale.SMALL else 560.0 37 height = 800 if uiscale is bui.UIScale.SMALL else 400.0 38 39 # yoffs = -60 if uiscale is bui.UIScale.SMALL else 0 40 space_height = spacing * 0.3 41 42 buttons_height = 0.0 43 44 # FIXME: should create vis settings under platform or 45 # app-adapter to determine whether to show this stuff; not 46 # hard-code it. 47 48 show_gamepads = False 49 platform = app.classic.platform 50 subplatform = app.classic.subplatform 51 non_vr_windows = platform == 'windows' and ( 52 subplatform != 'oculus' or not app.env.vr 53 ) 54 if platform in ('linux', 'android', 'mac') or non_vr_windows: 55 show_gamepads = True 56 buttons_height += spacing 57 58 show_touch = False 59 if bs.have_touchscreen_input(): 60 show_touch = True 61 buttons_height += spacing 62 63 show_space_1 = False 64 if show_gamepads or show_touch: 65 show_space_1 = True 66 buttons_height += space_height 67 68 show_keyboard = False 69 if bs.getinputdevice('Keyboard', '#1', doraise=False) is not None: 70 show_keyboard = True 71 buttons_height += spacing 72 show_keyboard_p2 = False if app.env.vr else show_keyboard 73 if show_keyboard_p2: 74 buttons_height += spacing 75 76 show_space_2 = False 77 if show_keyboard: 78 show_space_2 = True 79 buttons_height += space_height 80 81 if bool(True): 82 show_remote = True 83 buttons_height += spacing 84 else: 85 show_remote = False 86 87 # On windows (outside of oculus/vr), show an option to disable 88 # xinput. 89 show_xinput_toggle = False 90 if platform == 'windows' and not app.env.vr: 91 show_xinput_toggle = True 92 93 if show_xinput_toggle: 94 buttons_height += spacing 95 96 assert bui.app.classic is not None 97 98 # Do some fancy math to fill all available screen area up to the 99 # size of our backing container. This lets us fit to the exact 100 # screen shape at small ui scale. 101 screensize = bui.get_virtual_screen_size() 102 scale = ( 103 2.0 104 if uiscale is bui.UIScale.SMALL 105 else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0 106 ) 107 # Calc screen size in our local container space and clamp to a 108 # bit smaller than our container size. 109 # target_width = min(width - 60, screensize[0] / scale) 110 target_height = min(height - 70, screensize[1] / scale) 111 112 # To get top/left coords, go to the center of our window and 113 # offset by half the width/height of our target area. 114 yoffs = 0.5 * height + 0.5 * target_height + 30.0 115 116 super().__init__( 117 root_widget=bui.containerwidget( 118 size=(width, height), 119 scale=scale, 120 toolbar_visibility=( 121 'menu_minimal' 122 if uiscale is bui.UIScale.SMALL 123 else 'menu_full' 124 ), 125 ), 126 transition=transition, 127 origin_widget=origin_widget, 128 # We're affected by screen size only at small ui-scale. 129 refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, 130 ) 131 132 self._back_button: bui.Widget | None 133 if uiscale is bui.UIScale.SMALL: 134 bui.containerwidget( 135 edit=self._root_widget, on_cancel_call=self.main_window_back 136 ) 137 self._back_button = None 138 else: 139 self._back_button = btn = bui.buttonwidget( 140 parent=self._root_widget, 141 position=(35, height - 60), 142 size=(60, 60), 143 scale=0.8, 144 text_scale=1.2, 145 autoselect=True, 146 label=bui.charstr(bui.SpecialChar.BACK), 147 button_type='backSmall', 148 on_activate_call=self.main_window_back, 149 ) 150 bui.containerwidget(edit=self._root_widget, cancel_button=btn) 151 152 # We need these vars to exist even if the buttons don't. 153 self._gamepads_button: bui.Widget | None = None 154 self._touch_button: bui.Widget | None = None 155 self._keyboard_button: bui.Widget | None = None 156 self._keyboard_2_button: bui.Widget | None = None 157 self._idevices_button: bui.Widget | None = None 158 159 bui.textwidget( 160 parent=self._root_widget, 161 position=( 162 width * 0.5, 163 yoffs - (52 if uiscale is bui.UIScale.SMALL else 32), 164 ), 165 maxwidth=260, 166 size=(0, 0), 167 text=bui.Lstr(resource=f'{self._r}.titleText'), 168 color=bui.app.ui_v1.title_color, 169 h_align='center', 170 v_align='center', 171 ) 172 173 # Roughly center the rest of our stuff. 174 v = height * 0.5 + buttons_height * 0.5 - 10 175 v -= spacing 176 177 if show_touch: 178 self._touch_button = btn = bui.buttonwidget( 179 parent=self._root_widget, 180 position=((width - button_width) / 2, v), 181 size=(button_width, 43), 182 autoselect=True, 183 label=bui.Lstr(resource=f'{self._r}.configureTouchText'), 184 on_activate_call=self._do_touchscreen, 185 ) 186 bui.widget( 187 edit=btn, 188 right_widget=bui.get_special_widget('squad_button'), 189 ) 190 if not self._have_selected_child: 191 bui.containerwidget( 192 edit=self._root_widget, selected_child=self._touch_button 193 ) 194 if self._back_button is not None: 195 bui.widget( 196 edit=self._back_button, down_widget=self._touch_button 197 ) 198 self._have_selected_child = True 199 v -= spacing 200 201 if show_gamepads: 202 self._gamepads_button = btn = bui.buttonwidget( 203 parent=self._root_widget, 204 position=((width - button_width) / 2 - 7, v), 205 size=(button_width, 43), 206 autoselect=True, 207 label=bui.Lstr(resource=f'{self._r}.configureControllersText'), 208 on_activate_call=self._do_gamepads, 209 ) 210 bui.widget( 211 edit=btn, 212 right_widget=bui.get_special_widget('squad_button'), 213 ) 214 if not self._have_selected_child: 215 bui.containerwidget( 216 edit=self._root_widget, selected_child=self._gamepads_button 217 ) 218 if self._back_button is not None: 219 bui.widget( 220 edit=self._back_button, 221 down_widget=self._gamepads_button, 222 ) 223 self._have_selected_child = True 224 v -= spacing 225 else: 226 self._gamepads_button = None 227 228 if show_space_1: 229 v -= space_height 230 231 if show_keyboard: 232 self._keyboard_button = btn = bui.buttonwidget( 233 parent=self._root_widget, 234 position=((width - button_width) / 2 - 5, v), 235 size=(button_width, 43), 236 autoselect=True, 237 label=bui.Lstr(resource=f'{self._r}.configureKeyboardText'), 238 on_activate_call=self._config_keyboard, 239 ) 240 bui.widget( 241 edit=self._keyboard_button, left_widget=self._keyboard_button 242 ) 243 bui.widget( 244 edit=btn, 245 right_widget=bui.get_special_widget('squad_button'), 246 ) 247 if not self._have_selected_child: 248 bui.containerwidget( 249 edit=self._root_widget, selected_child=self._keyboard_button 250 ) 251 if self._back_button is not None: 252 bui.widget( 253 edit=self._back_button, 254 down_widget=self._keyboard_button, 255 ) 256 self._have_selected_child = True 257 v -= spacing 258 if show_keyboard_p2: 259 self._keyboard_2_button = bui.buttonwidget( 260 parent=self._root_widget, 261 position=((width - button_width) / 2 - 3, v), 262 size=(button_width, 43), 263 autoselect=True, 264 label=bui.Lstr(resource=f'{self._r}.configureKeyboard2Text'), 265 on_activate_call=self._config_keyboard2, 266 ) 267 v -= spacing 268 bui.widget( 269 edit=self._keyboard_2_button, 270 left_widget=self._keyboard_2_button, 271 ) 272 if show_space_2: 273 v -= space_height 274 if show_remote: 275 self._idevices_button = btn = bui.buttonwidget( 276 parent=self._root_widget, 277 position=((width - button_width) / 2 - 5, v), 278 size=(button_width, 43), 279 autoselect=True, 280 label=bui.Lstr(resource=f'{self._r}.configureMobileText'), 281 on_activate_call=self._do_mobile_devices, 282 ) 283 bui.widget( 284 edit=self._idevices_button, left_widget=self._idevices_button 285 ) 286 bui.widget( 287 edit=btn, 288 right_widget=bui.get_special_widget('squad_button'), 289 ) 290 if not self._have_selected_child: 291 bui.containerwidget( 292 edit=self._root_widget, selected_child=self._idevices_button 293 ) 294 if self._back_button is not None: 295 bui.widget( 296 edit=self._back_button, 297 down_widget=self._idevices_button, 298 ) 299 self._have_selected_child = True 300 v -= spacing 301 302 if show_xinput_toggle: 303 304 def do_toggle(value: bool) -> None: 305 bui.screenmessage( 306 bui.Lstr(resource='settingsWindowAdvanced.mustRestartText'), 307 color=(1, 1, 0), 308 ) 309 bui.getsound('gunCocking').play() 310 bui.set_low_level_config_value('enablexinput', not value) 311 312 xinput_checkbox = bui.checkboxwidget( 313 parent=self._root_widget, 314 position=( 315 width * (0.35 if uiscale is bui.UIScale.SMALL else 0.25), 316 v + 3, 317 ), 318 size=(120, 30), 319 value=(not bui.get_low_level_config_value('enablexinput', 1)), 320 maxwidth=200, 321 on_value_change_call=do_toggle, 322 text=bui.Lstr(resource='disableXInputText'), 323 autoselect=True, 324 ) 325 bui.textwidget( 326 parent=self._root_widget, 327 position=(width * 0.5, v - 5), 328 size=(0, 0), 329 text=bui.Lstr(resource='disableXInputDescriptionText'), 330 scale=0.5, 331 h_align='center', 332 v_align='center', 333 color=bui.app.ui_v1.infotextcolor, 334 maxwidth=width * 0.8, 335 ) 336 bui.widget( 337 edit=xinput_checkbox, 338 left_widget=xinput_checkbox, 339 right_widget=xinput_checkbox, 340 ) 341 v -= spacing 342 343 self._restore_state() 344 345 @override 346 def get_main_window_state(self) -> bui.MainWindowState: 347 # Support recreating our window for back/refresh purposes. 348 cls = type(self) 349 return bui.BasicMainWindowState( 350 create_call=lambda transition, origin_widget: cls( 351 transition=transition, origin_widget=origin_widget 352 ) 353 ) 354 355 @override 356 def on_main_window_close(self) -> None: 357 self._save_state() 358 359 def _set_mac_controller_subsystem(self, val: str) -> None: 360 cfg = bui.app.config 361 cfg['Mac Controller Subsystem'] = val 362 cfg.apply_and_commit() 363 364 def _config_keyboard(self) -> None: 365 # pylint: disable=cyclic-import 366 from bauiv1lib.settings.keyboard import ConfigKeyboardWindow 367 368 # no-op if we're not in control. 369 if not self.main_window_has_control(): 370 return 371 372 self.main_window_replace( 373 ConfigKeyboardWindow(bs.getinputdevice('Keyboard', '#1')) 374 ) 375 376 def _config_keyboard2(self) -> None: 377 # pylint: disable=cyclic-import 378 from bauiv1lib.settings.keyboard import ConfigKeyboardWindow 379 380 # no-op if we're not in control. 381 if not self.main_window_has_control(): 382 return 383 384 self.main_window_replace( 385 ConfigKeyboardWindow(bs.getinputdevice('Keyboard', '#2')) 386 ) 387 388 def _do_mobile_devices(self) -> None: 389 # pylint: disable=cyclic-import 390 from bauiv1lib.settings.remoteapp import RemoteAppSettingsWindow 391 392 # no-op if we're not in control. 393 if not self.main_window_has_control(): 394 return 395 396 self.main_window_replace(RemoteAppSettingsWindow()) 397 398 def _do_gamepads(self) -> None: 399 # pylint: disable=cyclic-import 400 from bauiv1lib.settings.gamepadselect import GamepadSelectWindow 401 402 # no-op if we're not in control. 403 if not self.main_window_has_control(): 404 return 405 406 self.main_window_replace(GamepadSelectWindow()) 407 408 def _do_touchscreen(self) -> None: 409 # pylint: disable=cyclic-import 410 from bauiv1lib.settings.touchscreen import TouchscreenSettingsWindow 411 412 # no-op if we're not in control. 413 if not self.main_window_has_control(): 414 return 415 416 self.main_window_replace(TouchscreenSettingsWindow()) 417 418 def _save_state(self) -> None: 419 sel = self._root_widget.get_selected_child() 420 if sel == self._gamepads_button: 421 sel_name = 'GamePads' 422 elif sel == self._touch_button: 423 sel_name = 'Touch' 424 elif sel == self._keyboard_button: 425 sel_name = 'Keyboard' 426 elif sel == self._keyboard_2_button: 427 sel_name = 'Keyboard2' 428 elif sel == self._idevices_button: 429 sel_name = 'iDevices' 430 else: 431 sel_name = 'Back' 432 assert bui.app.classic is not None 433 bui.app.ui_v1.window_states[type(self)] = sel_name 434 435 def _restore_state(self) -> None: 436 assert bui.app.classic is not None 437 sel_name = bui.app.ui_v1.window_states.get(type(self)) 438 if sel_name == 'GamePads': 439 sel = self._gamepads_button 440 elif sel_name == 'Touch': 441 sel = self._touch_button 442 elif sel_name == 'Keyboard': 443 sel = self._keyboard_button 444 elif sel_name == 'Keyboard2': 445 sel = self._keyboard_2_button 446 elif sel_name == 'iDevices': 447 sel = self._idevices_button 448 elif sel_name == 'Back': 449 sel = self._back_button 450 else: 451 sel = ( 452 self._gamepads_button 453 if self._gamepads_button is not None 454 else self._back_button 455 ) 456 bui.containerwidget(edit=self._root_widget, selected_child=sel)
class
ControlsSettingsWindow(bauiv1._uitypes.MainWindow):
14class ControlsSettingsWindow(bui.MainWindow): 15 """Top level control settings window.""" 16 17 def __init__( 18 self, 19 transition: str | None = 'in_right', 20 origin_widget: bui.Widget | None = None, 21 ): 22 # FIXME: should tidy up here. 23 # pylint: disable=too-many-statements 24 # pylint: disable=too-many-branches 25 # pylint: disable=too-many-locals 26 # pylint: disable=cyclic-import 27 28 self._have_selected_child = False 29 30 self._r = 'configControllersWindow' 31 uiscale = bui.app.ui_v1.uiscale 32 app = bui.app 33 assert app.classic is not None 34 35 spacing = 50.0 36 button_width = 350.0 37 width = 1200.0 if uiscale is bui.UIScale.SMALL else 560.0 38 height = 800 if uiscale is bui.UIScale.SMALL else 400.0 39 40 # yoffs = -60 if uiscale is bui.UIScale.SMALL else 0 41 space_height = spacing * 0.3 42 43 buttons_height = 0.0 44 45 # FIXME: should create vis settings under platform or 46 # app-adapter to determine whether to show this stuff; not 47 # hard-code it. 48 49 show_gamepads = False 50 platform = app.classic.platform 51 subplatform = app.classic.subplatform 52 non_vr_windows = platform == 'windows' and ( 53 subplatform != 'oculus' or not app.env.vr 54 ) 55 if platform in ('linux', 'android', 'mac') or non_vr_windows: 56 show_gamepads = True 57 buttons_height += spacing 58 59 show_touch = False 60 if bs.have_touchscreen_input(): 61 show_touch = True 62 buttons_height += spacing 63 64 show_space_1 = False 65 if show_gamepads or show_touch: 66 show_space_1 = True 67 buttons_height += space_height 68 69 show_keyboard = False 70 if bs.getinputdevice('Keyboard', '#1', doraise=False) is not None: 71 show_keyboard = True 72 buttons_height += spacing 73 show_keyboard_p2 = False if app.env.vr else show_keyboard 74 if show_keyboard_p2: 75 buttons_height += spacing 76 77 show_space_2 = False 78 if show_keyboard: 79 show_space_2 = True 80 buttons_height += space_height 81 82 if bool(True): 83 show_remote = True 84 buttons_height += spacing 85 else: 86 show_remote = False 87 88 # On windows (outside of oculus/vr), show an option to disable 89 # xinput. 90 show_xinput_toggle = False 91 if platform == 'windows' and not app.env.vr: 92 show_xinput_toggle = True 93 94 if show_xinput_toggle: 95 buttons_height += spacing 96 97 assert bui.app.classic is not None 98 99 # Do some fancy math to fill all available screen area up to the 100 # size of our backing container. This lets us fit to the exact 101 # screen shape at small ui scale. 102 screensize = bui.get_virtual_screen_size() 103 scale = ( 104 2.0 105 if uiscale is bui.UIScale.SMALL 106 else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0 107 ) 108 # Calc screen size in our local container space and clamp to a 109 # bit smaller than our container size. 110 # target_width = min(width - 60, screensize[0] / scale) 111 target_height = min(height - 70, screensize[1] / scale) 112 113 # To get top/left coords, go to the center of our window and 114 # offset by half the width/height of our target area. 115 yoffs = 0.5 * height + 0.5 * target_height + 30.0 116 117 super().__init__( 118 root_widget=bui.containerwidget( 119 size=(width, height), 120 scale=scale, 121 toolbar_visibility=( 122 'menu_minimal' 123 if uiscale is bui.UIScale.SMALL 124 else 'menu_full' 125 ), 126 ), 127 transition=transition, 128 origin_widget=origin_widget, 129 # We're affected by screen size only at small ui-scale. 130 refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, 131 ) 132 133 self._back_button: bui.Widget | None 134 if uiscale is bui.UIScale.SMALL: 135 bui.containerwidget( 136 edit=self._root_widget, on_cancel_call=self.main_window_back 137 ) 138 self._back_button = None 139 else: 140 self._back_button = btn = bui.buttonwidget( 141 parent=self._root_widget, 142 position=(35, height - 60), 143 size=(60, 60), 144 scale=0.8, 145 text_scale=1.2, 146 autoselect=True, 147 label=bui.charstr(bui.SpecialChar.BACK), 148 button_type='backSmall', 149 on_activate_call=self.main_window_back, 150 ) 151 bui.containerwidget(edit=self._root_widget, cancel_button=btn) 152 153 # We need these vars to exist even if the buttons don't. 154 self._gamepads_button: bui.Widget | None = None 155 self._touch_button: bui.Widget | None = None 156 self._keyboard_button: bui.Widget | None = None 157 self._keyboard_2_button: bui.Widget | None = None 158 self._idevices_button: bui.Widget | None = None 159 160 bui.textwidget( 161 parent=self._root_widget, 162 position=( 163 width * 0.5, 164 yoffs - (52 if uiscale is bui.UIScale.SMALL else 32), 165 ), 166 maxwidth=260, 167 size=(0, 0), 168 text=bui.Lstr(resource=f'{self._r}.titleText'), 169 color=bui.app.ui_v1.title_color, 170 h_align='center', 171 v_align='center', 172 ) 173 174 # Roughly center the rest of our stuff. 175 v = height * 0.5 + buttons_height * 0.5 - 10 176 v -= spacing 177 178 if show_touch: 179 self._touch_button = btn = bui.buttonwidget( 180 parent=self._root_widget, 181 position=((width - button_width) / 2, v), 182 size=(button_width, 43), 183 autoselect=True, 184 label=bui.Lstr(resource=f'{self._r}.configureTouchText'), 185 on_activate_call=self._do_touchscreen, 186 ) 187 bui.widget( 188 edit=btn, 189 right_widget=bui.get_special_widget('squad_button'), 190 ) 191 if not self._have_selected_child: 192 bui.containerwidget( 193 edit=self._root_widget, selected_child=self._touch_button 194 ) 195 if self._back_button is not None: 196 bui.widget( 197 edit=self._back_button, down_widget=self._touch_button 198 ) 199 self._have_selected_child = True 200 v -= spacing 201 202 if show_gamepads: 203 self._gamepads_button = btn = bui.buttonwidget( 204 parent=self._root_widget, 205 position=((width - button_width) / 2 - 7, v), 206 size=(button_width, 43), 207 autoselect=True, 208 label=bui.Lstr(resource=f'{self._r}.configureControllersText'), 209 on_activate_call=self._do_gamepads, 210 ) 211 bui.widget( 212 edit=btn, 213 right_widget=bui.get_special_widget('squad_button'), 214 ) 215 if not self._have_selected_child: 216 bui.containerwidget( 217 edit=self._root_widget, selected_child=self._gamepads_button 218 ) 219 if self._back_button is not None: 220 bui.widget( 221 edit=self._back_button, 222 down_widget=self._gamepads_button, 223 ) 224 self._have_selected_child = True 225 v -= spacing 226 else: 227 self._gamepads_button = None 228 229 if show_space_1: 230 v -= space_height 231 232 if show_keyboard: 233 self._keyboard_button = btn = bui.buttonwidget( 234 parent=self._root_widget, 235 position=((width - button_width) / 2 - 5, v), 236 size=(button_width, 43), 237 autoselect=True, 238 label=bui.Lstr(resource=f'{self._r}.configureKeyboardText'), 239 on_activate_call=self._config_keyboard, 240 ) 241 bui.widget( 242 edit=self._keyboard_button, left_widget=self._keyboard_button 243 ) 244 bui.widget( 245 edit=btn, 246 right_widget=bui.get_special_widget('squad_button'), 247 ) 248 if not self._have_selected_child: 249 bui.containerwidget( 250 edit=self._root_widget, selected_child=self._keyboard_button 251 ) 252 if self._back_button is not None: 253 bui.widget( 254 edit=self._back_button, 255 down_widget=self._keyboard_button, 256 ) 257 self._have_selected_child = True 258 v -= spacing 259 if show_keyboard_p2: 260 self._keyboard_2_button = bui.buttonwidget( 261 parent=self._root_widget, 262 position=((width - button_width) / 2 - 3, v), 263 size=(button_width, 43), 264 autoselect=True, 265 label=bui.Lstr(resource=f'{self._r}.configureKeyboard2Text'), 266 on_activate_call=self._config_keyboard2, 267 ) 268 v -= spacing 269 bui.widget( 270 edit=self._keyboard_2_button, 271 left_widget=self._keyboard_2_button, 272 ) 273 if show_space_2: 274 v -= space_height 275 if show_remote: 276 self._idevices_button = btn = bui.buttonwidget( 277 parent=self._root_widget, 278 position=((width - button_width) / 2 - 5, v), 279 size=(button_width, 43), 280 autoselect=True, 281 label=bui.Lstr(resource=f'{self._r}.configureMobileText'), 282 on_activate_call=self._do_mobile_devices, 283 ) 284 bui.widget( 285 edit=self._idevices_button, left_widget=self._idevices_button 286 ) 287 bui.widget( 288 edit=btn, 289 right_widget=bui.get_special_widget('squad_button'), 290 ) 291 if not self._have_selected_child: 292 bui.containerwidget( 293 edit=self._root_widget, selected_child=self._idevices_button 294 ) 295 if self._back_button is not None: 296 bui.widget( 297 edit=self._back_button, 298 down_widget=self._idevices_button, 299 ) 300 self._have_selected_child = True 301 v -= spacing 302 303 if show_xinput_toggle: 304 305 def do_toggle(value: bool) -> None: 306 bui.screenmessage( 307 bui.Lstr(resource='settingsWindowAdvanced.mustRestartText'), 308 color=(1, 1, 0), 309 ) 310 bui.getsound('gunCocking').play() 311 bui.set_low_level_config_value('enablexinput', not value) 312 313 xinput_checkbox = bui.checkboxwidget( 314 parent=self._root_widget, 315 position=( 316 width * (0.35 if uiscale is bui.UIScale.SMALL else 0.25), 317 v + 3, 318 ), 319 size=(120, 30), 320 value=(not bui.get_low_level_config_value('enablexinput', 1)), 321 maxwidth=200, 322 on_value_change_call=do_toggle, 323 text=bui.Lstr(resource='disableXInputText'), 324 autoselect=True, 325 ) 326 bui.textwidget( 327 parent=self._root_widget, 328 position=(width * 0.5, v - 5), 329 size=(0, 0), 330 text=bui.Lstr(resource='disableXInputDescriptionText'), 331 scale=0.5, 332 h_align='center', 333 v_align='center', 334 color=bui.app.ui_v1.infotextcolor, 335 maxwidth=width * 0.8, 336 ) 337 bui.widget( 338 edit=xinput_checkbox, 339 left_widget=xinput_checkbox, 340 right_widget=xinput_checkbox, 341 ) 342 v -= spacing 343 344 self._restore_state() 345 346 @override 347 def get_main_window_state(self) -> bui.MainWindowState: 348 # Support recreating our window for back/refresh purposes. 349 cls = type(self) 350 return bui.BasicMainWindowState( 351 create_call=lambda transition, origin_widget: cls( 352 transition=transition, origin_widget=origin_widget 353 ) 354 ) 355 356 @override 357 def on_main_window_close(self) -> None: 358 self._save_state() 359 360 def _set_mac_controller_subsystem(self, val: str) -> None: 361 cfg = bui.app.config 362 cfg['Mac Controller Subsystem'] = val 363 cfg.apply_and_commit() 364 365 def _config_keyboard(self) -> None: 366 # pylint: disable=cyclic-import 367 from bauiv1lib.settings.keyboard import ConfigKeyboardWindow 368 369 # no-op if we're not in control. 370 if not self.main_window_has_control(): 371 return 372 373 self.main_window_replace( 374 ConfigKeyboardWindow(bs.getinputdevice('Keyboard', '#1')) 375 ) 376 377 def _config_keyboard2(self) -> None: 378 # pylint: disable=cyclic-import 379 from bauiv1lib.settings.keyboard import ConfigKeyboardWindow 380 381 # no-op if we're not in control. 382 if not self.main_window_has_control(): 383 return 384 385 self.main_window_replace( 386 ConfigKeyboardWindow(bs.getinputdevice('Keyboard', '#2')) 387 ) 388 389 def _do_mobile_devices(self) -> None: 390 # pylint: disable=cyclic-import 391 from bauiv1lib.settings.remoteapp import RemoteAppSettingsWindow 392 393 # no-op if we're not in control. 394 if not self.main_window_has_control(): 395 return 396 397 self.main_window_replace(RemoteAppSettingsWindow()) 398 399 def _do_gamepads(self) -> None: 400 # pylint: disable=cyclic-import 401 from bauiv1lib.settings.gamepadselect import GamepadSelectWindow 402 403 # no-op if we're not in control. 404 if not self.main_window_has_control(): 405 return 406 407 self.main_window_replace(GamepadSelectWindow()) 408 409 def _do_touchscreen(self) -> None: 410 # pylint: disable=cyclic-import 411 from bauiv1lib.settings.touchscreen import TouchscreenSettingsWindow 412 413 # no-op if we're not in control. 414 if not self.main_window_has_control(): 415 return 416 417 self.main_window_replace(TouchscreenSettingsWindow()) 418 419 def _save_state(self) -> None: 420 sel = self._root_widget.get_selected_child() 421 if sel == self._gamepads_button: 422 sel_name = 'GamePads' 423 elif sel == self._touch_button: 424 sel_name = 'Touch' 425 elif sel == self._keyboard_button: 426 sel_name = 'Keyboard' 427 elif sel == self._keyboard_2_button: 428 sel_name = 'Keyboard2' 429 elif sel == self._idevices_button: 430 sel_name = 'iDevices' 431 else: 432 sel_name = 'Back' 433 assert bui.app.classic is not None 434 bui.app.ui_v1.window_states[type(self)] = sel_name 435 436 def _restore_state(self) -> None: 437 assert bui.app.classic is not None 438 sel_name = bui.app.ui_v1.window_states.get(type(self)) 439 if sel_name == 'GamePads': 440 sel = self._gamepads_button 441 elif sel_name == 'Touch': 442 sel = self._touch_button 443 elif sel_name == 'Keyboard': 444 sel = self._keyboard_button 445 elif sel_name == 'Keyboard2': 446 sel = self._keyboard_2_button 447 elif sel_name == 'iDevices': 448 sel = self._idevices_button 449 elif sel_name == 'Back': 450 sel = self._back_button 451 else: 452 sel = ( 453 self._gamepads_button 454 if self._gamepads_button is not None 455 else self._back_button 456 ) 457 bui.containerwidget(edit=self._root_widget, selected_child=sel)
Top level control settings window.
ControlsSettingsWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
17 def __init__( 18 self, 19 transition: str | None = 'in_right', 20 origin_widget: bui.Widget | None = None, 21 ): 22 # FIXME: should tidy up here. 23 # pylint: disable=too-many-statements 24 # pylint: disable=too-many-branches 25 # pylint: disable=too-many-locals 26 # pylint: disable=cyclic-import 27 28 self._have_selected_child = False 29 30 self._r = 'configControllersWindow' 31 uiscale = bui.app.ui_v1.uiscale 32 app = bui.app 33 assert app.classic is not None 34 35 spacing = 50.0 36 button_width = 350.0 37 width = 1200.0 if uiscale is bui.UIScale.SMALL else 560.0 38 height = 800 if uiscale is bui.UIScale.SMALL else 400.0 39 40 # yoffs = -60 if uiscale is bui.UIScale.SMALL else 0 41 space_height = spacing * 0.3 42 43 buttons_height = 0.0 44 45 # FIXME: should create vis settings under platform or 46 # app-adapter to determine whether to show this stuff; not 47 # hard-code it. 48 49 show_gamepads = False 50 platform = app.classic.platform 51 subplatform = app.classic.subplatform 52 non_vr_windows = platform == 'windows' and ( 53 subplatform != 'oculus' or not app.env.vr 54 ) 55 if platform in ('linux', 'android', 'mac') or non_vr_windows: 56 show_gamepads = True 57 buttons_height += spacing 58 59 show_touch = False 60 if bs.have_touchscreen_input(): 61 show_touch = True 62 buttons_height += spacing 63 64 show_space_1 = False 65 if show_gamepads or show_touch: 66 show_space_1 = True 67 buttons_height += space_height 68 69 show_keyboard = False 70 if bs.getinputdevice('Keyboard', '#1', doraise=False) is not None: 71 show_keyboard = True 72 buttons_height += spacing 73 show_keyboard_p2 = False if app.env.vr else show_keyboard 74 if show_keyboard_p2: 75 buttons_height += spacing 76 77 show_space_2 = False 78 if show_keyboard: 79 show_space_2 = True 80 buttons_height += space_height 81 82 if bool(True): 83 show_remote = True 84 buttons_height += spacing 85 else: 86 show_remote = False 87 88 # On windows (outside of oculus/vr), show an option to disable 89 # xinput. 90 show_xinput_toggle = False 91 if platform == 'windows' and not app.env.vr: 92 show_xinput_toggle = True 93 94 if show_xinput_toggle: 95 buttons_height += spacing 96 97 assert bui.app.classic is not None 98 99 # Do some fancy math to fill all available screen area up to the 100 # size of our backing container. This lets us fit to the exact 101 # screen shape at small ui scale. 102 screensize = bui.get_virtual_screen_size() 103 scale = ( 104 2.0 105 if uiscale is bui.UIScale.SMALL 106 else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0 107 ) 108 # Calc screen size in our local container space and clamp to a 109 # bit smaller than our container size. 110 # target_width = min(width - 60, screensize[0] / scale) 111 target_height = min(height - 70, screensize[1] / scale) 112 113 # To get top/left coords, go to the center of our window and 114 # offset by half the width/height of our target area. 115 yoffs = 0.5 * height + 0.5 * target_height + 30.0 116 117 super().__init__( 118 root_widget=bui.containerwidget( 119 size=(width, height), 120 scale=scale, 121 toolbar_visibility=( 122 'menu_minimal' 123 if uiscale is bui.UIScale.SMALL 124 else 'menu_full' 125 ), 126 ), 127 transition=transition, 128 origin_widget=origin_widget, 129 # We're affected by screen size only at small ui-scale. 130 refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, 131 ) 132 133 self._back_button: bui.Widget | None 134 if uiscale is bui.UIScale.SMALL: 135 bui.containerwidget( 136 edit=self._root_widget, on_cancel_call=self.main_window_back 137 ) 138 self._back_button = None 139 else: 140 self._back_button = btn = bui.buttonwidget( 141 parent=self._root_widget, 142 position=(35, height - 60), 143 size=(60, 60), 144 scale=0.8, 145 text_scale=1.2, 146 autoselect=True, 147 label=bui.charstr(bui.SpecialChar.BACK), 148 button_type='backSmall', 149 on_activate_call=self.main_window_back, 150 ) 151 bui.containerwidget(edit=self._root_widget, cancel_button=btn) 152 153 # We need these vars to exist even if the buttons don't. 154 self._gamepads_button: bui.Widget | None = None 155 self._touch_button: bui.Widget | None = None 156 self._keyboard_button: bui.Widget | None = None 157 self._keyboard_2_button: bui.Widget | None = None 158 self._idevices_button: bui.Widget | None = None 159 160 bui.textwidget( 161 parent=self._root_widget, 162 position=( 163 width * 0.5, 164 yoffs - (52 if uiscale is bui.UIScale.SMALL else 32), 165 ), 166 maxwidth=260, 167 size=(0, 0), 168 text=bui.Lstr(resource=f'{self._r}.titleText'), 169 color=bui.app.ui_v1.title_color, 170 h_align='center', 171 v_align='center', 172 ) 173 174 # Roughly center the rest of our stuff. 175 v = height * 0.5 + buttons_height * 0.5 - 10 176 v -= spacing 177 178 if show_touch: 179 self._touch_button = btn = bui.buttonwidget( 180 parent=self._root_widget, 181 position=((width - button_width) / 2, v), 182 size=(button_width, 43), 183 autoselect=True, 184 label=bui.Lstr(resource=f'{self._r}.configureTouchText'), 185 on_activate_call=self._do_touchscreen, 186 ) 187 bui.widget( 188 edit=btn, 189 right_widget=bui.get_special_widget('squad_button'), 190 ) 191 if not self._have_selected_child: 192 bui.containerwidget( 193 edit=self._root_widget, selected_child=self._touch_button 194 ) 195 if self._back_button is not None: 196 bui.widget( 197 edit=self._back_button, down_widget=self._touch_button 198 ) 199 self._have_selected_child = True 200 v -= spacing 201 202 if show_gamepads: 203 self._gamepads_button = btn = bui.buttonwidget( 204 parent=self._root_widget, 205 position=((width - button_width) / 2 - 7, v), 206 size=(button_width, 43), 207 autoselect=True, 208 label=bui.Lstr(resource=f'{self._r}.configureControllersText'), 209 on_activate_call=self._do_gamepads, 210 ) 211 bui.widget( 212 edit=btn, 213 right_widget=bui.get_special_widget('squad_button'), 214 ) 215 if not self._have_selected_child: 216 bui.containerwidget( 217 edit=self._root_widget, selected_child=self._gamepads_button 218 ) 219 if self._back_button is not None: 220 bui.widget( 221 edit=self._back_button, 222 down_widget=self._gamepads_button, 223 ) 224 self._have_selected_child = True 225 v -= spacing 226 else: 227 self._gamepads_button = None 228 229 if show_space_1: 230 v -= space_height 231 232 if show_keyboard: 233 self._keyboard_button = btn = bui.buttonwidget( 234 parent=self._root_widget, 235 position=((width - button_width) / 2 - 5, v), 236 size=(button_width, 43), 237 autoselect=True, 238 label=bui.Lstr(resource=f'{self._r}.configureKeyboardText'), 239 on_activate_call=self._config_keyboard, 240 ) 241 bui.widget( 242 edit=self._keyboard_button, left_widget=self._keyboard_button 243 ) 244 bui.widget( 245 edit=btn, 246 right_widget=bui.get_special_widget('squad_button'), 247 ) 248 if not self._have_selected_child: 249 bui.containerwidget( 250 edit=self._root_widget, selected_child=self._keyboard_button 251 ) 252 if self._back_button is not None: 253 bui.widget( 254 edit=self._back_button, 255 down_widget=self._keyboard_button, 256 ) 257 self._have_selected_child = True 258 v -= spacing 259 if show_keyboard_p2: 260 self._keyboard_2_button = bui.buttonwidget( 261 parent=self._root_widget, 262 position=((width - button_width) / 2 - 3, v), 263 size=(button_width, 43), 264 autoselect=True, 265 label=bui.Lstr(resource=f'{self._r}.configureKeyboard2Text'), 266 on_activate_call=self._config_keyboard2, 267 ) 268 v -= spacing 269 bui.widget( 270 edit=self._keyboard_2_button, 271 left_widget=self._keyboard_2_button, 272 ) 273 if show_space_2: 274 v -= space_height 275 if show_remote: 276 self._idevices_button = btn = bui.buttonwidget( 277 parent=self._root_widget, 278 position=((width - button_width) / 2 - 5, v), 279 size=(button_width, 43), 280 autoselect=True, 281 label=bui.Lstr(resource=f'{self._r}.configureMobileText'), 282 on_activate_call=self._do_mobile_devices, 283 ) 284 bui.widget( 285 edit=self._idevices_button, left_widget=self._idevices_button 286 ) 287 bui.widget( 288 edit=btn, 289 right_widget=bui.get_special_widget('squad_button'), 290 ) 291 if not self._have_selected_child: 292 bui.containerwidget( 293 edit=self._root_widget, selected_child=self._idevices_button 294 ) 295 if self._back_button is not None: 296 bui.widget( 297 edit=self._back_button, 298 down_widget=self._idevices_button, 299 ) 300 self._have_selected_child = True 301 v -= spacing 302 303 if show_xinput_toggle: 304 305 def do_toggle(value: bool) -> None: 306 bui.screenmessage( 307 bui.Lstr(resource='settingsWindowAdvanced.mustRestartText'), 308 color=(1, 1, 0), 309 ) 310 bui.getsound('gunCocking').play() 311 bui.set_low_level_config_value('enablexinput', not value) 312 313 xinput_checkbox = bui.checkboxwidget( 314 parent=self._root_widget, 315 position=( 316 width * (0.35 if uiscale is bui.UIScale.SMALL else 0.25), 317 v + 3, 318 ), 319 size=(120, 30), 320 value=(not bui.get_low_level_config_value('enablexinput', 1)), 321 maxwidth=200, 322 on_value_change_call=do_toggle, 323 text=bui.Lstr(resource='disableXInputText'), 324 autoselect=True, 325 ) 326 bui.textwidget( 327 parent=self._root_widget, 328 position=(width * 0.5, v - 5), 329 size=(0, 0), 330 text=bui.Lstr(resource='disableXInputDescriptionText'), 331 scale=0.5, 332 h_align='center', 333 v_align='center', 334 color=bui.app.ui_v1.infotextcolor, 335 maxwidth=width * 0.8, 336 ) 337 bui.widget( 338 edit=xinput_checkbox, 339 left_widget=xinput_checkbox, 340 right_widget=xinput_checkbox, 341 ) 342 v -= spacing 343 344 self._restore_state()
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.
346 @override 347 def get_main_window_state(self) -> bui.MainWindowState: 348 # Support recreating our window for back/refresh purposes. 349 cls = type(self) 350 return bui.BasicMainWindowState( 351 create_call=lambda transition, origin_widget: cls( 352 transition=transition, origin_widget=origin_widget 353 ) 354 )
Return a WindowState to recreate this window, if supported.