bauiv1lib.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 override
  8
  9import bascenev1 as bs
 10import bauiv1 as bui
 11
 12
 13class ControlsSettingsWindow(bui.MainWindow):
 14    """Top level control settings window."""
 15
 16    def __init__(
 17        self,
 18        transition: str | None = 'in_right',
 19        origin_widget: bui.Widget | None = None,
 20    ):
 21        # FIXME: should tidy up here.
 22        # pylint: disable=too-many-statements
 23        # pylint: disable=too-many-branches
 24        # pylint: disable=too-many-locals
 25        # pylint: disable=cyclic-import
 26
 27        self._have_selected_child = False
 28
 29        self._r = 'configControllersWindow'
 30        uiscale = bui.app.ui_v1.uiscale
 31        app = bui.app
 32        assert app.classic is not None
 33
 34        spacing = 50.0
 35        button_width = 350.0
 36        width = 800.0 if uiscale is bui.UIScale.SMALL else 460.0
 37        height = 300 if uiscale is bui.UIScale.SMALL else 130.0
 38
 39        yoffs = -60 if uiscale is bui.UIScale.SMALL else 0
 40        space_height = spacing * 0.3
 41
 42        # FIXME: should create vis settings under platform or
 43        # app-adapter to determine whether to show this stuff; not hard
 44        # code it.
 45
 46        show_gamepads = False
 47        platform = app.classic.platform
 48        subplatform = app.classic.subplatform
 49        non_vr_windows = platform == 'windows' and (
 50            subplatform != 'oculus' or not app.env.vr
 51        )
 52        if platform in ('linux', 'android', 'mac') or non_vr_windows:
 53            show_gamepads = True
 54            height += spacing
 55
 56        show_touch = False
 57        if bs.have_touchscreen_input():
 58            show_touch = True
 59            height += spacing
 60
 61        show_space_1 = False
 62        if show_gamepads or show_touch:
 63            show_space_1 = True
 64            height += space_height
 65
 66        show_keyboard = False
 67        if bs.getinputdevice('Keyboard', '#1', doraise=False) is not None:
 68            show_keyboard = True
 69            height += spacing
 70        show_keyboard_p2 = False if app.env.vr else show_keyboard
 71        if show_keyboard_p2:
 72            height += spacing
 73
 74        show_space_2 = False
 75        if show_keyboard:
 76            show_space_2 = True
 77            height += space_height
 78
 79        if bool(True):
 80            show_remote = True
 81            height += spacing
 82        else:
 83            show_remote = False
 84
 85        # On windows (outside of oculus/vr), show an option to disable xinput.
 86        show_xinput_toggle = False
 87        if platform == 'windows' and not app.env.vr:
 88            show_xinput_toggle = True
 89
 90        if show_xinput_toggle:
 91            height += spacing
 92
 93        assert bui.app.classic is not None
 94        smallscale = 1.7
 95        super().__init__(
 96            root_widget=bui.containerwidget(
 97                size=(width, height),
 98                stack_offset=(
 99                    (0, -10) if uiscale is bui.UIScale.SMALL else (0, 0)
100                ),
101                scale=(
102                    smallscale
103                    if uiscale is bui.UIScale.SMALL
104                    else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
105                ),
106                toolbar_visibility=(
107                    'menu_minimal'
108                    if uiscale is bui.UIScale.SMALL
109                    else 'menu_full'
110                ),
111            ),
112            transition=transition,
113            origin_widget=origin_widget,
114        )
115
116        self._back_button: bui.Widget | None
117        if uiscale is bui.UIScale.SMALL:
118            bui.containerwidget(
119                edit=self._root_widget, on_cancel_call=self.main_window_back
120            )
121            self._back_button = None
122        else:
123            self._back_button = btn = bui.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=bui.Lstr(resource='backText'),
131                button_type='back',
132                on_activate_call=self.main_window_back,
133            )
134            bui.containerwidget(edit=self._root_widget, cancel_button=btn)
135            bui.buttonwidget(
136                edit=btn,
137                button_type='backSmall',
138                size=(60, 60),
139                label=bui.charstr(bui.SpecialChar.BACK),
140            )
141
142        # We need these vars to exist even if the buttons don't.
143        self._gamepads_button: bui.Widget | None = None
144        self._touch_button: bui.Widget | None = None
145        self._keyboard_button: bui.Widget | None = None
146        self._keyboard_2_button: bui.Widget | None = None
147        self._idevices_button: bui.Widget | None = None
148
149        bui.textwidget(
150            parent=self._root_widget,
151            position=(0, height - 49 + yoffs),
152            size=(width, 25),
153            text=bui.Lstr(resource=f'{self._r}.titleText'),
154            color=bui.app.ui_v1.title_color,
155            h_align='center',
156            v_align='top',
157        )
158
159        v = height - 75 + yoffs
160        v -= spacing
161
162        if show_touch:
163            self._touch_button = btn = bui.buttonwidget(
164                parent=self._root_widget,
165                position=((width - button_width) / 2, v),
166                size=(button_width, 43),
167                autoselect=True,
168                label=bui.Lstr(resource=f'{self._r}.configureTouchText'),
169                on_activate_call=self._do_touchscreen,
170            )
171            bui.widget(
172                edit=btn,
173                right_widget=bui.get_special_widget('squad_button'),
174            )
175            if not self._have_selected_child:
176                bui.containerwidget(
177                    edit=self._root_widget, selected_child=self._touch_button
178                )
179                if self._back_button is not None:
180                    bui.widget(
181                        edit=self._back_button, down_widget=self._touch_button
182                    )
183                self._have_selected_child = True
184            v -= spacing
185
186        if show_gamepads:
187            self._gamepads_button = btn = bui.buttonwidget(
188                parent=self._root_widget,
189                position=((width - button_width) / 2 - 7, v),
190                size=(button_width, 43),
191                autoselect=True,
192                label=bui.Lstr(resource=f'{self._r}.configureControllersText'),
193                on_activate_call=self._do_gamepads,
194            )
195            bui.widget(
196                edit=btn,
197                right_widget=bui.get_special_widget('squad_button'),
198            )
199            if not self._have_selected_child:
200                bui.containerwidget(
201                    edit=self._root_widget, selected_child=self._gamepads_button
202                )
203                if self._back_button is not None:
204                    bui.widget(
205                        edit=self._back_button,
206                        down_widget=self._gamepads_button,
207                    )
208                self._have_selected_child = True
209            v -= spacing
210        else:
211            self._gamepads_button = None
212
213        if show_space_1:
214            v -= space_height
215
216        if show_keyboard:
217            self._keyboard_button = btn = bui.buttonwidget(
218                parent=self._root_widget,
219                position=((width - button_width) / 2 - 5, v),
220                size=(button_width, 43),
221                autoselect=True,
222                label=bui.Lstr(resource=f'{self._r}.configureKeyboardText'),
223                on_activate_call=self._config_keyboard,
224            )
225            bui.widget(
226                edit=self._keyboard_button, left_widget=self._keyboard_button
227            )
228            bui.widget(
229                edit=btn,
230                right_widget=bui.get_special_widget('squad_button'),
231            )
232            if not self._have_selected_child:
233                bui.containerwidget(
234                    edit=self._root_widget, selected_child=self._keyboard_button
235                )
236                if self._back_button is not None:
237                    bui.widget(
238                        edit=self._back_button,
239                        down_widget=self._keyboard_button,
240                    )
241                self._have_selected_child = True
242            v -= spacing
243        if show_keyboard_p2:
244            self._keyboard_2_button = bui.buttonwidget(
245                parent=self._root_widget,
246                position=((width - button_width) / 2 - 3, v),
247                size=(button_width, 43),
248                autoselect=True,
249                label=bui.Lstr(resource=f'{self._r}.configureKeyboard2Text'),
250                on_activate_call=self._config_keyboard2,
251            )
252            v -= spacing
253            bui.widget(
254                edit=self._keyboard_2_button,
255                left_widget=self._keyboard_2_button,
256            )
257        if show_space_2:
258            v -= space_height
259        if show_remote:
260            self._idevices_button = btn = bui.buttonwidget(
261                parent=self._root_widget,
262                position=((width - button_width) / 2 - 5, v),
263                size=(button_width, 43),
264                autoselect=True,
265                label=bui.Lstr(resource=f'{self._r}.configureMobileText'),
266                on_activate_call=self._do_mobile_devices,
267            )
268            bui.widget(
269                edit=self._idevices_button, left_widget=self._idevices_button
270            )
271            bui.widget(
272                edit=btn,
273                right_widget=bui.get_special_widget('squad_button'),
274            )
275            if not self._have_selected_child:
276                bui.containerwidget(
277                    edit=self._root_widget, selected_child=self._idevices_button
278                )
279                if self._back_button is not None:
280                    bui.widget(
281                        edit=self._back_button,
282                        down_widget=self._idevices_button,
283                    )
284                self._have_selected_child = True
285            v -= spacing
286
287        if show_xinput_toggle:
288
289            def do_toggle(value: bool) -> None:
290                bui.screenmessage(
291                    bui.Lstr(resource='settingsWindowAdvanced.mustRestartText'),
292                    color=(1, 1, 0),
293                )
294                bui.getsound('gunCocking').play()
295                bui.set_low_level_config_value('enablexinput', not value)
296
297            xinput_checkbox = bui.checkboxwidget(
298                parent=self._root_widget,
299                position=(100, v + 3),
300                size=(120, 30),
301                value=(not bui.get_low_level_config_value('enablexinput', 1)),
302                maxwidth=200,
303                on_value_change_call=do_toggle,
304                text=bui.Lstr(resource='disableXInputText'),
305                autoselect=True,
306            )
307            bui.textwidget(
308                parent=self._root_widget,
309                position=(width * 0.5, v - 5),
310                size=(0, 0),
311                text=bui.Lstr(resource='disableXInputDescriptionText'),
312                scale=0.5,
313                h_align='center',
314                v_align='center',
315                color=bui.app.ui_v1.infotextcolor,
316                maxwidth=width * 0.8,
317            )
318            bui.widget(
319                edit=xinput_checkbox,
320                left_widget=xinput_checkbox,
321                right_widget=xinput_checkbox,
322            )
323            v -= spacing
324
325        self._restore_state()
326
327    @override
328    def get_main_window_state(self) -> bui.MainWindowState:
329        # Support recreating our window for back/refresh purposes.
330        cls = type(self)
331        return bui.BasicMainWindowState(
332            create_call=lambda transition, origin_widget: cls(
333                transition=transition, origin_widget=origin_widget
334            )
335        )
336
337    @override
338    def on_main_window_close(self) -> None:
339        self._save_state()
340
341    def _set_mac_controller_subsystem(self, val: str) -> None:
342        cfg = bui.app.config
343        cfg['Mac Controller Subsystem'] = val
344        cfg.apply_and_commit()
345
346    def _config_keyboard(self) -> None:
347        # pylint: disable=cyclic-import
348        from bauiv1lib.settings.keyboard import ConfigKeyboardWindow
349
350        # no-op if we're not in control.
351        if not self.main_window_has_control():
352            return
353
354        self.main_window_replace(
355            ConfigKeyboardWindow(bs.getinputdevice('Keyboard', '#1'))
356        )
357
358    def _config_keyboard2(self) -> None:
359        # pylint: disable=cyclic-import
360        from bauiv1lib.settings.keyboard import ConfigKeyboardWindow
361
362        # no-op if we're not in control.
363        if not self.main_window_has_control():
364            return
365
366        self.main_window_replace(
367            ConfigKeyboardWindow(bs.getinputdevice('Keyboard', '#2'))
368        )
369
370    def _do_mobile_devices(self) -> None:
371        # pylint: disable=cyclic-import
372        from bauiv1lib.settings.remoteapp import RemoteAppSettingsWindow
373
374        # no-op if we're not in control.
375        if not self.main_window_has_control():
376            return
377
378        self.main_window_replace(RemoteAppSettingsWindow())
379
380    def _do_gamepads(self) -> None:
381        # pylint: disable=cyclic-import
382        from bauiv1lib.settings.gamepadselect import GamepadSelectWindow
383
384        # no-op if we're not in control.
385        if not self.main_window_has_control():
386            return
387
388        self.main_window_replace(GamepadSelectWindow())
389
390    def _do_touchscreen(self) -> None:
391        # pylint: disable=cyclic-import
392        from bauiv1lib.settings.touchscreen import TouchscreenSettingsWindow
393
394        # no-op if we're not in control.
395        if not self.main_window_has_control():
396            return
397
398        self.main_window_replace(TouchscreenSettingsWindow())
399
400    def _save_state(self) -> None:
401        sel = self._root_widget.get_selected_child()
402        if sel == self._gamepads_button:
403            sel_name = 'GamePads'
404        elif sel == self._touch_button:
405            sel_name = 'Touch'
406        elif sel == self._keyboard_button:
407            sel_name = 'Keyboard'
408        elif sel == self._keyboard_2_button:
409            sel_name = 'Keyboard2'
410        elif sel == self._idevices_button:
411            sel_name = 'iDevices'
412        else:
413            sel_name = 'Back'
414        assert bui.app.classic is not None
415        bui.app.ui_v1.window_states[type(self)] = sel_name
416
417    def _restore_state(self) -> None:
418        assert bui.app.classic is not None
419        sel_name = bui.app.ui_v1.window_states.get(type(self))
420        if sel_name == 'GamePads':
421            sel = self._gamepads_button
422        elif sel_name == 'Touch':
423            sel = self._touch_button
424        elif sel_name == 'Keyboard':
425            sel = self._keyboard_button
426        elif sel_name == 'Keyboard2':
427            sel = self._keyboard_2_button
428        elif sel_name == 'iDevices':
429            sel = self._idevices_button
430        elif sel_name == 'Back':
431            sel = self._back_button
432        else:
433            sel = (
434                self._gamepads_button
435                if self._gamepads_button is not None
436                else self._back_button
437            )
438        bui.containerwidget(edit=self._root_widget, selected_child=sel)
class ControlsSettingsWindow(bauiv1._uitypes.MainWindow):
 14class ControlsSettingsWindow(bui.MainWindow):
 15    """Top level control settings window."""
 16
 17    def __init__(
 18        self,
 19        transition: str | None = 'in_right',
 20        origin_widget: bui.Widget | None = None,
 21    ):
 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
 28        self._have_selected_child = False
 29
 30        self._r = 'configControllersWindow'
 31        uiscale = bui.app.ui_v1.uiscale
 32        app = bui.app
 33        assert app.classic is not None
 34
 35        spacing = 50.0
 36        button_width = 350.0
 37        width = 800.0 if uiscale is bui.UIScale.SMALL else 460.0
 38        height = 300 if uiscale is bui.UIScale.SMALL else 130.0
 39
 40        yoffs = -60 if uiscale is bui.UIScale.SMALL else 0
 41        space_height = spacing * 0.3
 42
 43        # FIXME: should create vis settings under platform or
 44        # app-adapter to determine whether to show this stuff; not hard
 45        # code it.
 46
 47        show_gamepads = False
 48        platform = app.classic.platform
 49        subplatform = app.classic.subplatform
 50        non_vr_windows = platform == 'windows' and (
 51            subplatform != 'oculus' or not app.env.vr
 52        )
 53        if platform in ('linux', 'android', 'mac') or non_vr_windows:
 54            show_gamepads = True
 55            height += spacing
 56
 57        show_touch = False
 58        if bs.have_touchscreen_input():
 59            show_touch = True
 60            height += spacing
 61
 62        show_space_1 = False
 63        if show_gamepads or show_touch:
 64            show_space_1 = True
 65            height += space_height
 66
 67        show_keyboard = False
 68        if bs.getinputdevice('Keyboard', '#1', doraise=False) is not None:
 69            show_keyboard = True
 70            height += spacing
 71        show_keyboard_p2 = False if app.env.vr else show_keyboard
 72        if show_keyboard_p2:
 73            height += spacing
 74
 75        show_space_2 = False
 76        if show_keyboard:
 77            show_space_2 = True
 78            height += space_height
 79
 80        if bool(True):
 81            show_remote = True
 82            height += spacing
 83        else:
 84            show_remote = False
 85
 86        # On windows (outside of oculus/vr), show an option to disable xinput.
 87        show_xinput_toggle = False
 88        if platform == 'windows' and not app.env.vr:
 89            show_xinput_toggle = True
 90
 91        if show_xinput_toggle:
 92            height += spacing
 93
 94        assert bui.app.classic is not None
 95        smallscale = 1.7
 96        super().__init__(
 97            root_widget=bui.containerwidget(
 98                size=(width, height),
 99                stack_offset=(
100                    (0, -10) if uiscale is bui.UIScale.SMALL else (0, 0)
101                ),
102                scale=(
103                    smallscale
104                    if uiscale is bui.UIScale.SMALL
105                    else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
106                ),
107                toolbar_visibility=(
108                    'menu_minimal'
109                    if uiscale is bui.UIScale.SMALL
110                    else 'menu_full'
111                ),
112            ),
113            transition=transition,
114            origin_widget=origin_widget,
115        )
116
117        self._back_button: bui.Widget | None
118        if uiscale is bui.UIScale.SMALL:
119            bui.containerwidget(
120                edit=self._root_widget, on_cancel_call=self.main_window_back
121            )
122            self._back_button = None
123        else:
124            self._back_button = btn = bui.buttonwidget(
125                parent=self._root_widget,
126                position=(35, height - 60),
127                size=(140, 65),
128                scale=0.8,
129                text_scale=1.2,
130                autoselect=True,
131                label=bui.Lstr(resource='backText'),
132                button_type='back',
133                on_activate_call=self.main_window_back,
134            )
135            bui.containerwidget(edit=self._root_widget, cancel_button=btn)
136            bui.buttonwidget(
137                edit=btn,
138                button_type='backSmall',
139                size=(60, 60),
140                label=bui.charstr(bui.SpecialChar.BACK),
141            )
142
143        # We need these vars to exist even if the buttons don't.
144        self._gamepads_button: bui.Widget | None = None
145        self._touch_button: bui.Widget | None = None
146        self._keyboard_button: bui.Widget | None = None
147        self._keyboard_2_button: bui.Widget | None = None
148        self._idevices_button: bui.Widget | None = None
149
150        bui.textwidget(
151            parent=self._root_widget,
152            position=(0, height - 49 + yoffs),
153            size=(width, 25),
154            text=bui.Lstr(resource=f'{self._r}.titleText'),
155            color=bui.app.ui_v1.title_color,
156            h_align='center',
157            v_align='top',
158        )
159
160        v = height - 75 + yoffs
161        v -= spacing
162
163        if show_touch:
164            self._touch_button = btn = bui.buttonwidget(
165                parent=self._root_widget,
166                position=((width - button_width) / 2, v),
167                size=(button_width, 43),
168                autoselect=True,
169                label=bui.Lstr(resource=f'{self._r}.configureTouchText'),
170                on_activate_call=self._do_touchscreen,
171            )
172            bui.widget(
173                edit=btn,
174                right_widget=bui.get_special_widget('squad_button'),
175            )
176            if not self._have_selected_child:
177                bui.containerwidget(
178                    edit=self._root_widget, selected_child=self._touch_button
179                )
180                if self._back_button is not None:
181                    bui.widget(
182                        edit=self._back_button, down_widget=self._touch_button
183                    )
184                self._have_selected_child = True
185            v -= spacing
186
187        if show_gamepads:
188            self._gamepads_button = btn = bui.buttonwidget(
189                parent=self._root_widget,
190                position=((width - button_width) / 2 - 7, v),
191                size=(button_width, 43),
192                autoselect=True,
193                label=bui.Lstr(resource=f'{self._r}.configureControllersText'),
194                on_activate_call=self._do_gamepads,
195            )
196            bui.widget(
197                edit=btn,
198                right_widget=bui.get_special_widget('squad_button'),
199            )
200            if not self._have_selected_child:
201                bui.containerwidget(
202                    edit=self._root_widget, selected_child=self._gamepads_button
203                )
204                if self._back_button is not None:
205                    bui.widget(
206                        edit=self._back_button,
207                        down_widget=self._gamepads_button,
208                    )
209                self._have_selected_child = True
210            v -= spacing
211        else:
212            self._gamepads_button = None
213
214        if show_space_1:
215            v -= space_height
216
217        if show_keyboard:
218            self._keyboard_button = btn = bui.buttonwidget(
219                parent=self._root_widget,
220                position=((width - button_width) / 2 - 5, v),
221                size=(button_width, 43),
222                autoselect=True,
223                label=bui.Lstr(resource=f'{self._r}.configureKeyboardText'),
224                on_activate_call=self._config_keyboard,
225            )
226            bui.widget(
227                edit=self._keyboard_button, left_widget=self._keyboard_button
228            )
229            bui.widget(
230                edit=btn,
231                right_widget=bui.get_special_widget('squad_button'),
232            )
233            if not self._have_selected_child:
234                bui.containerwidget(
235                    edit=self._root_widget, selected_child=self._keyboard_button
236                )
237                if self._back_button is not None:
238                    bui.widget(
239                        edit=self._back_button,
240                        down_widget=self._keyboard_button,
241                    )
242                self._have_selected_child = True
243            v -= spacing
244        if show_keyboard_p2:
245            self._keyboard_2_button = bui.buttonwidget(
246                parent=self._root_widget,
247                position=((width - button_width) / 2 - 3, v),
248                size=(button_width, 43),
249                autoselect=True,
250                label=bui.Lstr(resource=f'{self._r}.configureKeyboard2Text'),
251                on_activate_call=self._config_keyboard2,
252            )
253            v -= spacing
254            bui.widget(
255                edit=self._keyboard_2_button,
256                left_widget=self._keyboard_2_button,
257            )
258        if show_space_2:
259            v -= space_height
260        if show_remote:
261            self._idevices_button = btn = bui.buttonwidget(
262                parent=self._root_widget,
263                position=((width - button_width) / 2 - 5, v),
264                size=(button_width, 43),
265                autoselect=True,
266                label=bui.Lstr(resource=f'{self._r}.configureMobileText'),
267                on_activate_call=self._do_mobile_devices,
268            )
269            bui.widget(
270                edit=self._idevices_button, left_widget=self._idevices_button
271            )
272            bui.widget(
273                edit=btn,
274                right_widget=bui.get_special_widget('squad_button'),
275            )
276            if not self._have_selected_child:
277                bui.containerwidget(
278                    edit=self._root_widget, selected_child=self._idevices_button
279                )
280                if self._back_button is not None:
281                    bui.widget(
282                        edit=self._back_button,
283                        down_widget=self._idevices_button,
284                    )
285                self._have_selected_child = True
286            v -= spacing
287
288        if show_xinput_toggle:
289
290            def do_toggle(value: bool) -> None:
291                bui.screenmessage(
292                    bui.Lstr(resource='settingsWindowAdvanced.mustRestartText'),
293                    color=(1, 1, 0),
294                )
295                bui.getsound('gunCocking').play()
296                bui.set_low_level_config_value('enablexinput', not value)
297
298            xinput_checkbox = bui.checkboxwidget(
299                parent=self._root_widget,
300                position=(100, v + 3),
301                size=(120, 30),
302                value=(not bui.get_low_level_config_value('enablexinput', 1)),
303                maxwidth=200,
304                on_value_change_call=do_toggle,
305                text=bui.Lstr(resource='disableXInputText'),
306                autoselect=True,
307            )
308            bui.textwidget(
309                parent=self._root_widget,
310                position=(width * 0.5, v - 5),
311                size=(0, 0),
312                text=bui.Lstr(resource='disableXInputDescriptionText'),
313                scale=0.5,
314                h_align='center',
315                v_align='center',
316                color=bui.app.ui_v1.infotextcolor,
317                maxwidth=width * 0.8,
318            )
319            bui.widget(
320                edit=xinput_checkbox,
321                left_widget=xinput_checkbox,
322                right_widget=xinput_checkbox,
323            )
324            v -= spacing
325
326        self._restore_state()
327
328    @override
329    def get_main_window_state(self) -> bui.MainWindowState:
330        # Support recreating our window for back/refresh purposes.
331        cls = type(self)
332        return bui.BasicMainWindowState(
333            create_call=lambda transition, origin_widget: cls(
334                transition=transition, origin_widget=origin_widget
335            )
336        )
337
338    @override
339    def on_main_window_close(self) -> None:
340        self._save_state()
341
342    def _set_mac_controller_subsystem(self, val: str) -> None:
343        cfg = bui.app.config
344        cfg['Mac Controller Subsystem'] = val
345        cfg.apply_and_commit()
346
347    def _config_keyboard(self) -> None:
348        # pylint: disable=cyclic-import
349        from bauiv1lib.settings.keyboard import ConfigKeyboardWindow
350
351        # no-op if we're not in control.
352        if not self.main_window_has_control():
353            return
354
355        self.main_window_replace(
356            ConfigKeyboardWindow(bs.getinputdevice('Keyboard', '#1'))
357        )
358
359    def _config_keyboard2(self) -> None:
360        # pylint: disable=cyclic-import
361        from bauiv1lib.settings.keyboard import ConfigKeyboardWindow
362
363        # no-op if we're not in control.
364        if not self.main_window_has_control():
365            return
366
367        self.main_window_replace(
368            ConfigKeyboardWindow(bs.getinputdevice('Keyboard', '#2'))
369        )
370
371    def _do_mobile_devices(self) -> None:
372        # pylint: disable=cyclic-import
373        from bauiv1lib.settings.remoteapp import RemoteAppSettingsWindow
374
375        # no-op if we're not in control.
376        if not self.main_window_has_control():
377            return
378
379        self.main_window_replace(RemoteAppSettingsWindow())
380
381    def _do_gamepads(self) -> None:
382        # pylint: disable=cyclic-import
383        from bauiv1lib.settings.gamepadselect import GamepadSelectWindow
384
385        # no-op if we're not in control.
386        if not self.main_window_has_control():
387            return
388
389        self.main_window_replace(GamepadSelectWindow())
390
391    def _do_touchscreen(self) -> None:
392        # pylint: disable=cyclic-import
393        from bauiv1lib.settings.touchscreen import TouchscreenSettingsWindow
394
395        # no-op if we're not in control.
396        if not self.main_window_has_control():
397            return
398
399        self.main_window_replace(TouchscreenSettingsWindow())
400
401    def _save_state(self) -> None:
402        sel = self._root_widget.get_selected_child()
403        if sel == self._gamepads_button:
404            sel_name = 'GamePads'
405        elif sel == self._touch_button:
406            sel_name = 'Touch'
407        elif sel == self._keyboard_button:
408            sel_name = 'Keyboard'
409        elif sel == self._keyboard_2_button:
410            sel_name = 'Keyboard2'
411        elif sel == self._idevices_button:
412            sel_name = 'iDevices'
413        else:
414            sel_name = 'Back'
415        assert bui.app.classic is not None
416        bui.app.ui_v1.window_states[type(self)] = sel_name
417
418    def _restore_state(self) -> None:
419        assert bui.app.classic is not None
420        sel_name = bui.app.ui_v1.window_states.get(type(self))
421        if sel_name == 'GamePads':
422            sel = self._gamepads_button
423        elif sel_name == 'Touch':
424            sel = self._touch_button
425        elif sel_name == 'Keyboard':
426            sel = self._keyboard_button
427        elif sel_name == 'Keyboard2':
428            sel = self._keyboard_2_button
429        elif sel_name == 'iDevices':
430            sel = self._idevices_button
431        elif sel_name == 'Back':
432            sel = self._back_button
433        else:
434            sel = (
435                self._gamepads_button
436                if self._gamepads_button is not None
437                else self._back_button
438            )
439        bui.containerwidget(edit=self._root_widget, selected_child=sel)

