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__(self, device: bs.InputDevice) -> None:
160        width = 700
161        height = 200
162        button_width = 80
163        uiscale = bui.app.ui_v1.uiscale
164        super().__init__(
165            root_widget=bui.containerwidget(
166                scale=(
167                    1.7
168                    if uiscale is bui.UIScale.SMALL
169                    else (1.4 if uiscale is bui.UIScale.MEDIUM else 1.0)
170                ),
171                size=(width, height),
172            ),
173            transition='in_right',
174            origin_widget=None,
175        )
176        if device.allows_configuring_in_system_settings:
177            msg = bui.Lstr(
178                resource='configureDeviceInSystemSettingsText',
179                subs=[('${DEVICE}', device.name)],
180            )
181        elif device.is_controller_app:
182            msg = bui.Lstr(
183                resource='bsRemoteConfigureInAppText',
184                subs=[
185                    (
186                        '${REMOTE_APP_NAME}',
187                        bui.get_remote_app_name(),
188                    )
189                ],
190            )
191        else:
192            msg = bui.Lstr(
193                resource='cantConfigureDeviceText',
194                subs=[('${DEVICE}', device.name)],
195            )
196        bui.textwidget(
197            parent=self._root_widget,
198            position=(0, height - 80),
199            size=(width, 25),
200            text=msg,
201            scale=0.8,
202            h_align='center',
203            v_align='top',
204        )
205
206        btn = bui.buttonwidget(
207            parent=self._root_widget,
208            position=((width - button_width) / 2, 20),
209            size=(button_width, 60),
210            label=bui.Lstr(resource='okText'),
211            on_activate_call=self.main_window_back,
212        )
213        bui.containerwidget(edit=self._root_widget, cancel_button=btn)
214
215    # def _ok(self) -> None:
216
217    #     # Back would take us to the gamepad-select window. We want to go
218    #     # past that.
219    #     assert self.main_window_back_state is not None
220    #     self.main_window_back_state = self.main_window_back_state.parent
221    #     self.main_window_back()
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.

@override
def get_main_window_state(self) -> bauiv1.MainWindowState:
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.

Inherited Members
bauiv1._uitypes.MainWindow
main_window_back_state
main_window_is_top_level
main_window_close
main_window_has_control
main_window_back
main_window_replace
on_main_window_close
bauiv1._uitypes.Window
get_root_widget