bastd.ui.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 TYPE_CHECKING
  8
  9import ba
 10import ba.internal
 11
 12if TYPE_CHECKING:
 13    pass
 14
 15
 16class ControlsSettingsWindow(ba.Window):
 17    """Top level control settings window."""
 18
 19    def __init__(
 20        self,
 21        transition: str = 'in_right',
 22        origin_widget: ba.Widget | None = None,
 23    ):
 24        # FIXME: should tidy up here.
 25        # pylint: disable=too-many-statements
 26        # pylint: disable=too-many-branches
 27        # pylint: disable=too-many-locals
 28        # pylint: disable=cyclic-import
 29        from bastd.ui import popup as popup_ui
 30
 31        self._have_selected_child = False
 32
 33        scale_origin: tuple[float, float] | None
 34
 35        # If they provided an origin-widget, scale up from that.
 36        if origin_widget is not None:
 37            self._transition_out = 'out_scale'
 38            scale_origin = origin_widget.get_screen_space_center()
 39            transition = 'in_scale'
 40        else:
 41            self._transition_out = 'out_right'
 42            scale_origin = None
 43
 44        self._r = 'configControllersWindow'
 45        app = ba.app
 46
 47        # is_fire_tv = ba.internal.is_running_on_fire_tv()
 48
 49        spacing = 50.0
 50        button_width = 350.0
 51        width = 460.0
 52        height = 130.0
 53
 54        space_height = spacing * 0.3
 55
 56        # FIXME: should create vis settings in platform for these,
 57        #  not hard code them here.
 58
 59        show_gamepads = False
 60        platform = app.platform
 61        subplatform = app.subplatform
 62        non_vr_windows = platform == 'windows' and (
 63            subplatform != 'oculus' or not app.vr_mode
 64        )
 65        if platform in ('linux', 'android', 'mac') or non_vr_windows:
 66            show_gamepads = True
 67            height += spacing
 68
 69        show_touch = False
 70        if ba.internal.have_touchscreen_input():
 71            show_touch = True
 72            height += spacing
 73
 74        show_space_1 = False
 75        if show_gamepads or show_touch:
 76            show_space_1 = True
 77            height += space_height
 78
 79        show_keyboard = False
 80        if (
 81            ba.internal.getinputdevice('Keyboard', '#1', doraise=False)
 82            is not None
 83        ):
 84            show_keyboard = True
 85            height += spacing
 86        show_keyboard_p2 = False if app.vr_mode else show_keyboard
 87        if show_keyboard_p2:
 88            height += spacing
 89
 90        show_space_2 = False
 91        if show_keyboard:
 92            show_space_2 = True
 93            height += space_height
 94
 95        if bool(True):
 96            show_remote = True
 97            height += spacing
 98        else:
 99            show_remote = False
