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

Top level control settings window.

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