bauiv1lib.settings.gamepadselect
Settings UI related to gamepad functionality.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Settings UI related to gamepad functionality.""" 4 5from __future__ import annotations 6 7from typing import TYPE_CHECKING, override 8 9import bascenev1 as bs 10import bauiv1 as bui 11 12if TYPE_CHECKING: 13 from typing import Any 14 15 16class GamepadSelectWindow(bui.MainWindow): 17 """Window for selecting a gamepad to configure.""" 18 19 def __init__( 20 self, 21 transition: str | None = 'in_right', 22 origin_widget: bui.Widget | None = None, 23 ) -> None: 24 from typing import cast 25 26 width = 480 27 height = 170 28 spacing = 40 29 self._r = 'configGamepadSelectWindow' 30 31 assert bui.app.classic is not None 32 uiscale = bui.app.ui_v1.uiscale 33 super().__init__( 34 root_widget=bui.containerwidget( 35 scale=( 36 2.3 37 if uiscale is bui.UIScale.SMALL 38 else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 39 ), 40 size=(width, height), 41 ), 42 transition=transition, 43 origin_widget=origin_widget, 44 ) 45 46 btn = bui.buttonwidget( 47 parent=self._root_widget, 48 position=(20, height - 60), 49 size=(130, 60), 50 label=bui.Lstr(resource='backText'), 51 button_type='back', 52 scale=0.8, 53 on_activate_call=self.main_window_back, 54 ) 55 56 # Let's not have anything selected by default; its misleading 57 # looking for the controller getting configured. 58 bui.containerwidget( 59 edit=self._root_widget, 60 cancel_button=btn, 61 selected_child=cast(bui.Widget, 0), 62 ) 63 bui.textwidget( 64 parent=self._root_widget, 65 position=(20, height - 50), 66 size=(width, 25), 67 text=bui.Lstr(resource=f'{self._r}.titleText'), 68 maxwidth=250, 69 color=bui.app.ui_v1.title_color, 70 h_align='center', 71 v_align='center', 72 ) 73 74 bui.buttonwidget( 75 edit=btn, 76 button_type='backSmall', 77 size=(60, 60), 78 label=bui.charstr(bui.SpecialChar.BACK), 79 ) 80 81 v: float = height - 60 82 v -= spacing 83 bui.textwidget( 84 parent=self._root_widget, 85 position=(15, v), 86 size=(width - 30, 30), 87 scale=0.8, 88 text=bui.Lstr(resource=f'{self._r}.pressAnyButtonText'), 89 maxwidth=width * 0.95, 90 color=bui.app.ui_v1.infotextcolor, 91 h_align='center', 92 v_align='top', 93 ) 94 v -= spacing * 1.24 95 if bui.app.classic.platform == 'android': 96 bui.textwidget( 97 parent=self._root_widget, 98 position=(15, v), 99 size=(width - 30, 30), 100 scale=0.46, 101 text=bui.Lstr(resource=f'{self._r}.androidNoteText'), 102 maxwidth=width * 0.95, 103 color=(0.7, 0.9, 0.7, 0.5), 104 h_align='center', 105 v_align='top', 106 ) 107 108 bs.capture_gamepad_input(bui.WeakCall(self.gamepad_configure_callback)) 109 110 def __del__(self) -> None: 111 bs.release_gamepad_input() 112 113 @override 114 def get_main_window_state(self) -> bui.MainWindowState: 115 # Support recreating our window for back/refresh purposes. 116 cls = type(self) 117 return bui.BasicMainWindowState( 118 create_call=lambda transition, origin_widget: cls( 119 transition=transition, origin_widget=origin_widget 120 ) 121 ) 122 123 def gamepad_configure_callback(self, event: dict[str, Any]) -> None: 124 """Respond to a gamepad button press during config selection.""" 125 from bauiv1lib.settings.gamepad import GamepadSettingsWindow 126 127 if not self.main_window_has_control(): 128 return 129 130 # Ignore all but button-presses. 131 if event['type'] not in ['BUTTONDOWN', 'HATMOTION']: 132 return 133 bs.release_gamepad_input() 134 135 assert bui.app.classic is not None 136 137 bui.getsound('activateBeep').play() 138 bui.getsound('swish').play() 139 device = event['input_device'] 140 assert isinstance(device, bs.InputDevice) 141 142 # No matter where we redirect to, we want their back 143 # functionality to skip over us and go to our parent. 144 assert self.main_window_back_state is not None 145 back_state = self.main_window_back_state 146 147 if device.allows_configuring: 148 self.main_window_replace( 149 GamepadSettingsWindow(device), back_state=back_state 150 ) 151 else: 152 self.main_window_replace( 153 _NotConfigurableWindow(device), back_state=back_state 154 ) 155 156 157class _NotConfigurableWindow(bui.MainWindow): 158 159 def __init__( 160 self, 161 device: bs.InputDevice, 162 transition: str | None = 'in_right', 163 origin_widget: bui.Widget | None = None, 164 ) -> None: 165 width = 700 166 height = 200 167 button_width = 80 168 uiscale = bui.app.ui_v1.uiscale 169 super().__init__( 170 root_widget=bui.containerwidget( 171 scale=( 172 1.7 173 if uiscale is bui.UIScale.SMALL 174 else (1.4 if uiscale is bui.UIScale.MEDIUM else 1.0) 175 ), 176 size=(width, height), 177 ), 178 transition=transition, 179 origin_widget=origin_widget, 180 ) 181 self.device = device 182 183 if device.allows_configuring_in_system_settings: 184 msg = bui.Lstr( 185 resource='configureDeviceInSystemSettingsText', 186 subs=[('${DEVICE}', device.name)], 187 ) 188 elif device.is_controller_app: 189 msg = bui.Lstr( 190 resource='bsRemoteConfigureInAppText', 191 subs=[ 192 ( 193 '${REMOTE_APP_NAME}', 194 bui.get_remote_app_name(), 195 ) 196 ], 197 ) 198 else: 199 msg = bui.Lstr( 200 resource='cantConfigureDeviceText', 201 subs=[('${DEVICE}', device.name)], 202 ) 203 bui.textwidget( 204 parent=self._root_widget, 205 position=(0, height - 80), 206 size=(width, 25), 207 text=msg, 208 scale=0.8, 209 h_align='center', 210 v_align='top', 211 ) 212 213 btn = bui.buttonwidget( 214 parent=self._root_widget, 215 position=((width - button_width) / 2, 20), 216 size=(button_width, 60), 217 label=bui.Lstr(resource='okText'), 218 on_activate_call=self.main_window_back, 219 ) 220 bui.containerwidget(edit=self._root_widget, cancel_button=btn) 221 222 @override 223 def get_main_window_state(self) -> bui.MainWindowState: 224 # Support recreating our window for back/refresh purposes. 225 cls = type(self) 226 227 # Pull stuff out of self here; if we do it in the lambda we'll 228 # keep self alive which we don't want. 229 device = self.device 230 231 return bui.BasicMainWindowState( 232 create_call=lambda transition, origin_widget: cls( 233 device=device, 234 transition=transition, 235 origin_widget=origin_widget, 236 ) 237 )
class
GamepadSelectWindow(bauiv1._uitypes.MainWindow):
17class GamepadSelectWindow(bui.MainWindow): 18 """Window for selecting a gamepad to configure.""" 19 20 def __init__( 21 self, 22 transition: str | None = 'in_right', 23 origin_widget: bui.Widget | None = None, 24 ) -> None: 25 from typing import cast 26 27 width = 480 28 height = 170 29 spacing = 40 30 self._r = 'configGamepadSelectWindow' 31 32 assert bui.app.classic is not None 33 uiscale = bui.app.ui_v1.uiscale 34 super().__init__( 35 root_widget=bui.containerwidget( 36 scale=( 37 2.3 38 if uiscale is bui.UIScale.SMALL 39 else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 40 ), 41 size=(width, height), 42 ), 43 transition=transition, 44 origin_widget=origin_widget, 45 ) 46 47 btn = bui.buttonwidget( 48 parent=self._root_widget, 49 position=(20, height - 60), 50 size=(130, 60), 51 label=bui.Lstr(resource='backText'), 52 button_type='back', 53 scale=0.8, 54 on_activate_call=self.main_window_back, 55 ) 56 57 # Let's not have anything selected by default; its misleading 58 # looking for the controller getting configured. 59 bui.containerwidget( 60 edit=self._root_widget, 61 cancel_button=btn, 62 selected_child=cast(bui.Widget, 0), 63 ) 64 bui.textwidget( 65 parent=self._root_widget, 66 position=(20, height - 50), 67 size=(width, 25), 68 text=bui.Lstr(resource=f'{self._r}.titleText'), 69 maxwidth=250, 70 color=bui.app.ui_v1.title_color, 71 h_align='center', 72 v_align='center', 73 ) 74 75 bui.buttonwidget( 76 edit=btn, 77 button_type='backSmall', 78 size=(60, 60), 79 label=bui.charstr(bui.SpecialChar.BACK), 80 ) 81 82 v: float = height - 60 83 v -= spacing 84 bui.textwidget( 85 parent=self._root_widget, 86 position=(15, v), 87 size=(width - 30, 30), 88 scale=0.8, 89 text=bui.Lstr(resource=f'{self._r}.pressAnyButtonText'), 90 maxwidth=width * 0.95, 91 color=bui.app.ui_v1.infotextcolor, 92 h_align='center', 93 v_align='top', 94 ) 95 v -= spacing * 1.24 96 if bui.app.classic.platform == 'android': 97 bui.textwidget( 98 parent=self._root_widget, 99 position=(15, v), 100 size=(width - 30, 30), 101 scale=0.46, 102 text=bui.Lstr(resource=f'{self._r}.androidNoteText'), 103 maxwidth=width * 0.95, 104 color=(0.7, 0.9, 0.7, 0.5), 105 h_align='center', 106 v_align='top', 107 ) 108 109 bs.capture_gamepad_input(bui.WeakCall(self.gamepad_configure_callback)) 110 111 def __del__(self) -> None: 112 bs.release_gamepad_input() 113 114 @override 115 def get_main_window_state(self) -> bui.MainWindowState: 116 # Support recreating our window for back/refresh purposes. 117 cls = type(self) 118 return bui.BasicMainWindowState( 119 create_call=lambda transition, origin_widget: cls( 120 transition=transition, origin_widget=origin_widget 121 ) 122 ) 123 124 def gamepad_configure_callback(self, event: dict[str, Any]) -> None: 125 """Respond to a gamepad button press during config selection.""" 126 from bauiv1lib.settings.gamepad import GamepadSettingsWindow 127 128 if not self.main_window_has_control(): 129 return 130 131 # Ignore all but button-presses. 132 if event['type'] not in ['BUTTONDOWN', 'HATMOTION']: 133 return 134 bs.release_gamepad_input() 135 136 assert bui.app.classic is not None 137 138 bui.getsound('activateBeep').play() 139 bui.getsound('swish').play() 140 device = event['input_device'] 141 assert isinstance(device, bs.InputDevice) 142 143 # No matter where we redirect to, we want their back 144 # functionality to skip over us and go to our parent. 145 assert self.main_window_back_state is not None 146 back_state = self.main_window_back_state 147 148 if device.allows_configuring: 149 self.main_window_replace( 150 GamepadSettingsWindow(device), back_state=back_state 151 ) 152 else: 153 self.main_window_replace( 154 _NotConfigurableWindow(device), back_state=back_state 155 )
Window for selecting a gamepad to configure.
GamepadSelectWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
20 def __init__( 21 self, 22 transition: str | None = 'in_right', 23 origin_widget: bui.Widget | None = None, 24 ) -> None: 25 from typing import cast 26 27 width = 480 28 height = 170 29 spacing = 40 30 self._r = 'configGamepadSelectWindow' 31 32 assert bui.app.classic is not None 33 uiscale = bui.app.ui_v1.uiscale 34 super().__init__( 35 root_widget=bui.containerwidget( 36 scale=( 37 2.3 38 if uiscale is bui.UIScale.SMALL 39 else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0 40 ), 41 size=(width, height), 42 ), 43 transition=transition, 44 origin_widget=origin_widget, 45 ) 46 47 btn = bui.buttonwidget( 48 parent=self._root_widget, 49 position=(20, height - 60), 50 size=(130, 60), 51 label=bui.Lstr(resource='backText'), 52 button_type='back', 53 scale=0.8, 54 on_activate_call=self.main_window_back, 55 ) 56 57 # Let's not have anything selected by default; its misleading 58 # looking for the controller getting configured. 59 bui.containerwidget( 60 edit=self._root_widget, 61 cancel_button=btn, 62 selected_child=cast(bui.Widget, 0), 63 ) 64 bui.textwidget( 65 parent=self._root_widget, 66 position=(20, height - 50), 67 size=(width, 25), 68 text=bui.Lstr(resource=f'{self._r}.titleText'), 69 maxwidth=250, 70 color=bui.app.ui_v1.title_color, 71 h_align='center', 72 v_align='center', 73 ) 74 75 bui.buttonwidget( 76 edit=btn, 77 button_type='backSmall', 78 size=(60, 60), 79 label=bui.charstr(bui.SpecialChar.BACK), 80 ) 81 82 v: float = height - 60 83 v -= spacing 84 bui.textwidget( 85 parent=self._root_widget, 86 position=(15, v), 87 size=(width - 30, 30), 88 scale=0.8, 89 text=bui.Lstr(resource=f'{self._r}.pressAnyButtonText'), 90 maxwidth=width * 0.95, 91 color=bui.app.ui_v1.infotextcolor, 92 h_align='center', 93 v_align='top', 94 ) 95 v -= spacing * 1.24 96 if bui.app.classic.platform == 'android': 97 bui.textwidget( 98 parent=self._root_widget, 99 position=(15, v), 100 size=(width - 30, 30), 101 scale=0.46, 102 text=bui.Lstr(resource=f'{self._r}.androidNoteText'), 103 maxwidth=width * 0.95, 104 color=(0.7, 0.9, 0.7, 0.5), 105 h_align='center', 106 v_align='top', 107 ) 108 109 bs.capture_gamepad_input(bui.WeakCall(self.gamepad_configure_callback))
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.
114 @override 115 def get_main_window_state(self) -> bui.MainWindowState: 116 # Support recreating our window for back/refresh purposes. 117 cls = type(self) 118 return bui.BasicMainWindowState( 119 create_call=lambda transition, origin_widget: cls( 120 transition=transition, origin_widget=origin_widget 121 ) 122 )
Return a WindowState to recreate this window, if supported.
def
gamepad_configure_callback(self, event: dict[str, typing.Any]) -> None:
124 def gamepad_configure_callback(self, event: dict[str, Any]) -> None: 125 """Respond to a gamepad button press during config selection.""" 126 from bauiv1lib.settings.gamepad import GamepadSettingsWindow 127 128 if not self.main_window_has_control(): 129 return 130 131 # Ignore all but button-presses. 132 if event['type'] not in ['BUTTONDOWN', 'HATMOTION']: 133 return 134 bs.release_gamepad_input() 135 136 assert bui.app.classic is not None 137 138 bui.getsound('activateBeep').play() 139 bui.getsound('swish').play() 140 device = event['input_device'] 141 assert isinstance(device, bs.InputDevice) 142 143 # No matter where we redirect to, we want their back 144 # functionality to skip over us and go to our parent. 145 assert self.main_window_back_state is not None 146 back_state = self.main_window_back_state 147 148 if device.allows_configuring: 149 self.main_window_replace( 150 GamepadSettingsWindow(device), back_state=back_state 151 ) 152 else: 153 self.main_window_replace( 154 _NotConfigurableWindow(device), back_state=back_state 155 )
Respond to a gamepad button press during config selection.