100
101        # On windows (outside of oculus/vr), show an option to disable xinput.
102        show_xinput_toggle = False
103        if platform == 'windows' and not app.vr_mode:
104            show_xinput_toggle = True
105
106        # On mac builds, show an option to switch between generic and
107        # made-for-iOS/Mac systems
108        # (we can run into problems where devices register as one of each
109        # type otherwise)..
110        show_mac_controller_subsystem = False
111        if platform == 'mac' and ba.internal.is_xcode_build():
112            show_mac_controller_subsystem = True
113
114        if show_mac_controller_subsystem:
115            height += spacing * 1.5
116
117        if show_xinput_toggle:
118            height += spacing
119
120        uiscale = ba.app.ui.uiscale
121        smallscale = 1.7 if show_keyboard else 2.2
122        super().__init__(
123            root_widget=ba.containerwidget(
124                size=(width, height),
125                transition=transition,
126                scale_origin_stack_offset=scale_origin,
127                stack_offset=(
128                    (0, -10) if uiscale is ba.UIScale.SMALL else (0, 0)
129                ),
130                scale=(
131                    smallscale
132                    if uiscale is ba.UIScale.SMALL
133                    else 1.5
134                    if uiscale is ba.UIScale.MEDIUM
135                    else 1.0
136                ),
137            )
138        )
139        self._back_button = btn = ba.buttonwidget(
140            parent=self._root_widget,
141            position=(35, height - 60),
142            size=(140, 65),
143            scale=0.8,
144            text_scale=1.2,
145            autoselect=True,
146            label=ba.Lstr(resource='backText'),
147            button_type='back',
148            on_activate_call=self._back,
149        )
150        ba.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: ba.Widget | None = None
154        self._touch_button: ba.Widget | None = None
155        self._keyboard_button: ba.Widget | None = None
156        self._keyboard_2_button: ba.Widget | None = None
157        self._idevices_button: ba.Widget | None = None
158
159        ba.textwidget(
160            parent=self._root_widget,
161            position=(0, height - 49),
162            size=(width, 25),
163            text=ba.Lstr(resource=self._r + '.titleText'),
164            color=ba.app.ui.title_color,
165            h_align='center',
166            v_align='top',
167        )
168        ba.buttonwidget(
169            edit=btn,
170            button_type='backSmall',
171            size=(60, 60),
172            label=ba.charstr(ba.SpecialChar.BACK),
173        )
174
175        v = height - 75
176        v -= spacing
177
178        if show_touch:
179            self._touch_button = btn = ba.buttonwidget(
180                parent=self._root_widget,
181                position=((width - button_width) / 2, v),
182                size=(button_width, 43),
183                autoselect=True,
184                label=ba.Lstr(resource=self._r + '.configureTouchText'),
185                on_activate_call=self._do_touchscreen,
186            )
187            if ba.app.ui.use_toolbars:
188                ba.widget(
189                    edit=btn,
190                    right_widget=ba.internal.get_special_widget('party_button'),
191                )
192            if not self._have_selected_child:
193                ba.containerwidget(
194                    edit=self._root_widget, selected_child=self._touch_button
195                )
196                ba.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 = ba.buttonwidget(
204                parent=self._root_widget,
205                position=((width - button_width) / 2 - 7, v),
206                size=(button_width, 43),
207                autoselect=True,
208                label=ba.Lstr(resource=self._r + '.configureControllersText'),
209                on_activate_call=self._do_gamepads,
210            )
211            if ba.app.ui.use_toolbars:
212                ba.widget(
213                    edit=btn,
214                    right_widget=ba.internal.get_special_widget('party_button'),
215                )
216            if not self._have_selected_child:
217                ba.containerwidget(
218                    edit=self._root_widget, selected_child=self._gamepads_button
219                )
220                ba.widget(
221                    edit=self._back_button, 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 = ba.buttonwidget(
233                parent=self._root_widget,
234                position=((width - button_width) / 2 + 5, v),
235                size=(button_width, 43),
236                autoselect=True,
237                label=ba.Lstr(resource=self._r + '.configureKeyboardText'),
238                on_activate_call=self._config_keyboard,
239            )
240            if ba.app.ui.use_toolbars:
241                ba.widget(
242                    edit=btn,
243                    right_widget=ba.internal.get_special_widget('party_button'),
244                )
245            if not self._have_selected_child:
246                ba.containerwidget(
247                    edit=self._root_widget, selected_child=self._keyboard_button
248                )
249                ba.widget(
250                    edit=self._back_button, down_widget=self._keyboard_button
251                )
252                self._have_selected_child = True
253            v -= spacing
254        if show_keyboard_p2:
255            self._keyboard_2_button = ba.buttonwidget(
256                parent=self._root_widget,
257                position=((width - button_width) / 2 - 3, v),
258                size=(button_width, 43),
259                autoselect=True,
260                label=ba.Lstr(resource=self._r + '.configureKeyboard2Text'),
261                on_activate_call=self._config_keyboard2,
262            )
263            v -= spacing
264        if show_space_2:
265            v -= space_height
266        if show_remote:
267            self._idevices_button = btn = ba.buttonwidget(
268                parent=self._root_widget,
269                position=((width - button_width) / 2 - 5, v),
270                size=(button_width, 43),
271                autoselect=True,
272                label=ba.Lstr(resource=self._r + '.configureMobileText'),
273                on_activate_call=self._do_mobile_devices,
274            )
275            if ba.app.ui.use_toolbars:
276                ba.widget(
277                    edit=btn,
278                    right_widget=ba.internal.get_special_widget('party_button'),
279                )
280            if not self._have_selected_child:
281                ba.containerwidget(
282                    edit=self._root_widget, selected_child=self._idevices_button
283                )
284                ba.widget(
285                    edit=self._back_button, down_widget=self._idevices_button
286                )
287                self._have_selected_child = True
288            v -= spacing
289
290        if show_xinput_toggle:
291
292            def do_toggle(value: bool) -> None:
293                ba.screenmessage(
294                    ba.Lstr(resource='settingsWindowAdvanced.mustRestartText'),
295                    color=(1, 1, 0),
296                )
297                ba.playsound(ba.getsound('gunCocking'))
298                ba.internal.set_low_level_config_value(
299                    'enablexinput', not value
300                )
301
302            ba.checkboxwidget(
303                parent=self._root_widget,
304                position=(100, v + 3),
305                size=(120, 30),
306                value=(
307                    not ba.internal.get_low_level_config_value(
308                        'enablexinput', 1
309                    )
310                ),
311                maxwidth=200,
312                on_value_change_call=do_toggle,
313                text=ba.Lstr(resource='disableXInputText'),
314                autoselect=True,
315            )
316            ba.textwidget(
317                parent=self._root_widget,
318                position=(width * 0.5, v - 5),
319                size=(0, 0),
320                text=ba.Lstr(resource='disableXInputDescriptionText'),
321                scale=0.5,
322                h_align='center',
323                v_align='center',
324                color=ba.app.ui.infotextcolor,
325                maxwidth=width * 0.8,
326            )
327            v -= spacing
328        if show_mac_controller_subsystem:
329            popup_ui.PopupMenu(
330                parent=self._root_widget,
331                position=(260, v - 10),
332                width=160,
333                button_size=(150, 50),
334                scale=1.5,
335                choices=['Classic', 'MFi', 'Both'],
336                choices_display=[
337                    ba.Lstr(resource='macControllerSubsystemClassicText'),
338                    ba.Lstr(resource='macControllerSubsystemMFiText'),
339                    ba.Lstr(resource='macControllerSubsystemBothText'),
340                ],
341                current_choice=ba.app.config.resolve(
342                    'Mac Controller Subsystem'
343                ),
344                on_value_change_call=self._set_mac_controller_subsystem,
345            )
346            ba.textwidget(
347                parent=self._root_widget,
348                position=(245, v + 13),
349                size=(0, 0),
350                text=ba.Lstr(resource='macControllerSubsystemTitleText'),
351                scale=1.0,
352                h_align='right',
353                v_align='center',
354                color=ba.app.ui.infotextcolor,
355                maxwidth=180,
356            )
357            ba.textwidget(
358                parent=self._root_widget,
359                position=(width * 0.5, v - 20),
360                size=(0, 0),
361                text=ba.Lstr(resource='macControllerSubsystemDescriptionText'),
362                scale=0.5,
363                h_align='center',
364                v_align='center',
365                color=ba.app.ui.infotextcolor,
366                maxwidth=width * 0.8,
367            )
368            v -= spacing * 1.5
369        self._restore_state()
370
371    def _set_mac_controller_subsystem(self, val: str) -> None:
372        cfg = ba.app.config
373        cfg['Mac Controller Subsystem'] = val
374        cfg.apply_and_commit()
375
376    def _config_keyboard(self) -> None:
377        # pylint: disable=cyclic-import
378        from bastd.ui.settings.keyboard import ConfigKeyboardWindow
379
380        self._save_state()
381        ba.containerwidget(edit=self._root_widget, transition='out_left')
382        ba.app.ui.set_main_menu_window(
383            ConfigKeyboardWindow(
384                ba.internal.getinputdevice('Keyboard', '#1')
385            ).get_root_widget()
386        )
387
388    def _config_keyboard2(self) -> None:
389        # pylint: disable=cyclic-import
390        from bastd.ui.settings.keyboard import ConfigKeyboardWindow
391
392        self._save_state()
393        ba.containerwidget(edit=self._root_widget, transition='out_left')
394        ba.app.ui.set_main_menu_window(
395            ConfigKeyboardWindow(
396                ba.internal.getinputdevice('Keyboard', '#2')
397            ).get_root_widget()
398        )
399
400    def _do_mobile_devices(self) -> None:
401        # pylint: disable=cyclic-import
402        from bastd.ui.settings.remoteapp import RemoteAppSettingsWindow
403
404        self._save_state()
405        ba.containerwidget(edit=self._root_widget, transition='out_left')
406        ba.app.ui.set_main_menu_window(
407            RemoteAppSettingsWindow().get_root_widget()
408        )
409
410    def _do_gamepads(self) -> None:
411        # pylint: disable=cyclic-import
412        from bastd.ui.settings.gamepadselect import GamepadSelectWindow
413
414        self._save_state()
415        ba.containerwidget(edit=self._root_widget, transition='out_left')
416        ba.app.ui.set_main_menu_window(GamepadSelectWindow().get_root_widget())
417
418    def _do_touchscreen(self) -> None:
419        # pylint: disable=cyclic-import
420        from bastd.ui.settings.touchscreen import TouchscreenSettingsWindow
421
422        self._save_state()
423        ba.containerwidget(edit=self._root_widget, transition='out_left')
424        ba.app.ui.set_main_menu_window(
425            TouchscreenSettingsWindow().get_root_widget()
426        )
427
428    def _save_state(self) -> None:
429        sel = self._root_widget.get_selected_child()
430        if sel == self._gamepads_button:
431            sel_name = 'GamePads'
432        elif sel == self._touch_button:
433            sel_name = 'Touch'
434        elif sel == self._keyboard_button:
435            sel_name = 'Keyboard'
436        elif sel == self._keyboard_2_button:
437            sel_name = 'Keyboard2'
438        elif sel == self._idevices_button:
439            sel_name = 'iDevices'
440        else:
441            sel_name = 'Back'
442        ba.app.ui.window_states[type(self)] = sel_name
443
444    def _restore_state(self) -> None:
445        sel_name = ba.app.ui.window_states.get(type(self))
446        if sel_name == 'GamePads':
447            sel = self._gamepads_button
448        elif sel_name == 'Touch':
449            sel = self._touch_button
450        elif sel_name == 'Keyboard':
451            sel = self._keyboard_button
452        elif sel_name == 'Keyboard2':
453            sel = self._keyboard_2_button
454        elif sel_name == 'iDevices':
455            sel = self._idevices_button
456        elif sel_name == 'Back':
457            sel = self._back_button
458        else:
459            sel = (
460                self._gamepads_button
461                if self._gamepads_button is not None
462                else self._back_button
463            )
464        ba.containerwidget(edit=self._root_widget, selected_child=sel)
465
466    def _back(self) -> None:
467        # pylint: disable=cyclic-import
468        from bastd.ui.settings.allsettings import AllSettingsWindow
469
470        self._save_state()
471        ba.containerwidget(
472            edit=self._root_widget, transition=self._transition_out
473        )
474        ba.app.ui.set_main_menu_window(
475            AllSettingsWindow(transition='in_left').get_root_widget()
476        )
class ControlsSettingsWindow(ba.ui.Window):
 17class ControlsSettingsWindow(ba.Window):
 18    """Top level control settings window."""
 19
 20    def __init__(
 21        self,
 22        transition: str = 'in_right',
 23        origin_widget: ba.Widget | None = None,
 24    ):
 25        # FIXME: should tidy up here.
 26        # pylint: disable=too-many-statements
 27        # pylint: disable=too-many-branches
 28        # pylint: disable=too-many-locals
 29        # pylint: disable=cyclic-import
 30        from bastd.ui import popup as popup_ui
 31
 32        self._have_selected_child = False
 33
 34        scale_origin: tuple[float, float] | None
 35
 36        # If they provided an origin-widget, scale up from that.
 37        if origin_widget is not None:
 38            self._transition_out = 'out_scale'
 39            scale_origin = origin_widget.get_screen_space_center()
 40            transition = 'in_scale'
 41        else:
 42            self._transition_out = 'out_right'
 43            scale_origin = None
 44
 45        self._r = 'configControllersWindow'
 46        app = ba.app
 47
 48        # is_fire_tv = ba.internal.is_running_on_fire_tv()
 49
 50        spacing = 50.0
 51        button_width = 350.0
 52        width = 460.0
 53        height = 130.0
 54
 55        space_height = spacing * 0.3
 56
 57        # FIXME: should create vis settings in platform for these,
 58        #  not hard code them here.
 59
 60        show_gamepads = False
 61        platform = app.platform
 62        subplatform = app.subplatform
 63        non_vr_windows = platform == 'windows' and (
 64            subplatform != 'oculus' or not app.vr_mode
 65        )
 66        if platform in ('linux', 'android', 'mac') or non_vr_windows:
 67            show_gamepads = True
 68            height += spacing
 69
 70        show_touch = False
 71        if ba.internal.have_touchscreen_input():
 72            show_touch = True
 73            height += spacing
 74
 75        show_space_1 = False
 76        if show_gamepads or show_touch:
 77            show_space_1 = True
 78            height += space_height
 79
 80        show_keyboard = False
 81        if (
 82            ba.internal.getinputdevice('Keyboard', '#1', doraise=False)
 83            is not None
 84        ):
 85            show_keyboard = True
 86            height += spacing
 87        show_keyboard_p2 = False if app.vr_mode else show_keyboard
 88        if show_keyboard_p2:
 89            height += spacing
 90
 91        show_space_2 = False
 92        if show_keyboard:
 93            show_space_2 = True
 94            height += space_height
 95
 96        if bool(True):
 97            show_remote = True
 98            height += spacing
 99        else:
100            show_remote = False
101
102        # On windows (outside of oculus/vr), show an option to disable xinput.
103        show_xinput_toggle = False
104        if platform == 'windows' and not app.vr_mode:
105            show_xinput_toggle = True
106
107        # On mac builds, show an option to switch between generic and
108        # made-for-iOS/Mac systems
109        # (we can run into problems where devices register as one of each
110        # type otherwise)..
111        show_mac_controller_subsystem = False
112        if platform == 'mac' and ba.internal.is_xcode_build():
113            show_mac_controller_subsystem = True
114
115        if show_mac_controller_subsystem:
116            height += spacing * 1.5
117
118        if show_xinput_toggle:
119            height += spacing
120
121        uiscale = ba.app.ui.uiscale
122        smallscale = 1.7 if show_keyboard else 2.2
123        super().__init__(
124            root_widget=ba.containerwidget(
125                size=(width, height),
126                transition=transition,
127                scale_origin_stack_offset=scale_origin,
128                stack_offset=(
129                    (0, -10) if uiscale is ba.UIScale.SMALL else (0, 0)
130                ),
131                scale=(
132                    smallscale
133                    if uiscale is ba.UIScale.SMALL
134                    else 1.5
135                    if uiscale is ba.UIScale.MEDIUM
136                    else 1.0
137                ),
138            )
139        )
140        self._back_button = btn = ba.buttonwidget(
141            parent=self._root_widget,
142            position=(35, height - 60),
143            size=(140, 65),
144            scale=0.8,
145            text_scale=1.2,
146            autoselect=True,
147            label=ba.Lstr(resource='backText'),
148            button_type='back',
149            on_activate_call=self._back,
150        )
151        ba.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: ba.Widget | None = None
155        self._touch_button: ba.Widget | None = None
156        self._keyboard_button: ba.Widget | None = None
157        self._keyboard_2_button: ba.Widget | None = None
158        self._idevices_button: ba.Widget | None = None
159
160        ba.textwidget(
161            parent=self._root_widget,
162            position=(0, height - 49),
163            size=(width, 25),
164            text=ba.Lstr(resource=self._r + '.titleText'),
165            color=ba.app.ui.title_color,
166            h_align='center',
167            v_align='top',
168        )
169        ba.buttonwidget(
170            edit=btn,
171            button_type='backSmall',
172            size=(60, 60),
173            label=ba.charstr(ba.SpecialChar.BACK),
174        )
175
176        v = height - 75
177        v -= spacing
178
179        if show_touch:
180            self._touch_button = btn = ba.buttonwidget(
181                parent=self._root_widget,
182                position=((width - button_width) / 2, v),
183                size=(button_width, 43),
184                autoselect=True,
185                label=ba.Lstr(resource=self._r + '.configureTouchText'),
186                on_activate_call=self._do_touchscreen,
187            )
188            if ba.app.ui.use_toolbars:
189                ba.widget(
190                    edit=btn,
191                    right_widget=ba.internal.get_special_widget('party_button'),
192                )
193            if not self._have_selected_child:
194                ba.containerwidget(
195                    edit=self._root_widget, selected_child=self._touch_button
196                )
197                ba.widget(
198                    edit=self._back_button, down_widget=self._touch_button
199                )
200                self._have_selected_child = True
201            v -= spacing
202
203        if show_gamepads:
204            self._gamepads_button = btn = ba.buttonwidget(
205                parent=self._root_widget,
206                position=((width - button_width) / 2 - 7, v),
207                size=(button_width, 43),
208                autoselect=True,
209                label=ba.Lstr(resource=self._r + '.configureControllersText'),
210                on_activate_call=self._do_gamepads,
211            )
212            if ba.app.ui.use_toolbars:
213                ba.widget(
214                    edit=btn,
215                    right_widget=ba.internal.get_special_widget('party_button'),
216                )
217            if not self._have_selected_child:
218                ba.containerwidget(
219                    edit=self._root_widget, selected_child=self._gamepads_button
220                )
221                ba.widget(
222                    edit=self._back_button, 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 = ba.buttonwidget(
234                parent=self._root_widget,
235                position=((width - button_width) / 2 + 5, v),
236                size=(button_width, 43),
237                autoselect=True,
238                label=ba.Lstr(resource=self._r + '.configureKeyboardText'),
239                on_activate_call=self._config_keyboard,
240            )
241            if ba.app.ui.use_toolbars:
242                ba.widget(
243                    edit=btn,
244                    right_widget=ba.internal.get_special_widget('party_button'),
245                )
246            if not self._have_selected_child:
247                ba.containerwidget(
248                    edit=self._root_widget, selected_child=self._keyboard_button
249                )
250                ba.widget(
251                    edit=self._back_button, down_widget=self._keyboard_button
252                )
253                self._have_selected_child = True
254            v -= spacing
255        if show_keyboard_p2:
256            self._keyboard_2_button = ba.buttonwidget(
257                parent=self._root_widget,
258                position=((width - button_width) / 2 - 3, v),
259                size=(button_width, 43),
260                autoselect=True,
261                label=ba.Lstr(resource=self._r + '.configureKeyboard2Text'),
262                on_activate_call=self._config_keyboard2,
263            )
264            v -= spacing
265        if show_space_2:
266            v -= space_height
267        if show_remote:
268            self._idevices_button = btn = ba.buttonwidget(
269                parent=self._root_widget,
270                position=((width - button_width) / 2 - 5, v),
271                size=(button_width, 43),
272                autoselect=True,
273                label=ba.Lstr(resource=self._r + '.configureMobileText'),
274                on_activate_call=self._do_mobile_devices,
275            )
276            if ba.app.ui.use_toolbars:
277                ba.widget(
278                    edit=btn,
279                    right_widget=ba.internal.get_special_widget('party_button'),
280                )
281            if not self._have_selected_child:
282                ba.containerwidget(
283                    edit=self._root_widget, selected_child=self._idevices_button
284                )
285                ba.widget(
286                    edit=self._back_button, down_widget=self._idevices_button
287                )
288                self._have_selected_child = True
289            v -= spacing
290
291        if show_xinput_toggle:
292
293            def do_toggle(value: bool) -> None:
294                ba.screenmessage(
295                    ba.Lstr(resource='settingsWindowAdvanced.mustRestartText'),
296                    color=(1, 1, 0),
297                )
298                ba.playsound(ba.getsound('gunCocking'))
299                ba.internal.set_low_level_config_value(
300                    'enablexinput', not value
301                )
302
303            ba.checkboxwidget(
304                parent=self._root_widget,
305                position=(100, v + 3),
306                size=(120, 30),
307                value=(
308                    not ba.internal.get_low_level_config_value(
309                        'enablexinput', 1
310                    )
311                ),
312                maxwidth=200,
313                on_value_change_call=do_toggle,
314                text=ba.Lstr(resource='disableXInputText'),
315                autoselect=True,
316            )
317            ba.textwidget(
318                parent=self._root_widget,
319                position=(width * 0.5, v - 5),
320                size=(0, 0),
321                text=ba.Lstr(resource='disableXInputDescriptionText'),
322                scale=0.5,
323                h_align='center',
324                v_align='center',
325                color=ba.app.ui.infotextcolor,
326                maxwidth=width * 0.8,
327            )
328            v -= spacing
329        if show_mac_controller_subsystem:
330            popup_ui.PopupMenu(
331                parent=self._root_widget,
332                position=(260, v - 10),
333                width=160,
334                button_size=(150, 50),
335                scale=1.5,
336                choices=['Classic', 'MFi', 'Both'],
337                choices_display=[
338                    ba.Lstr(resource='macControllerSubsystemClassicText'),
339                    ba.Lstr(resource='macControllerSubsystemMFiText'),
340                    ba.Lstr(resource='macControllerSubsystemBothText'),
341                ],
342                current_choice=ba.app.config.resolve(
343                    'Mac Controller Subsystem'
344                ),
345                on_value_change_call=self._set_mac_controller_subsystem,
346            )
347            ba.textwidget(
348                parent=self._root_widget,
349                position=(245, v + 13),
350                size=(0, 0),
351                text=ba.Lstr(resource='macControllerSubsystemTitleText'),
352                scale=1.0,
353                h_align='right',
354                v_align='center',
355                color=ba.app.ui.infotextcolor,
356                maxwidth=180,
357            )
358            ba.textwidget(
359                parent=self._root_widget,
360                position=(width * 0.5, v - 20),
361                size=(0, 0),
362                text=ba.Lstr(resource='macControllerSubsystemDescriptionText'),
363                scale=0.5,
364                h_align='center',
365                v_align='center',
366                color=ba.app.ui.infotextcolor,
367                maxwidth=width * 0.8,
368            )
369            v -= spacing * 1.5
370        self._restore_state()
371
372    def _set_mac_controller_subsystem(self, val: str) -> None:
373        cfg = ba.app.config
374        cfg['Mac Controller Subsystem'] = val
375        cfg.apply_and_commit()
376
377    def _config_keyboard(self) -> None:
378        # pylint: disable=cyclic-import
379        from bastd.ui.settings.keyboard import ConfigKeyboardWindow
380
381        self._save_state()
382        ba.containerwidget(edit=self._root_widget, transition='out_left')
383        ba.app.ui.set_main_menu_window(
384            ConfigKeyboardWindow(
385                ba.internal.getinputdevice('Keyboard', '#1')
386            ).get_root_widget()
387        )
388
389    def _config_keyboard2(self) -> None:
390        # pylint: disable=cyclic-import
391        from bastd.ui.settings.keyboard import ConfigKeyboardWindow
392
393        self._save_state()
394        ba.containerwidget(edit=self._root_widget, transition='out_left')
395        ba.app.ui.set_main_menu_window(
396            ConfigKeyboardWindow(
397                ba.internal.getinputdevice('Keyboard', '#2')
398            ).get_root_widget()
399        )
400
401    def _do_mobile_devices(self) -> None:
402        # pylint: disable=cyclic-import
403        from bastd.ui.settings.remoteapp import RemoteAppSettingsWindow
404
405        self._save_state()
406        ba.containerwidget(edit=self._root_widget, transition='out_left')
407        ba.app.ui.set_main_menu_window(
408            RemoteAppSettingsWindow().get_root_widget()
409        )
410
411    def _do_gamepads(self) -> None:
412        # pylint: disable=cyclic-import
413        from bastd.ui.settings.gamepadselect import GamepadSelectWindow
414
415        self._save_state()
416        ba.containerwidget(edit=self._root_widget, transition='out_left')
417        ba.app.ui.set_main_menu_window(GamepadSelectWindow().get_root_widget())
418
419    def _do_touchscreen(self) -> None:
420        # pylint: disable=cyclic-import
421        from bastd.ui.settings.touchscreen import TouchscreenSettingsWindow
422
423        self._save_state()
424        ba.containerwidget(edit=self._root_widget, transition='out_left')
425        ba.app.ui.set_main_menu_window(
426            TouchscreenSettingsWindow().get_root_widget()
427        )
428
429    def _save_state(self) -> None:
430        sel = self._root_widget.get_selected_child()
431        if sel == self._gamepads_button:
432            sel_name = 'GamePads'
433        elif sel == self._touch_button:
434            sel_name = 'Touch'
435        elif sel == self._keyboard_button:
436            sel_name = 'Keyboard'
437        elif sel == self._keyboard_2_button:
438            sel_name = 'Keyboard2'
439        elif sel == self._idevices_button:
440            sel_name = 'iDevices'
441        else:
442            sel_name = 'Back'
443        ba.app.ui.window_states[type(self)] = sel_name
444
445    def _restore_state(self) -> None:
446        sel_name = ba.app.ui.window_states.get(type(self))
447        if sel_name == 'GamePads':
448            sel = self._gamepads_button
449        elif sel_name == 'Touch':
450            sel = self._touch_button
451        elif sel_name == 'Keyboard':
452            sel = self._keyboard_button
453        elif sel_name == 'Keyboard2':
454            sel = self._keyboard_2_button
455        elif sel_name == 'iDevices':
456            sel = self._idevices_button
457        elif sel_name == 'Back':
458            sel = self._back_button
459        else:
460            sel = (
461                self._gamepads_button
462                if self._gamepads_button is not None
463                else self._back_button
464            )
465        ba.containerwidget(edit=self._root_widget, selected_child=sel)
466
467    def _back(self) -> None:
468        # pylint: disable=cyclic-import
469        from bastd.ui.settings.allsettings import AllSettingsWindow
470
471        self._save_state()
472        ba.containerwidget(
473            edit=self._root_widget, transition=self._transition_out
474        )
475        ba.app.ui.set_main_menu_window(
476            AllSettingsWindow(transition='in_left').get_root_widget()
477        )

Top level control settings window.

ControlsSettingsWindow( transition: str = 'in_right', origin_widget: _ba.Widget | None = None)
 20    def __init__(
 21        self,
 22        transition: str = 'in_right',
 23        origin_widget: ba.Widget | None = None,
 24    ):
 25        # FIXME: should tidy up here.
 26        # pylint: disable=too-many-statements
 27        # pylint: disable=too-many-branches
 28        # pylint: disable=too-many-locals
 29        # pylint: disable=cyclic-import
 30        from bastd.ui import popup as popup_ui
 31
 32        self._have_selected_child = False
 33
 34        scale_origin: tuple[float, float] | None
 35
 36        # If they provided an origin-widget, scale up from that.
 37        if origin_widget is not None:
 38            self._transition_out = 'out_scale'
 39            scale_origin = origin_widget.get_screen_space_center()
 40            transition = 'in_scale'
 41        else:
 42            self._transition_out = 'out_right'
 43            scale_origin = None
 44
 45        self._r = 'configControllersWindow'
 46        app = ba.app
 47
 48        # is_fire_tv = ba.internal.is_running_on_fire_tv()
 49
 50        spacing = 50.0
 51        button_width = 350.0
 52        width = 460.0
 53        height = 130.0
 54
 55        space_height = spacing * 0.3
 56
 57        # FIXME: should create vis settings in platform for these,
 58        #  not hard code them here.
 59
 60        show_gamepads = False
 61        platform = app.platform
 62        subplatform = app.subplatform
 63        non_vr_windows = platform == 'windows' and (
 64            subplatform != 'oculus' or not app.vr_mode
 65        )
 66        if platform in ('linux', 'android', 'mac') or non_vr_windows:
 67            show_gamepads = True
 68            height += spacing
 69
 70        show_touch = False
 71        if ba.internal.have_touchscreen_input():
 72            show_touch = True
 73            height += spacing
 74
 75        show_space_1 = False
 76        if show_gamepads or show_touch:
 77            show_space_1 = True
 78            height += space_height
 79
 80        show_keyboard = False
 81        if (
 82            ba.internal.getinputdevice('Keyboard', '#1', doraise=False)
 83            is not None
 84        ):
 85            show_keyboard = True
 86            height += spacing
 87        show_keyboard_p2 = False if app.vr_mode else show_keyboard
 88        if show_keyboard_p2:
 89            height += spacing
 90
 91        show_space_2 = False
 92        if show_keyboard:
 93            show_space_2 = True
 94            height += space_height
 95
 96        if bool(True):
 97            show_remote = True
 98            height += spacing
 99        else:
100            show_remote = False
101
102        # On windows (outside of oculus/vr), show an option to disable xinput.
103        show_xinput_toggle = False
104        if platform == 'windows' and not app.vr_mode:
105            show_xinput_toggle = True
106
107        # On mac builds, show an option to switch between generic and
108        # made-for-iOS/Mac systems
109        # (we can run into problems where devices register as one of each
110        # type otherwise)..
111        show_mac_controller_subsystem = False
112        if platform == 'mac' and ba.internal.is_xcode_build():
113            show_mac_controller_subsystem = True
114
115        if show_mac_controller_subsystem:
116            height += spacing * 1.5
117
118        if show_xinput_toggle:
119            height += spacing
120
121        uiscale = ba.app.ui.uiscale
122        smallscale = 1.7 if show_keyboard else 2.2
123        super().__init__(
124            root_widget=ba.containerwidget(
125                size=(width, height),
126                transition=transition,
127                scale_origin_stack_offset=scale_origin,
128                stack_offset=(
129                    (0, -10) if uiscale is ba.UIScale.SMALL else (0, 0)
130                ),
131                scale=(
132                    smallscale
133                    if uiscale is ba.UIScale.SMALL
134                    else 1.5
135                    if uiscale is ba.UIScale.MEDIUM
136                    else 1.0
137                ),
138            )
139        )
140        self._back_button = btn = ba.buttonwidget(
141            parent=self._root_widget,
142            position=(35, height - 60),
143            size=(140, 65),
144            scale=0.8,
145            text_scale=1.2,
146            autoselect=True,
147            label=ba.Lstr(resource='backText'),
148            button_type='back',
149            on_activate_call=self._back,
150        )
151        ba.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: ba.Widget | None = None
155        self._touch_button: ba.Widget | None = None
156        self._keyboard_button: ba.Widget | None = None
157        self._keyboard_2_button: ba.Widget | None = None
158        self._idevices_button: ba.Widget | None = None
159
160        ba.textwidget(
161            parent=self._root_widget,
162            position=(0, height - 49),
163            size=(width, 25),
164            text=ba.Lstr(resource=self._r + '.titleText'),
165            color=ba.app.ui.title_color,
166            h_align='center',
167            v_align='top',
168        )
169        ba.buttonwidget(
170            edit=btn,
171            button_type='backSmall',
172            size=(60, 60),
173            label=ba.charstr(ba.SpecialChar.BACK),
174        )
175
176        v = height - 75
177        v -= spacing
178
179        if show_touch:
180            self._touch_button = btn = ba.buttonwidget(
181                parent=self._root_widget,
182                position=((width - button_width) / 2, v),
183                size=(button_width, 43),
184                autoselect=True,
185                label=ba.Lstr(resource=self._r + '.configureTouchText'),
186                on_activate_call=self._do_touchscreen,
187            )
188            if ba.app.ui.use_toolbars:
189                ba.widget(
190                    edit=btn,
191                    right_widget=ba.internal.get_special_widget('party_button'),
192                )
193            if not self._have_selected_child:
194                ba.containerwidget(
195                    edit=self._root_widget, selected_child=self._touch_button
196                )
197                ba.widget(
198                    edit=self._back_button, down_widget=self._touch_button
199                )
200                self._have_selected_child = True
201            v -= spacing
202
203        if show_gamepads:
204            self._gamepads_button = btn = ba.buttonwidget(
205                parent=self._root_widget,
206                position=((width - button_width) / 2 - 7, v),
207                size=(button_width, 43),
208                autoselect=True,
209                label=ba.Lstr(resource=self._r + '.configureControllersText'),
210                on_activate_call=self._do_gamepads,
211            )
212            if ba.app.ui.use_toolbars:
213                ba.widget(
214                    edit=btn,
215                    right_widget=ba.internal.get_special_widget('party_button'),
216                )
217            if not self._have_selected_child:
218                ba.containerwidget(
219                    edit=self._root_widget, selected_child=self._gamepads_button
220                )
221                ba.widget(
222                    edit=self._back_button, 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 = ba.buttonwidget(
234                parent=self._root_widget,
235                position=((width - button_width) / 2 + 5, v),
236                size=(button_width, 43),
237                autoselect=True,
238                label=ba.Lstr(resource=self._r + '.configureKeyboardText'),
239                on_activate_call=self._config_keyboard,
240            )
241            if ba.app.ui.use_toolbars:
242                ba.widget(
243                    edit=btn,
244                    right_widget=ba.internal.get_special_widget('party_button'),
245                )
246            if not self._have_selected_child:
247                ba.containerwidget(
248                    edit=self._root_widget, selected_child=self._keyboard_button
249                )
250                ba.widget(
251                    edit=self._back_button, down_widget=self._keyboard_button
252                )
253                self._have_selected_child = True
254            v -= spacing
255        if show_keyboard_p2:
256            self._keyboard_2_button = ba.buttonwidget(
257                parent=self._root_widget,
258                position=((width - button_width) / 2 - 3, v),
259                size=(button_width, 43),
260                autoselect=True,
261                label=ba.Lstr(resource=self._r + '.configureKeyboard2Text'),
262                on_activate_call=self._config_keyboard2,
263            )
264            v -= spacing
265        if show_space_2:
266            v -= space_height
267        if show_remote:
268            self._idevices_button = btn = ba.buttonwidget(
269                parent=self._root_widget,
270                position=((width - button_width) / 2 - 5, v),
271                size=(button_width, 43),
272                autoselect=True,
273                label=ba.Lstr(resource=self._r + '.configureMobileText'),
274                on_activate_call=self._do_mobile_devices,
275            )
276            if ba.app.ui.use_toolbars:
277                ba.widget(
278                    edit=btn,
279                    right_widget=ba.internal.get_special_widget('party_button'),
280                )
281            if not self._have_selected_child:
282                ba.containerwidget(
283                    edit=self._root_widget, selected_child=self._idevices_button
284                )
285                ba.widget(
286                    edit=self._back_button, down_widget=self._idevices_button
287                )
288                self._have_selected_child = True
289            v -= spacing
290
291        if show_xinput_toggle:
292
293            def do_toggle(value: bool) -> None:
294                ba.screenmessage(
295                    ba.Lstr(resource='settingsWindowAdvanced.mustRestartText'),
296                    color=(1, 1, 0),
297                )
298                ba.playsound(ba.getsound('gunCocking'))
299                ba.internal.set_low_level_config_value(
300                    'enablexinput', not value
301                )
302
303            ba.checkboxwidget(
304                parent=self._root_widget,
305                position=(100, v + 3),
306                size=(120, 30),
307                value=(
308                    not ba.internal.get_low_level_config_value(
309                        'enablexinput', 1
310                    )
311                ),
312                maxwidth=200,
313                on_value_change_call=do_toggle,
314                text=ba.Lstr(resource='disableXInputText'),
315                autoselect=True,
316            )
317            ba.textwidget(
318                parent=self._root_widget,
319                position=(width * 0.5, v - 5),
320                size=(0, 0),
321                text=ba.Lstr(resource='disableXInputDescriptionText'),
322                scale=0.5,
323                h_align='center',
324                v_align='center',
325                color=ba.app.ui.infotextcolor,
326                maxwidth=width * 0.8,
327            )
328            v -= spacing
329        if show_mac_controller_subsystem:
330            popup_ui.PopupMenu(
331                parent=self._root_widget,
332                position=(260, v - 10),
333                width=160,
334                button_size=(150, 50),
335                scale=1.5,
336                choices=['Classic', 'MFi', 'Both'],
337                choices_display=[
338                    ba.Lstr(resource='macControllerSubsystemClassicText'),
339                    ba.Lstr(resource='macControllerSubsystemMFiText'),
340                    ba.Lstr(resource='macControllerSubsystemBothText'),
341                ],
342                current_choice=ba.app.config.resolve(
343                    'Mac Controller Subsystem'
344                ),
345                on_value_change_call=self._set_mac_controller_subsystem,
346            )
347            ba.textwidget(
348                parent=self._root_widget,
349                position=(245, v + 13),
350                size=(0, 0),
351                text=ba.Lstr(resource='macControllerSubsystemTitleText'),
352                scale=1.0,
353                h_align='right',
354                v_align='center',
355                color=ba.app.ui.infotextcolor,
356                maxwidth=180,
357            )
358            ba.textwidget(
359                parent=self._root_widget,
360                position=(width * 0.5, v - 20),
361                size=(0, 0),
362                text=ba.Lstr(resource='macControllerSubsystemDescriptionText'),
363                scale=0.5,
364                h_align='center',
365                v_align='center',
366                color=ba.app.ui.infotextcolor,
367                maxwidth=width * 0.8,
368            )
369            v -= spacing * 1.5
370        self._restore_state()
Inherited Members
ba.ui.Window
get_root_widget