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 @override 111 def __del__(self) -> None: 112 super().__del__() 113 bs.release_gamepad_input() 114 115 @override 116 def get_main_window_state(self) -> bui.MainWindowState: 117 # Support recreating our window for back/refresh purposes. 118 cls = type(self) 119 return bui.BasicMainWindowState( 120 create_call=lambda transition, origin_widget: cls( 121 transition=transition, origin_widget=origin_widget 122 ) 123 ) 124 125 def gamepad_configure_callback(self, event: dict[str, Any]) -> None: 126 """Respond to a gamepad button press during config selection.""" 127 from bauiv1lib.settings.gamepad import GamepadSettingsWindow 128 129 if not self.main_window_has_control(): 130 return 131 132 # Ignore all but button-presses. 133 if event['type'] not in ['BUTTONDOWN', 'HATMOTION']: 134 return 135 bs.release_gamepad_input() 136 137 assert bui.app.classic is not None 138 139 bui.getsound('activateBeep').play() 140 bui.getsound('swish').play() 141 device = event['input_device'] 142 assert isinstance(device, bs.InputDevice) 143 144 # No matter where we redirect to, we want their back 145 # functionality to skip over us and go to our parent. 146 assert self.main_window_back_state is not None 147 back_state = self.main_window_back_state 148 149 if device.allows_configuring: 150 self.main_window_replace( 151 GamepadSettingsWindow(device), back_state=back_state 152 ) 153 else: 154 self.main_window_replace( 155 _NotConfigurableWindow(device), back_state=back_state 156 ) 157 158 159class _NotConfigurableWindow(bui.MainWindow): 160 161 def __init__( 162 self, 163 device: bs.InputDevice, 164 transition: str | None = 'in_right', 165 origin_widget: bui.Widget | None = None, 166 ) -> None: 167 width = 700 168 height = 200 169 button_width = 80 170 uiscale = bui.app.ui_v1.uiscale 171 super().__init__( 172 root_widget=bui.containerwidget( 173 scale=( 174 1.7 175 if uiscale is bui.UIScale.SMALL 176 else (1.4 if uiscale is bui.UIScale.MEDIUM else 1.0) 177 ), 178 size=(width, height), 179 ), 180 transition=transition, 181 origin_widget=origin_widget, 182 ) 183 self.device = device 184 185 if device.allows_configuring_in_system_settings: 186 msg = bui.Lstr( 187 resource='configureDeviceInSystemSettingsText', 188 subs=[('${DEVICE}', device.name)], 189 ) 190 elif device.is_controller_app: 191 msg = bui.Lstr( 192 resource='bsRemoteConfigureInAppText', 193 subs=[ 194 ( 195 '${REMOTE_APP_NAME}', 196 bui.get_remote_app_name(), 197 ) 198 ], 199 ) 200 else: 201 msg = bui.Lstr( 202 resource='cantConfigureDeviceText', 203 subs=[('${DEVICE}', device.name)], 204 ) 205 bui.textwidget( 206 parent=self._root_widget, 207 position=(0, height - 80), 208 size=(width, 25), 209 text=msg, 210 scale=0.8, 211 h_align='center', 212 v_align='top', 213 ) 214 215 btn = bui.buttonwidget( 216 parent=self._root_widget, 217 position=((width - button_width) / 2, 20), 218 size=(button_width, 60), 219 label=bui.Lstr(resource='okText'), 220 on_activate_call=self.main_window_back, 221 ) 222 bui.containerwidget(edit=self._root_widget, cancel_button=btn) 223 224 @override 225 def get_main_window_state(self) -> bui.MainWindowState: 226 # Support recreating our window for back/refresh purposes. 227 cls = type(self) 228 229 # Pull stuff out of self here; if we do it in the lambda we'll 230 # keep self alive which we don't want. 231 device = self.device 232 233 return bui.BasicMainWindowState( 234 create_call=lambda transition, origin_widget: cls( 235 device=device, 236 transition=transition, 237 origin_widget=origin_widget, 238 ) 239 )
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 @override 112 def __del__(self) -> None: 113 super().__del__() 114 bs.release_gamepad_input() 115 116 @override 117 def get_main_window_state(self) -> bui.MainWindowState: 118 # Support recreating our window for back/refresh purposes. 119 cls = type(self) 120 return bui.BasicMainWindowState( 121 create_call=lambda transition, origin_widget: cls( 122 transition=transition, origin_widget=origin_widget 123 ) 124 ) 125 126 def gamepad_configure_callback(self, event: dict[str, Any]) -> None: 127 """Respond to a gamepad button press during config selection.""" 128 from bauiv1lib.settings.gamepad import GamepadSettingsWindow 129 130 if not self.main_window_has_control(): 131 return 132 133 # Ignore all but button-presses. 134 if event['type'] not in ['BUTTONDOWN', 'HATMOTION']: 135 return 136 bs.release_gamepad_input() 137 138 assert bui.app.classic is not None 139 140 bui.getsound('activateBeep').play() 141 bui.getsound('swish').play() 142 device = event['input_device'] 143 assert isinstance(device, bs.InputDevice) 144 145 # No matter where we redirect to, we want their back 146 # functionality to skip over us and go to our parent. 147 assert self.main_window_back_state is not None 148 back_state = self.main_window_back_state 149 150 if device.allows_configuring: 151 self.main_window_replace( 152 GamepadSettingsWindow(device), back_state=back_state 153 ) 154 else: 155 self.main_window_replace( 156 _NotConfigurableWindow(device), back_state=back_state 157 )
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.
116 @override 117 def get_main_window_state(self) -> bui.MainWindowState: 118 # Support recreating our window for back/refresh purposes. 119 cls = type(self) 120 return bui.BasicMainWindowState( 121 create_call=lambda transition, origin_widget: cls( 122 transition=transition, origin_widget=origin_widget 123 ) 124 )
Return a WindowState to recreate this window, if supported.
def
gamepad_configure_callback(self, event: dict[str, typing.Any]) -> None:
126 def gamepad_configure_callback(self, event: dict[str, Any]) -> None: 127 """Respond to a gamepad button press during config selection.""" 128 from bauiv1lib.settings.gamepad import GamepadSettingsWindow 129 130 if not self.main_window_has_control(): 131 return 132 133 # Ignore all but button-presses. 134 if event['type'] not in ['BUTTONDOWN', 'HATMOTION']: 135 return 136 bs.release_gamepad_input() 137 138 assert bui.app.classic is not None 139 140 bui.getsound('activateBeep').play() 141 bui.getsound('swish').play() 142 device = event['input_device'] 143 assert isinstance(device, bs.InputDevice) 144 145 # No matter where we redirect to, we want their back 146 # functionality to skip over us and go to our parent. 147 assert self.main_window_back_state is not None 148 back_state = self.main_window_back_state 149 150 if device.allows_configuring: 151 self.main_window_replace( 152 GamepadSettingsWindow(device), back_state=back_state 153 ) 154 else: 155 self.main_window_replace( 156 _NotConfigurableWindow(device), back_state=back_state 157 )
Respond to a gamepad button press during config selection.