Top level control settings window.

ControlsSettingsWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
 17    def __init__(
 18        self,
 19        transition: str | None = 'in_right',
 20        origin_widget: bui.Widget | None = None,
 21    ):
 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
 28        self._have_selected_child = False
 29
 30        self._r = 'configControllersWindow'
 31        uiscale = bui.app.ui_v1.uiscale
 32        app = bui.app
 33        assert app.classic is not None
 34
 35        spacing = 50.0
 36        button_width = 350.0
 37        width = 800.0 if uiscale is bui.UIScale.SMALL else 460.0
 38        height = 300 if uiscale is bui.UIScale.SMALL else 130.0
 39
 40        yoffs = -60 if uiscale is bui.UIScale.SMALL else 0
 41        space_height = spacing * 0.3
 42
 43        # FIXME: should create vis settings under platform or
 44        # app-adapter to determine whether to show this stuff; not hard
 45        # code it.
 46
 47        show_gamepads = False
 48        platform = app.classic.platform
 49        subplatform = app.classic.subplatform
 50        non_vr_windows = platform == 'windows' and (
 51            subplatform != 'oculus' or not app.env.vr
 52        )
 53        if platform in ('linux', 'android', 'mac') or non_vr_windows:
 54            show_gamepads = True
 55            height += spacing
 56
 57        show_touch = False
 58        if bs.have_touchscreen_input():
 59            show_touch = True
 60            height += spacing
 61
 62        show_space_1 = False
 63        if show_gamepads or show_touch:
 64            show_space_1 = True
 65            height += space_height
 66
 67        show_keyboard = False
 68        if bs.getinputdevice('Keyboard', '#1', doraise=False) is not None:
 69            show_keyboard = True
 70            height += spacing
 71        show_keyboard_p2 = False if app.env.vr else show_keyboard
 72        if show_keyboard_p2:
 73            height += spacing
 74
 75        show_space_2 = False
 76        if show_keyboard:
 77            show_space_2 = True
 78            height += space_height
 79
 80        if bool(True):
 81            show_remote = True
 82            height += spacing
 83        else:
 84            show_remote = False
 85
 86        # On windows (outside of oculus/vr), show an option to disable xinput.
 87        show_xinput_toggle = False
 88        if platform == 'windows' and not app.env.vr:
 89            show_xinput_toggle = True
 90
 91        if show_xinput_toggle:
 92            height += spacing
 93
 94        assert bui.app.classic is not None
 95        smallscale = 1.7
 96        super().__init__(
 97            root_widget=bui.containerwidget(
 98                size=(width, height),
 99                stack_offset=(
100                    (0, -10) if uiscale is bui.UIScale.SMALL else (0, 0)
101                ),
102                scale=(
103                    smallscale
104                    if uiscale is bui.UIScale.SMALL
105                    else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
106                ),
107                toolbar_visibility=(
108                    'menu_minimal'
109                    if uiscale is bui.UIScale.SMALL
110                    else 'menu_full'
111                ),
112            ),
113            transition=transition,
114            origin_widget=origin_widget,
115        )
116
117        self._back_button: bui.Widget | None
118        if uiscale is bui.UIScale.SMALL:
119            bui.containerwidget(
120                edit=self._root_widget, on_cancel_call=self.main_window_back
121            )
122            self._back_button = None
123        else:
124            self._back_button = btn = bui.buttonwidget(
125                parent=self._root_widget,
126                position=(35, height - 60),
127                size=(140, 65),
128                scale=0.8,
129                text_scale=1.2,
130                autoselect=True,
131                label=bui.Lstr(resource='backText'),
132                button_type='back',
133                on_activate_call=self.main_window_back,
134            )
135            bui.containerwidget(edit=self._root_widget, cancel_button=btn)
136            bui.buttonwidget(
137                edit=btn,
138                button_type='backSmall',
139                size=(60, 60),
140                label=bui.charstr(bui.SpecialChar.BACK),
141            )
142
143        # We need these vars to exist even if the buttons don't.
144        self._gamepads_button: bui.Widget | None = None
145        self._touch_button: bui.Widget | None = None
146        self._keyboard_button: bui.Widget | None = None
147        self._keyboard_2_button: bui.Widget | None = None
148        self._idevices_button: bui.Widget | None = None
149
150        bui.textwidget(
151            parent=self._root_widget,
152            position=(0, height - 49 + yoffs),
153            size=(width, 25),
154            text=bui.Lstr(resource=f'{self._r}.titleText'),
155            color=bui.app.ui_v1.title_color,
156            h_align='center',
157            v_align='top',
158        )
159
160        v = height - 75 + yoffs
161        v -= spacing
162
163        if show_touch:
164            self._touch_button = btn = bui.buttonwidget(
165                parent=self._root_widget,
166                position=((width - button_width) / 2, v),
167                size=(button_width, 43),
168                autoselect=True,
169                label=bui.Lstr(resource=f'{self._r}.configureTouchText'),
170                on_activate_call=self._do_touchscreen,
171            )
172            bui.widget(
173                edit=btn,
174                right_widget=bui.get_special_widget('squad_button'),
175            )
176            if not self._have_selected_child:
177                bui.containerwidget(
178                    edit=self._root_widget, selected_child=self._touch_button
179                )
180                if self._back_button is not None:
181                    bui.widget(
182                        edit=self._back_button, down_widget=self._touch_button
183                    )
184                self._have_selected_child = True
185            v -= spacing
186
187        if show_gamepads:
188            self._gamepads_button = btn = bui.buttonwidget(
189                parent=self._root_widget,
190                position=((width - button_width) / 2 - 7, v),
191                size=(button_width, 43),
192                autoselect=True,
193                label=bui.Lstr(resource=f'{self._r}.configureControllersText'),
194                on_activate_call=self._do_gamepads,
195            )
196            bui.widget(
197                edit=btn,
198                right_widget=bui.get_special_widget('squad_button'),
199            )
200            if not self._have_selected_child:
201                bui.containerwidget(
202                    edit=self._root_widget, selected_child=self._gamepads_button
203                )
204                if self._back_button is not None:
205                    bui.widget(
206                        edit=self._back_button,
207                        down_widget=self._gamepads_button,
208                    )
209                self._have_selected_child = True
210            v -= spacing
211        else:
212            self._gamepads_button = None
213
214        if show_space_1:
215            v -= space_height
216
217        if show_keyboard:
218            self._keyboard_button = btn = bui.buttonwidget(
219                parent=self._root_widget,
220                position=((width - button_width) / 2 - 5, v),
221                size=(button_width, 43),
222                autoselect=True,
223                label=bui.Lstr(resource=f'{self._r}.configureKeyboardText'),
224                on_activate_call=self._config_keyboard,
225            )
226            bui.widget(
227                edit=self._keyboard_button, left_widget=self._keyboard_button
228            )
229            bui.widget(
230                edit=btn,
231                right_widget=bui.get_special_widget('squad_button'),
232            )
233            if not self._have_selected_child:
234                bui.containerwidget(
235                    edit=self._root_widget, selected_child=self._keyboard_button
236                )
237                if self._back_button is not None:
238                    bui.widget(
239                        edit=self._back_button,
240                        down_widget=self._keyboard_button,
241                    )
242                self._have_selected_child = True
243            v -= spacing
244        if show_keyboard_p2:
245            self._keyboard_2_button = bui.buttonwidget(
246                parent=self._root_widget,
247                position=((width - button_width) / 2 - 3, v),
248                size=(button_width, 43),
249                autoselect=True,
250                label=bui.Lstr(resource=f'{self._r}.configureKeyboard2Text'),
251                on_activate_call=self._config_keyboard2,
252            )
253            v -= spacing
254            bui.widget(
255                edit=self._keyboard_2_button,
256                left_widget=self._keyboard_2_button,
257            )
258        if show_space_2:
259            v -= space_height
260        if show_remote:
261            self._idevices_button = btn = bui.buttonwidget(
262                parent=self._root_widget,
263                position=((width - button_width) / 2 - 5, v),
264                size=(button_width, 43),
265                autoselect=True,
266                label=bui.Lstr(resource=f'{self._r}.configureMobileText'),
267                on_activate_call=self._do_mobile_devices,
268            )
269            bui.widget(
270                edit=self._idevices_button, left_widget=self._idevices_button
271            )
272            bui.widget(
273                edit=btn,
274                right_widget=bui.get_special_widget('squad_button'),
275            )
276            if not self._have_selected_child:
277                bui.containerwidget(
278                    edit=self._root_widget, selected_child=self._idevices_button
279                )
280                if self._back_button is not None:
281                    bui.widget(
282                        edit=self._back_button,
283                        down_widget=self._idevices_button,
284                    )
285                self._have_selected_child = True
286            v -= spacing
287
288        if show_xinput_toggle:
289
290            def do_toggle(value: bool) -> None:
291                bui.screenmessage(
292                    bui.Lstr(resource='settingsWindowAdvanced.mustRestartText'),
293                    color=(1, 1, 0),
294                )
295                bui.getsound('gunCocking').play()
296                bui.set_low_level_config_value('enablexinput', not value)
297
298            xinput_checkbox = bui.checkboxwidget(
299                parent=self._root_widget,
300                position=(100, v + 3),
301                size=(120, 30),
302                value=(not bui.get_low_level_config_value('enablexinput', 1)),
303                maxwidth=200,
304                on_value_change_call=do_toggle,
305                text=bui.Lstr(resource='disableXInputText'),
306                autoselect=True,
307            )
308            bui.textwidget(
309                parent=self._root_widget,
310                position=(width * 0.5, v - 5),
311                size=(0, 0),
312                text=bui.Lstr(resource='disableXInputDescriptionText'),
313                scale=0.5,
314                h_align='center',
315                v_align='center',
316                color=bui.app.ui_v1.infotextcolor,
317                maxwidth=width * 0.8,
318            )
319            bui.widget(
320                edit=xinput_checkbox,
321                left_widget=xinput_checkbox,
322                right_widget=xinput_checkbox,
323            )
324            v -= spacing
325
326        self._restore_state()

Create a MainWindow given a root widget and transition info.

Automatically handles in and out transitions on the provided widget, so there is no need to set transitions when creating it.

@override
def get_main_window_state(self) -> bauiv1.MainWindowState:
328    @override
329    def get_main_window_state(self) -> bui.MainWindowState:
330        # Support recreating our window for back/refresh purposes.
331        cls = type(self)
332        return bui.BasicMainWindowState(
333            create_call=lambda transition, origin_widget: cls(
334                transition=transition, origin_widget=origin_widget
335            )
336        )

Return a WindowState to recreate this window, if supported.

@override
def on_main_window_close(self) -> None:
338    @override
339    def on_main_window_close(self) -> None:
340        self._save_state()

Called before transitioning out a main window.

A good opportunity to save window state/etc.

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