bauiv1lib.settings.gamepadadvanced

UI functionality related to advanced gamepad configuring.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""UI functionality related to advanced gamepad configuring."""
  4
  5from __future__ import annotations
  6
  7from typing import TYPE_CHECKING
  8
  9import bauiv1 as bui
 10
 11if TYPE_CHECKING:
 12    from typing import Any
 13    from bauiv1lib.settings.gamepad import (
 14        GamepadSettingsWindow,
 15        AwaitGamepadInputWindow,
 16    )
 17
 18
 19class GamepadAdvancedSettingsWindow(bui.Window):
 20    """Window for advanced gamepad configuration."""
 21
 22    def __init__(self, parent_window: GamepadSettingsWindow):
 23        # pylint: disable=too-many-statements
 24        # pylint: disable=too-many-locals
 25        self._parent_window = parent_window
 26
 27        app = bui.app
 28
 29        self._r = parent_window.get_r()
 30        assert bui.app.classic is not None
 31        uiscale = bui.app.ui_v1.uiscale
 32        self._width = 900 if uiscale is bui.UIScale.SMALL else 700
 33        self._x_inset = x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 34        self._height = 402 if uiscale is bui.UIScale.SMALL else 512
 35        self._textwidgets: dict[str, bui.Widget] = {}
 36        advb = parent_window.get_advanced_button()
 37        super().__init__(
 38            root_widget=bui.containerwidget(
 39                transition='in_scale',
 40                size=(self._width, self._height),
 41                scale=1.06
 42                * (
 43                    1.6
 44                    if uiscale is bui.UIScale.SMALL
 45                    else 1.35 if uiscale is bui.UIScale.MEDIUM else 1.0
 46                ),
 47                stack_offset=(
 48                    (0, -25) if uiscale is bui.UIScale.SMALL else (0, 0)
 49                ),
 50                scale_origin_stack_offset=(advb.get_screen_space_center()),
 51            )
 52        )
 53
 54        bui.textwidget(
 55            parent=self._root_widget,
 56            position=(
 57                self._width * 0.5,
 58                self._height - (40 if uiscale is bui.UIScale.SMALL else 34),
 59            ),
 60            size=(0, 0),
 61            text=bui.Lstr(resource=f'{self._r}.advancedTitleText'),
 62            maxwidth=320,
 63            color=bui.app.ui_v1.title_color,
 64            h_align='center',
 65            v_align='center',
 66        )
 67
 68        back_button = btn = bui.buttonwidget(
 69            parent=self._root_widget,
 70            autoselect=True,
 71            position=(
 72                self._width - (176 + x_inset),
 73                self._height - (60 if uiscale is bui.UIScale.SMALL else 55),
 74            ),
 75            size=(120, 48),
 76            text_scale=0.8,
 77            label=bui.Lstr(resource='doneText'),
 78            on_activate_call=self._done,
 79        )
 80        bui.containerwidget(
 81            edit=self._root_widget,
 82            start_button=btn,
 83            on_cancel_call=btn.activate,
 84        )
 85
 86        self._scroll_width = self._width - (100 + 2 * x_inset)
 87        self._scroll_height = self._height - 110
 88        self._sub_width = self._scroll_width - 20
 89        self._sub_height = (
 90            940 if self._parent_window.get_is_secondary() else 1040
 91        )
 92        if app.env.vr:
 93            self._sub_height += 50
 94        self._scrollwidget = bui.scrollwidget(
 95            parent=self._root_widget,
 96            position=(
 97                (self._width - self._scroll_width) * 0.5,
 98                self._height - 65 - self._scroll_height,
 99            ),
100            size=(self._scroll_width, self._scroll_height),
101            claims_left_right=True,
102            claims_tab=True,
103            selection_loops_to_parent=True,
104        )
105        self._subcontainer = bui.containerwidget(
106            parent=self._scrollwidget,
107            size=(self._sub_width, self._sub_height),
108            background=False,
109            claims_left_right=True,
110            claims_tab=True,
111            selection_loops_to_parent=True,
112        )
113        bui.containerwidget(
114            edit=self._root_widget, selected_child=self._scrollwidget
115        )
116
117        h = 30
118        v = self._sub_height - 10
119
120        h2 = h + 12
121
122        # don't allow secondary joysticks to handle unassigned buttons
123        if not self._parent_window.get_is_secondary():
124            v -= 40
125            cb1 = bui.checkboxwidget(
126                parent=self._subcontainer,
127                position=(h + 70, v),
128                size=(500, 30),
129                text=bui.Lstr(resource=f'{self._r}.unassignedButtonsRunText'),
130                textcolor=(0.8, 0.8, 0.8),
131                maxwidth=330,
132                scale=1.0,
133                on_value_change_call=(
134                    self._parent_window.set_unassigned_buttons_run_value
135                ),
136                autoselect=True,
137                value=self._parent_window.get_unassigned_buttons_run_value(),
138            )
139            bui.widget(edit=cb1, up_widget=back_button)
140        v -= 60
141        capb = self._capture_button(
142            pos=(h2, v),
143            name=bui.Lstr(resource=f'{self._r}.runButton1Text'),
144            control='buttonRun1' + self._parent_window.get_ext(),
145        )
146        if self._parent_window.get_is_secondary():
147            for widget in capb:
148                bui.widget(edit=widget, up_widget=back_button)
149        v -= 42
150        self._capture_button(
151            pos=(h2, v),
152            name=bui.Lstr(resource=f'{self._r}.runButton2Text'),
153            control='buttonRun2' + self._parent_window.get_ext(),
154        )
155        bui.textwidget(
156            parent=self._subcontainer,
157            position=(self._sub_width * 0.5, v - 24),
158            size=(0, 0),
159            text=bui.Lstr(resource=f'{self._r}.runTriggerDescriptionText'),
160            color=(0.7, 1, 0.7, 0.6),
161            maxwidth=self._sub_width * 0.8,
162            scale=0.7,
163            h_align='center',
164            v_align='center',
165        )
166
167        v -= 85
168
169        self._capture_button(
170            pos=(h2, v),
171            name=bui.Lstr(resource=f'{self._r}.runTrigger1Text'),
172            control='triggerRun1' + self._parent_window.get_ext(),
173            message=bui.Lstr(resource=f'{self._r}.pressAnyAnalogTriggerText'),
174        )
175        v -= 42
176        self._capture_button(
177            pos=(h2, v),
178            name=bui.Lstr(resource=f'{self._r}.runTrigger2Text'),
179            control='triggerRun2' + self._parent_window.get_ext(),
180            message=bui.Lstr(resource=f'{self._r}.pressAnyAnalogTriggerText'),
181        )
182
183        # in vr mode, allow assigning a reset-view button
184        if app.env.vr:
185            v -= 50
186            self._capture_button(
187                pos=(h2, v),
188                name=bui.Lstr(resource=f'{self._r}.vrReorientButtonText'),
189                control='buttonVRReorient' + self._parent_window.get_ext(),
190            )
191
192        v -= 60
193        self._capture_button(
194            pos=(h2, v),
195            name=bui.Lstr(resource=f'{self._r}.extraStartButtonText'),
196            control='buttonStart2' + self._parent_window.get_ext(),
197        )
198        v -= 60
199        self._capture_button(
200            pos=(h2, v),
201            name=bui.Lstr(resource=f'{self._r}.ignoredButton1Text'),
202            control='buttonIgnored' + self._parent_window.get_ext(),
203        )
204        v -= 42
205        self._capture_button(
206            pos=(h2, v),
207            name=bui.Lstr(resource=f'{self._r}.ignoredButton2Text'),
208            control='buttonIgnored2' + self._parent_window.get_ext(),
209        )
210        v -= 42
211        self._capture_button(
212            pos=(h2, v),
213            name=bui.Lstr(resource=f'{self._r}.ignoredButton3Text'),
214            control='buttonIgnored3' + self._parent_window.get_ext(),
215        )
216        v -= 42
217        self._capture_button(
218            pos=(h2, v),
219            name=bui.Lstr(resource=f'{self._r}.ignoredButton4Text'),
220            control='buttonIgnored4' + self._parent_window.get_ext(),
221        )
222        bui.textwidget(
223            parent=self._subcontainer,
224            position=(self._sub_width * 0.5, v - 14),
225            size=(0, 0),
226            text=bui.Lstr(resource=f'{self._r}.ignoredButtonDescriptionText'),
227            color=(0.7, 1, 0.7, 0.6),
228            scale=0.8,
229            maxwidth=self._sub_width * 0.8,
230            h_align='center',
231            v_align='center',
232        )
233
234        v -= 80
235        pwin = self._parent_window
236        bui.checkboxwidget(
237            parent=self._subcontainer,
238            autoselect=True,
239            position=(h + 50, v),
240            size=(400, 30),
241            text=bui.Lstr(
242                resource=f'{self._r}.startButtonActivatesDefaultText'
243            ),
244            textcolor=(0.8, 0.8, 0.8),
245            maxwidth=450,
246            scale=0.9,
247            on_value_change_call=(
248                pwin.set_start_button_activates_default_widget_value
249            ),
250            value=pwin.get_start_button_activates_default_widget_value(),
251        )
252        bui.textwidget(
253            parent=self._subcontainer,
254            position=(self._sub_width * 0.5, v - 12),
255            size=(0, 0),
256            text=bui.Lstr(
257                resource=f'{self._r}.startButtonActivatesDefaultDescriptionText'
258            ),
259            color=(0.7, 1, 0.7, 0.6),
260            maxwidth=self._sub_width * 0.8,
261            scale=0.7,
262            h_align='center',
263            v_align='center',
264        )
265
266        v -= 80
267        bui.checkboxwidget(
268            parent=self._subcontainer,
269            autoselect=True,
270            position=(h + 50, v),
271            size=(400, 30),
272            text=bui.Lstr(resource=f'{self._r}.uiOnlyText'),
273            textcolor=(0.8, 0.8, 0.8),
274            maxwidth=450,
275            scale=0.9,
276            on_value_change_call=self._parent_window.set_ui_only_value,
277            value=self._parent_window.get_ui_only_value(),
278        )
279        bui.textwidget(
280            parent=self._subcontainer,
281            position=(self._sub_width * 0.5, v - 12),
282            size=(0, 0),
283            text=bui.Lstr(resource=f'{self._r}.uiOnlyDescriptionText'),
284            color=(0.7, 1, 0.7, 0.6),
285            maxwidth=self._sub_width * 0.8,
286            scale=0.7,
287            h_align='center',
288            v_align='center',
289        )
290
291        v -= 80
292        bui.checkboxwidget(
293            parent=self._subcontainer,
294            autoselect=True,
295            position=(h + 50, v),
296            size=(400, 30),
297            text=bui.Lstr(resource=f'{self._r}.ignoreCompletelyText'),
298            textcolor=(0.8, 0.8, 0.8),
299            maxwidth=450,
300            scale=0.9,
301            on_value_change_call=pwin.set_ignore_completely_value,
302            value=self._parent_window.get_ignore_completely_value(),
303        )
304        bui.textwidget(
305            parent=self._subcontainer,
306            position=(self._sub_width * 0.5, v - 12),
307            size=(0, 0),
308            text=bui.Lstr(
309                resource=f'{self._r}.ignoreCompletelyDescriptionText'
310            ),
311            color=(0.7, 1, 0.7, 0.6),
312            maxwidth=self._sub_width * 0.8,
313            scale=0.7,
314            h_align='center',
315            v_align='center',
316        )
317
318        v -= 80
319
320        cb1 = bui.checkboxwidget(
321            parent=self._subcontainer,
322            autoselect=True,
323            position=(h + 50, v),
324            size=(400, 30),
325            text=bui.Lstr(resource=f'{self._r}.autoRecalibrateText'),
326            textcolor=(0.8, 0.8, 0.8),
327            maxwidth=450,
328            scale=0.9,
329            on_value_change_call=pwin.set_auto_recalibrate_analog_stick_value,
330            value=self._parent_window.get_auto_recalibrate_analog_stick_value(),
331        )
332        bui.textwidget(
333            parent=self._subcontainer,
334            position=(self._sub_width * 0.5, v - 12),
335            size=(0, 0),
336            text=bui.Lstr(resource=f'{self._r}.autoRecalibrateDescriptionText'),
337            color=(0.7, 1, 0.7, 0.6),
338            maxwidth=self._sub_width * 0.8,
339            scale=0.7,
340            h_align='center',
341            v_align='center',
342        )
343        v -= 80
344
345        buttons = self._config_value_editor(
346            bui.Lstr(resource=f'{self._r}.analogStickDeadZoneText'),
347            control=('analogStickDeadZone' + self._parent_window.get_ext()),
348            position=(h + 40, v),
349            min_val=0,
350            max_val=10.0,
351            increment=0.1,
352            x_offset=100,
353        )
354        bui.widget(edit=buttons[0], left_widget=cb1, up_widget=cb1)
355        bui.widget(edit=cb1, right_widget=buttons[0], down_widget=buttons[0])
356
357        bui.textwidget(
358            parent=self._subcontainer,
359            position=(self._sub_width * 0.5, v - 12),
360            size=(0, 0),
361            text=bui.Lstr(
362                resource=f'{self._r}.analogStickDeadZoneDescriptionText'
363            ),
364            color=(0.7, 1, 0.7, 0.6),
365            maxwidth=self._sub_width * 0.8,
366            scale=0.7,
367            h_align='center',
368            v_align='center',
369        )
370        v -= 100
371
372        # child joysticks cant have child joysticks.. that's just
373        # crazy talk
374        if not self._parent_window.get_is_secondary():
375            bui.buttonwidget(
376                parent=self._subcontainer,
377                autoselect=True,
378                label=bui.Lstr(resource=f'{self._r}.twoInOneSetupText'),
379                position=(40, v),
380                size=(self._sub_width - 80, 50),
381                on_activate_call=self._parent_window.show_secondary_editor,
382                up_widget=buttons[0],
383            )
384
385        # set a bigger bottom show-buffer for the widgets we just made
386        # so we can see the text below them when navigating with
387        # a gamepad
388        for child in self._subcontainer.get_children():
389            bui.widget(edit=child, show_buffer_bottom=30, show_buffer_top=30)
390
391    def _capture_button(
392        self,
393        pos: tuple[float, float],
394        name: bui.Lstr,
395        control: str,
396        message: bui.Lstr | None = None,
397    ) -> tuple[bui.Widget, bui.Widget]:
398        if message is None:
399            message = bui.Lstr(
400                resource=self._parent_window.get_r() + '.pressAnyButtonText'
401            )
402        btn = bui.buttonwidget(
403            parent=self._subcontainer,
404            autoselect=True,
405            position=(pos[0], pos[1]),
406            label=name,
407            size=(250, 60),
408            scale=0.7,
409        )
410        btn2 = bui.buttonwidget(
411            parent=self._subcontainer,
412            autoselect=True,
413            position=(pos[0] + 400, pos[1] + 2),
414            left_widget=btn,
415            color=(0.45, 0.4, 0.5),
416            textcolor=(0.65, 0.6, 0.7),
417            label=bui.Lstr(resource=f'{self._r}.clearText'),
418            size=(110, 50),
419            scale=0.7,
420            on_activate_call=bui.Call(self._clear_control, control),
421        )
422        bui.widget(edit=btn, right_widget=btn2)
423
424        # make this in a timer so that it shows up on top of all
425        # other buttons
426
427        def doit() -> None:
428            from bauiv1lib.settings.gamepad import AwaitGamepadInputWindow
429
430            txt = bui.textwidget(
431                parent=self._subcontainer,
432                position=(pos[0] + 285, pos[1] + 20),
433                color=(1, 1, 1, 0.3),
434                size=(0, 0),
435                h_align='center',
436                v_align='center',
437                scale=0.7,
438                text=self._parent_window.get_control_value_name(control),
439                maxwidth=200,
440            )
441            self._textwidgets[control] = txt
442            bui.buttonwidget(
443                edit=btn,
444                on_activate_call=bui.Call(
445                    AwaitGamepadInputWindow,
446                    self._parent_window.get_input(),
447                    control,
448                    self._gamepad_event,
449                    message,
450                ),
451            )
452
453        bui.pushcall(doit)
454        return btn, btn2
455
456    def _inc(
457        self, control: str, min_val: float, max_val: float, inc: float
458    ) -> None:
459        val = self._parent_window.get_settings().get(control, 1.0)
460        val = min(max_val, max(min_val, val + inc))
461        if abs(1.0 - val) < 0.001:
462            if control in self._parent_window.get_settings():
463                del self._parent_window.get_settings()[control]
464        else:
465            self._parent_window.get_settings()[control] = round(val, 1)
466        bui.textwidget(
467            edit=self._textwidgets[control],
468            text=self._parent_window.get_control_value_name(control),
469        )
470
471    def _config_value_editor(
472        self,
473        name: bui.Lstr,
474        control: str,
475        position: tuple[float, float],
476        *,
477        min_val: float = 0.0,
478        max_val: float = 100.0,
479        increment: float = 1.0,
480        change_sound: bool = True,
481        x_offset: float = 0.0,
482        displayname: bui.Lstr | None = None,
483    ) -> tuple[bui.Widget, bui.Widget]:
484        if displayname is None:
485            displayname = name
486        bui.textwidget(
487            parent=self._subcontainer,
488            position=position,
489            size=(100, 30),
490            text=displayname,
491            color=(0.8, 0.8, 0.8, 1.0),
492            h_align='left',
493            v_align='center',
494            scale=1.0,
495            maxwidth=280,
496        )
497        self._textwidgets[control] = bui.textwidget(
498            parent=self._subcontainer,
499            position=(246.0 + x_offset, position[1]),
500            size=(60, 28),
501            editable=False,
502            color=(0.3, 1.0, 0.3, 1.0),
503            h_align='right',
504            v_align='center',
505            text=self._parent_window.get_control_value_name(control),
506            padding=2,
507        )
508        btn = bui.buttonwidget(
509            parent=self._subcontainer,
510            autoselect=True,
511            position=(330 + x_offset, position[1] + 4),
512            size=(28, 28),
513            label='-',
514            on_activate_call=bui.Call(
515                self._inc, control, min_val, max_val, -increment
516            ),
517            repeat=True,
518            enable_sound=(change_sound is True),
519        )
520        btn2 = bui.buttonwidget(
521            parent=self._subcontainer,
522            autoselect=True,
523            position=(380 + x_offset, position[1] + 4),
524            size=(28, 28),
525            label='+',
526            on_activate_call=bui.Call(
527                self._inc, control, min_val, max_val, increment
528            ),
529            repeat=True,
530            enable_sound=(change_sound is True),
531        )
532        return btn, btn2
533
534    def _clear_control(self, control: str) -> None:
535        if control in self._parent_window.get_settings():
536            del self._parent_window.get_settings()[control]
537        bui.textwidget(
538            edit=self._textwidgets[control],
539            text=self._parent_window.get_control_value_name(control),
540        )
541
542    def _gamepad_event(
543        self,
544        control: str,
545        event: dict[str, Any],
546        dialog: AwaitGamepadInputWindow,
547    ) -> None:
548        ext = self._parent_window.get_ext()
549        if control in ['triggerRun1' + ext, 'triggerRun2' + ext]:
550            if event['type'] == 'AXISMOTION':
551                # ignore small values or else we might get triggered
552                # by noise
553                if abs(event['value']) > 0.5:
554                    self._parent_window.get_settings()[control] = event['axis']
555                    # update the button's text widget
556                    if self._textwidgets[control]:
557                        bui.textwidget(
558                            edit=self._textwidgets[control],
559                            text=self._parent_window.get_control_value_name(
560                                control
561                            ),
562                        )
563                    bui.getsound('gunCocking').play()
564                    dialog.die()
565        else:
566            if event['type'] == 'BUTTONDOWN':
567                value = event['button']
568                self._parent_window.get_settings()[control] = value
569                # update the button's text widget
570                if self._textwidgets[control]:
571                    bui.textwidget(
572                        edit=self._textwidgets[control],
573                        text=self._parent_window.get_control_value_name(
574                            control
575                        ),
576                    )
577                bui.getsound('gunCocking').play()
578                dialog.die()
579
580    def _done(self) -> None:
581        bui.containerwidget(edit=self._root_widget, transition='out_scale')
class GamepadAdvancedSettingsWindow(bauiv1._uitypes.Window):
 20class GamepadAdvancedSettingsWindow(bui.Window):
 21    """Window for advanced gamepad configuration."""
 22
 23    def __init__(self, parent_window: GamepadSettingsWindow):
 24        # pylint: disable=too-many-statements
 25        # pylint: disable=too-many-locals
 26        self._parent_window = parent_window
 27
 28        app = bui.app
 29
 30        self._r = parent_window.get_r()
 31        assert bui.app.classic is not None
 32        uiscale = bui.app.ui_v1.uiscale
 33        self._width = 900 if uiscale is bui.UIScale.SMALL else 700
 34        self._x_inset = x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 35        self._height = 402 if uiscale is bui.UIScale.SMALL else 512
 36        self._textwidgets: dict[str, bui.Widget] = {}
 37        advb = parent_window.get_advanced_button()
 38        super().__init__(
 39            root_widget=bui.containerwidget(
 40                transition='in_scale',
 41                size=(self._width, self._height),
 42                scale=1.06
 43                * (
 44                    1.6
 45                    if uiscale is bui.UIScale.SMALL
 46                    else 1.35 if uiscale is bui.UIScale.MEDIUM else 1.0
 47                ),
 48                stack_offset=(
 49                    (0, -25) if uiscale is bui.UIScale.SMALL else (0, 0)
 50                ),
 51                scale_origin_stack_offset=(advb.get_screen_space_center()),
 52            )
 53        )
 54
 55        bui.textwidget(
 56            parent=self._root_widget,
 57            position=(
 58                self._width * 0.5,
 59                self._height - (40 if uiscale is bui.UIScale.SMALL else 34),
 60            ),
 61            size=(0, 0),
 62            text=bui.Lstr(resource=f'{self._r}.advancedTitleText'),
 63            maxwidth=320,
 64            color=bui.app.ui_v1.title_color,
 65            h_align='center',
 66            v_align='center',
 67        )
 68
 69        back_button = btn = bui.buttonwidget(
 70            parent=self._root_widget,
 71            autoselect=True,
 72            position=(
 73                self._width - (176 + x_inset),
 74                self._height - (60 if uiscale is bui.UIScale.SMALL else 55),
 75            ),
 76            size=(120, 48),
 77            text_scale=0.8,
 78            label=bui.Lstr(resource='doneText'),
 79            on_activate_call=self._done,
 80        )
 81        bui.containerwidget(
 82            edit=self._root_widget,
 83            start_button=btn,
 84            on_cancel_call=btn.activate,
 85        )
 86
 87        self._scroll_width = self._width - (100 + 2 * x_inset)
 88        self._scroll_height = self._height - 110
 89        self._sub_width = self._scroll_width - 20
 90        self._sub_height = (
 91            940 if self._parent_window.get_is_secondary() else 1040
 92        )
 93        if app.env.vr:
 94            self._sub_height += 50
 95        self._scrollwidget = bui.scrollwidget(
 96            parent=self._root_widget,
 97            position=(
 98                (self._width - self._scroll_width) * 0.5,
 99                self._height - 65 - self._scroll_height,
100            ),
101            size=(self._scroll_width, self._scroll_height),
102            claims_left_right=True,
103            claims_tab=True,
104            selection_loops_to_parent=True,
105        )
106        self._subcontainer = bui.containerwidget(
107            parent=self._scrollwidget,
108            size=(self._sub_width, self._sub_height),
109            background=False,
110            claims_left_right=True,
111            claims_tab=True,
112            selection_loops_to_parent=True,
113        )
114        bui.containerwidget(
115            edit=self._root_widget, selected_child=self._scrollwidget
116        )
117
118        h = 30
119        v = self._sub_height - 10
120
121        h2 = h + 12
122
123        # don't allow secondary joysticks to handle unassigned buttons
124        if not self._parent_window.get_is_secondary():
125            v -= 40
126            cb1 = bui.checkboxwidget(
127                parent=self._subcontainer,
128                position=(h + 70, v),
129                size=(500, 30),
130                text=bui.Lstr(resource=f'{self._r}.unassignedButtonsRunText'),
131                textcolor=(0.8, 0.8, 0.8),
132                maxwidth=330,
133                scale=1.0,
134                on_value_change_call=(
135                    self._parent_window.set_unassigned_buttons_run_value
136                ),
137                autoselect=True,
138                value=self._parent_window.get_unassigned_buttons_run_value(),
139            )
140            bui.widget(edit=cb1, up_widget=back_button)
141        v -= 60
142        capb = self._capture_button(
143            pos=(h2, v),
144            name=bui.Lstr(resource=f'{self._r}.runButton1Text'),
145            control='buttonRun1' + self._parent_window.get_ext(),
146        )
147        if self._parent_window.get_is_secondary():
148            for widget in capb:
149                bui.widget(edit=widget, up_widget=back_button)
150        v -= 42
151        self._capture_button(
152            pos=(h2, v),
153            name=bui.Lstr(resource=f'{self._r}.runButton2Text'),
154            control='buttonRun2' + self._parent_window.get_ext(),
155        )
156        bui.textwidget(
157            parent=self._subcontainer,
158            position=(self._sub_width * 0.5, v - 24),
159            size=(0, 0),
160            text=bui.Lstr(resource=f'{self._r}.runTriggerDescriptionText'),
161            color=(0.7, 1, 0.7, 0.6),
162            maxwidth=self._sub_width * 0.8,
163            scale=0.7,
164            h_align='center',
165            v_align='center',
166        )
167
168        v -= 85
169
170        self._capture_button(
171            pos=(h2, v),
172            name=bui.Lstr(resource=f'{self._r}.runTrigger1Text'),
173            control='triggerRun1' + self._parent_window.get_ext(),
174            message=bui.Lstr(resource=f'{self._r}.pressAnyAnalogTriggerText'),
175        )
176        v -= 42
177        self._capture_button(
178            pos=(h2, v),
179            name=bui.Lstr(resource=f'{self._r}.runTrigger2Text'),
180            control='triggerRun2' + self._parent_window.get_ext(),
181            message=bui.Lstr(resource=f'{self._r}.pressAnyAnalogTriggerText'),
182        )
183
184        # in vr mode, allow assigning a reset-view button
185        if app.env.vr:
186            v -= 50
187            self._capture_button(
188                pos=(h2, v),
189                name=bui.Lstr(resource=f'{self._r}.vrReorientButtonText'),
190                control='buttonVRReorient' + self._parent_window.get_ext(),
191            )
192
193        v -= 60
194        self._capture_button(
195            pos=(h2, v),
196            name=bui.Lstr(resource=f'{self._r}.extraStartButtonText'),
197            control='buttonStart2' + self._parent_window.get_ext(),
198        )
199        v -= 60
200        self._capture_button(
201            pos=(h2, v),
202            name=bui.Lstr(resource=f'{self._r}.ignoredButton1Text'),
203            control='buttonIgnored' + self._parent_window.get_ext(),
204        )
205        v -= 42
206        self._capture_button(
207            pos=(h2, v),
208            name=bui.Lstr(resource=f'{self._r}.ignoredButton2Text'),
209            control='buttonIgnored2' + self._parent_window.get_ext(),
210        )
211        v -= 42
212        self._capture_button(
213            pos=(h2, v),
214            name=bui.Lstr(resource=f'{self._r}.ignoredButton3Text'),
215            control='buttonIgnored3' + self._parent_window.get_ext(),
216        )
217        v -= 42
218        self._capture_button(
219            pos=(h2, v),
220            name=bui.Lstr(resource=f'{self._r}.ignoredButton4Text'),
221            control='buttonIgnored4' + self._parent_window.get_ext(),
222        )
223        bui.textwidget(
224            parent=self._subcontainer,
225            position=(self._sub_width * 0.5, v - 14),
226            size=(0, 0),
227            text=bui.Lstr(resource=f'{self._r}.ignoredButtonDescriptionText'),
228            color=(0.7, 1, 0.7, 0.6),
229            scale=0.8,
230            maxwidth=self._sub_width * 0.8,
231            h_align='center',
232            v_align='center',
233        )
234
235        v -= 80
236        pwin = self._parent_window
237        bui.checkboxwidget(
238            parent=self._subcontainer,
239            autoselect=True,
240            position=(h + 50, v),
241            size=(400, 30),
242            text=bui.Lstr(
243                resource=f'{self._r}.startButtonActivatesDefaultText'
244            ),
245            textcolor=(0.8, 0.8, 0.8),
246            maxwidth=450,
247            scale=0.9,
248            on_value_change_call=(
249                pwin.set_start_button_activates_default_widget_value
250            ),
251            value=pwin.get_start_button_activates_default_widget_value(),
252        )
253        bui.textwidget(
254            parent=self._subcontainer,
255            position=(self._sub_width * 0.5, v - 12),
256            size=(0, 0),
257            text=bui.Lstr(
258                resource=f'{self._r}.startButtonActivatesDefaultDescriptionText'
259            ),
260            color=(0.7, 1, 0.7, 0.6),
261            maxwidth=self._sub_width * 0.8,
262            scale=0.7,
263            h_align='center',
264            v_align='center',
265        )
266
267        v -= 80
268        bui.checkboxwidget(
269            parent=self._subcontainer,
270            autoselect=True,
271            position=(h + 50, v),
272            size=(400, 30),
273            text=bui.Lstr(resource=f'{self._r}.uiOnlyText'),
274            textcolor=(0.8, 0.8, 0.8),
275            maxwidth=450,
276            scale=0.9,
277            on_value_change_call=self._parent_window.set_ui_only_value,
278            value=self._parent_window.get_ui_only_value(),
279        )
280        bui.textwidget(
281            parent=self._subcontainer,
282            position=(self._sub_width * 0.5, v - 12),
283            size=(0, 0),
284            text=bui.Lstr(resource=f'{self._r}.uiOnlyDescriptionText'),
285            color=(0.7, 1, 0.7, 0.6),
286            maxwidth=self._sub_width * 0.8,
287            scale=0.7,
288            h_align='center',
289            v_align='center',
290        )
291
292        v -= 80
293        bui.checkboxwidget(
294            parent=self._subcontainer,
295            autoselect=True,
296            position=(h + 50, v),
297            size=(400, 30),
298            text=bui.Lstr(resource=f'{self._r}.ignoreCompletelyText'),
299            textcolor=(0.8, 0.8, 0.8),
300            maxwidth=450,
301            scale=0.9,
302            on_value_change_call=pwin.set_ignore_completely_value,
303            value=self._parent_window.get_ignore_completely_value(),
304        )
305        bui.textwidget(
306            parent=self._subcontainer,
307            position=(self._sub_width * 0.5, v - 12),
308            size=(0, 0),
309            text=bui.Lstr(
310                resource=f'{self._r}.ignoreCompletelyDescriptionText'
311            ),
312            color=(0.7, 1, 0.7, 0.6),
313            maxwidth=self._sub_width * 0.8,
314            scale=0.7,
315            h_align='center',
316            v_align='center',
317        )
318
319        v -= 80
320
321        cb1 = bui.checkboxwidget(
322            parent=self._subcontainer,
323            autoselect=True,
324            position=(h + 50, v),
325            size=(400, 30),
326            text=bui.Lstr(resource=f'{self._r}.autoRecalibrateText'),
327            textcolor=(0.8, 0.8, 0.8),
328            maxwidth=450,
329            scale=0.9,
330            on_value_change_call=pwin.set_auto_recalibrate_analog_stick_value,
331            value=self._parent_window.get_auto_recalibrate_analog_stick_value(),
332        )
333        bui.textwidget(
334            parent=self._subcontainer,
335            position=(self._sub_width * 0.5, v - 12),
336            size=(0, 0),
337            text=bui.Lstr(resource=f'{self._r}.autoRecalibrateDescriptionText'),
338            color=(0.7, 1, 0.7, 0.6),
339            maxwidth=self._sub_width * 0.8,
340            scale=0.7,
341            h_align='center',
342            v_align='center',
343        )
344        v -= 80
345
346        buttons = self._config_value_editor(
347            bui.Lstr(resource=f'{self._r}.analogStickDeadZoneText'),
348            control=('analogStickDeadZone' + self._parent_window.get_ext()),
349            position=(h + 40, v),
350            min_val=0,
351            max_val=10.0,
352            increment=0.1,
353            x_offset=100,
354        )
355        bui.widget(edit=buttons[0], left_widget=cb1, up_widget=cb1)
356        bui.widget(edit=cb1, right_widget=buttons[0], down_widget=buttons[0])
357
358        bui.textwidget(
359            parent=self._subcontainer,
360            position=(self._sub_width * 0.5, v - 12),
361            size=(0, 0),
362            text=bui.Lstr(
363                resource=f'{self._r}.analogStickDeadZoneDescriptionText'
364            ),
365            color=(0.7, 1, 0.7, 0.6),
366            maxwidth=self._sub_width * 0.8,
367            scale=0.7,
368            h_align='center',
369            v_align='center',
370        )
371        v -= 100
372
373        # child joysticks cant have child joysticks.. that's just
374        # crazy talk
375        if not self._parent_window.get_is_secondary():
376            bui.buttonwidget(
377                parent=self._subcontainer,
378                autoselect=True,
379                label=bui.Lstr(resource=f'{self._r}.twoInOneSetupText'),
380                position=(40, v),
381                size=(self._sub_width - 80, 50),
382                on_activate_call=self._parent_window.show_secondary_editor,
383                up_widget=buttons[0],
384            )
385
386        # set a bigger bottom show-buffer for the widgets we just made
387        # so we can see the text below them when navigating with
388        # a gamepad
389        for child in self._subcontainer.get_children():
390            bui.widget(edit=child, show_buffer_bottom=30, show_buffer_top=30)
391
392    def _capture_button(
393        self,
394        pos: tuple[float, float],
395        name: bui.Lstr,
396        control: str,
397        message: bui.Lstr | None = None,
398    ) -> tuple[bui.Widget, bui.Widget]:
399        if message is None:
400            message = bui.Lstr(
401                resource=self._parent_window.get_r() + '.pressAnyButtonText'
402            )
403        btn = bui.buttonwidget(
404            parent=self._subcontainer,
405            autoselect=True,
406            position=(pos[0], pos[1]),
407            label=name,
408            size=(250, 60),
409            scale=0.7,
410        )
411        btn2 = bui.buttonwidget(
412            parent=self._subcontainer,
413            autoselect=True,
414            position=(pos[0] + 400, pos[1] + 2),
415            left_widget=btn,
416            color=(0.45, 0.4, 0.5),
417            textcolor=(0.65, 0.6, 0.7),
418            label=bui.Lstr(resource=f'{self._r}.clearText'),
419            size=(110, 50),
420            scale=0.7,
421            on_activate_call=bui.Call(self._clear_control, control),
422        )
423        bui.widget(edit=btn, right_widget=btn2)
424
425        # make this in a timer so that it shows up on top of all
426        # other buttons
427
428        def doit() -> None:
429            from bauiv1lib.settings.gamepad import AwaitGamepadInputWindow
430
431            txt = bui.textwidget(
432                parent=self._subcontainer,
433                position=(pos[0] + 285, pos[1] + 20),
434                color=(1, 1, 1, 0.3),
435                size=(0, 0),
436                h_align='center',
437                v_align='center',
438                scale=0.7,
439                text=self._parent_window.get_control_value_name(control),
440                maxwidth=200,
441            )
442            self._textwidgets[control] = txt
443            bui.buttonwidget(
444                edit=btn,
445                on_activate_call=bui.Call(
446                    AwaitGamepadInputWindow,
447                    self._parent_window.get_input(),
448                    control,
449                    self._gamepad_event,
450                    message,
451                ),
452            )
453
454        bui.pushcall(doit)
455        return btn, btn2
456
457    def _inc(
458        self, control: str, min_val: float, max_val: float, inc: float
459    ) -> None:
460        val = self._parent_window.get_settings().get(control, 1.0)
461        val = min(max_val, max(min_val, val + inc))
462        if abs(1.0 - val) < 0.001:
463            if control in self._parent_window.get_settings():
464                del self._parent_window.get_settings()[control]
465        else:
466            self._parent_window.get_settings()[control] = round(val, 1)
467        bui.textwidget(
468            edit=self._textwidgets[control],
469            text=self._parent_window.get_control_value_name(control),
470        )
471
472    def _config_value_editor(
473        self,
474        name: bui.Lstr,
475        control: str,
476        position: tuple[float, float],
477        *,
478        min_val: float = 0.0,
479        max_val: float = 100.0,
480        increment: float = 1.0,
481        change_sound: bool = True,
482        x_offset: float = 0.0,
483        displayname: bui.Lstr | None = None,
484    ) -> tuple[bui.Widget, bui.Widget]:
485        if displayname is None:
486            displayname = name
487        bui.textwidget(
488            parent=self._subcontainer,
489            position=position,
490            size=(100, 30),
491            text=displayname,
492            color=(0.8, 0.8, 0.8, 1.0),
493            h_align='left',
494            v_align='center',
495            scale=1.0,
496            maxwidth=280,
497        )
498        self._textwidgets[control] = bui.textwidget(
499            parent=self._subcontainer,
500            position=(246.0 + x_offset, position[1]),
501            size=(60, 28),
502            editable=False,
503            color=(0.3, 1.0, 0.3, 1.0),
504            h_align='right',
505            v_align='center',
506            text=self._parent_window.get_control_value_name(control),
507            padding=2,
508        )
509        btn = bui.buttonwidget(
510            parent=self._subcontainer,
511            autoselect=True,
512            position=(330 + x_offset, position[1] + 4),
513            size=(28, 28),
514            label='-',
515            on_activate_call=bui.Call(
516                self._inc, control, min_val, max_val, -increment
517            ),
518            repeat=True,
519            enable_sound=(change_sound is True),
520        )
521        btn2 = bui.buttonwidget(
522            parent=self._subcontainer,
523            autoselect=True,
524            position=(380 + x_offset, position[1] + 4),
525            size=(28, 28),
526            label='+',
527            on_activate_call=bui.Call(
528                self._inc, control, min_val, max_val, increment
529            ),
530            repeat=True,
531            enable_sound=(change_sound is True),
532        )
533        return btn, btn2
534
535    def _clear_control(self, control: str) -> None:
536        if control in self._parent_window.get_settings():
537            del self._parent_window.get_settings()[control]
538        bui.textwidget(
539            edit=self._textwidgets[control],
540            text=self._parent_window.get_control_value_name(control),
541        )
542
543    def _gamepad_event(
544        self,
545        control: str,
546        event: dict[str, Any],
547        dialog: AwaitGamepadInputWindow,
548    ) -> None:
549        ext = self._parent_window.get_ext()
550        if control in ['triggerRun1' + ext, 'triggerRun2' + ext]:
551            if event['type'] == 'AXISMOTION':
552                # ignore small values or else we might get triggered
553                # by noise
554                if abs(event['value']) > 0.5:
555                    self._parent_window.get_settings()[control] = event['axis']
556                    # update the button's text widget
557                    if self._textwidgets[control]:
558                        bui.textwidget(
559                            edit=self._textwidgets[control],
560                            text=self._parent_window.get_control_value_name(
561                                control
562                            ),
563                        )
564                    bui.getsound('gunCocking').play()
565                    dialog.die()
566        else:
567            if event['type'] == 'BUTTONDOWN':
568                value = event['button']
569                self._parent_window.get_settings()[control] = value
570                # update the button's text widget
571                if self._textwidgets[control]:
572                    bui.textwidget(
573                        edit=self._textwidgets[control],
574                        text=self._parent_window.get_control_value_name(
575                            control
576                        ),
577                    )
578                bui.getsound('gunCocking').play()
579                dialog.die()
580
581    def _done(self) -> None:
582        bui.containerwidget(edit=self._root_widget, transition='out_scale')

Window for advanced gamepad configuration.

GamepadAdvancedSettingsWindow(parent_window: bauiv1lib.settings.gamepad.GamepadSettingsWindow)
 23    def __init__(self, parent_window: GamepadSettingsWindow):
 24        # pylint: disable=too-many-statements
 25        # pylint: disable=too-many-locals
 26        self._parent_window = parent_window
 27
 28        app = bui.app
 29
 30        self._r = parent_window.get_r()
 31        assert bui.app.classic is not None
 32        uiscale = bui.app.ui_v1.uiscale
 33        self._width = 900 if uiscale is bui.UIScale.SMALL else 700
 34        self._x_inset = x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 35        self._height = 402 if uiscale is bui.UIScale.SMALL else 512
 36        self._textwidgets: dict[str, bui.Widget] = {}
 37        advb = parent_window.get_advanced_button()
 38        super().__init__(
 39            root_widget=bui.containerwidget(
 40                transition='in_scale',
 41                size=(self._width, self._height),
 42                scale=1.06
 43                * (
 44                    1.6
 45                    if uiscale is bui.UIScale.SMALL
 46                    else 1.35 if uiscale is bui.UIScale.MEDIUM else 1.0
 47                ),
 48                stack_offset=(
 49                    (0, -25) if uiscale is bui.UIScale.SMALL else (0, 0)
 50                ),
 51                scale_origin_stack_offset=(advb.get_screen_space_center()),
 52            )
 53        )
 54
 55        bui.textwidget(
 56            parent=self._root_widget,
 57            position=(
 58                self._width * 0.5,
 59                self._height - (40 if uiscale is bui.UIScale.SMALL else 34),
 60            ),
 61            size=(0, 0),
 62            text=bui.Lstr(resource=f'{self._r}.advancedTitleText'),
 63            maxwidth=320,
 64            color=bui.app.ui_v1.title_color,
 65            h_align='center',
 66            v_align='center',
 67        )
 68
 69        back_button = btn = bui.buttonwidget(
 70            parent=self._root_widget,
 71            autoselect=True,
 72            position=(
 73                self._width - (176 + x_inset),
 74                self._height - (60 if uiscale is bui.UIScale.SMALL else 55),
 75            ),
 76            size=(120, 48),
 77            text_scale=0.8,
 78            label=bui.Lstr(resource='doneText'),
 79            on_activate_call=self._done,
 80        )
 81        bui.containerwidget(
 82            edit=self._root_widget,
 83            start_button=btn,
 84            on_cancel_call=btn.activate,
 85        )
 86
 87        self._scroll_width = self._width - (100 + 2 * x_inset)
 88        self._scroll_height = self._height - 110
 89        self._sub_width = self._scroll_width - 20
 90        self._sub_height = (
 91            940 if self._parent_window.get_is_secondary() else 1040
 92        )
 93        if app.env.vr:
 94            self._sub_height += 50
 95        self._scrollwidget = bui.scrollwidget(
 96            parent=self._root_widget,
 97            position=(
 98                (self._width - self._scroll_width) * 0.5,
 99                self._height - 65 - self._scroll_height,
100            ),
101            size=(self._scroll_width, self._scroll_height),
102            claims_left_right=True,
103            claims_tab=True,
104            selection_loops_to_parent=True,
105        )
106        self._subcontainer = bui.containerwidget(
107            parent=self._scrollwidget,
108            size=(self._sub_width, self._sub_height),
109            background=False,
110            claims_left_right=True,
111            claims_tab=True,
112            selection_loops_to_parent=True,
113        )
114        bui.containerwidget(
115            edit=self._root_widget, selected_child=self._scrollwidget
116        )
117
118        h = 30
119        v = self._sub_height - 10
120
121        h2 = h + 12
122
123        # don't allow secondary joysticks to handle unassigned buttons
124        if not self._parent_window.get_is_secondary():
125            v -= 40
126            cb1 = bui.checkboxwidget(
127                parent=self._subcontainer,
128                position=(h + 70, v),
129                size=(500, 30),
130                text=bui.Lstr(resource=f'{self._r}.unassignedButtonsRunText'),
131                textcolor=(0.8, 0.8, 0.8),
132                maxwidth=330,
133                scale=1.0,
134                on_value_change_call=(
135                    self._parent_window.set_unassigned_buttons_run_value
136                ),
137                autoselect=True,
138                value=self._parent_window.get_unassigned_buttons_run_value(),
139            )
140            bui.widget(edit=cb1, up_widget=back_button)
141        v -= 60
142        capb = self._capture_button(
143            pos=(h2, v),
144            name=bui.Lstr(resource=f'{self._r}.runButton1Text'),
145            control='buttonRun1' + self._parent_window.get_ext(),
146        )
147        if self._parent_window.get_is_secondary():
148            for widget in capb:
149                bui.widget(edit=widget, up_widget=back_button)
150        v -= 42
151        self._capture_button(
152            pos=(h2, v),
153            name=bui.Lstr(resource=f'{self._r}.runButton2Text'),
154            control='buttonRun2' + self._parent_window.get_ext(),
155        )
156        bui.textwidget(
157            parent=self._subcontainer,
158            position=(self._sub_width * 0.5, v - 24),
159            size=(0, 0),
160            text=bui.Lstr(resource=f'{self._r}.runTriggerDescriptionText'),
161            color=(0.7, 1, 0.7, 0.6),
162            maxwidth=self._sub_width * 0.8,
163            scale=0.7,
164            h_align='center',
165            v_align='center',
166        )
167
168        v -= 85
169
170        self._capture_button(
171            pos=(h2, v),
172            name=bui.Lstr(resource=f'{self._r}.runTrigger1Text'),
173            control='triggerRun1' + self._parent_window.get_ext(),
174            message=bui.Lstr(resource=f'{self._r}.pressAnyAnalogTriggerText'),
175        )
176        v -= 42
177        self._capture_button(
178            pos=(h2, v),
179            name=bui.Lstr(resource=f'{self._r}.runTrigger2Text'),
180            control='triggerRun2' + self._parent_window.get_ext(),
181            message=bui.Lstr(resource=f'{self._r}.pressAnyAnalogTriggerText'),
182        )
183
184        # in vr mode, allow assigning a reset-view button
185        if app.env.vr:
186            v -= 50
187            self._capture_button(
188                pos=(h2, v),
189                name=bui.Lstr(resource=f'{self._r}.vrReorientButtonText'),
190                control='buttonVRReorient' + self._parent_window.get_ext(),
191            )
192
193        v -= 60
194        self._capture_button(
195            pos=(h2, v),
196            name=bui.Lstr(resource=f'{self._r}.extraStartButtonText'),
197            control='buttonStart2' + self._parent_window.get_ext(),
198        )
199        v -= 60
200        self._capture_button(
201            pos=(h2, v),
202            name=bui.Lstr(resource=f'{self._r}.ignoredButton1Text'),
203            control='buttonIgnored' + self._parent_window.get_ext(),
204        )
205        v -= 42
206        self._capture_button(
207            pos=(h2, v),
208            name=bui.Lstr(resource=f'{self._r}.ignoredButton2Text'),
209            control='buttonIgnored2' + self._parent_window.get_ext(),
210        )
211        v -= 42
212        self._capture_button(
213            pos=(h2, v),
214            name=bui.Lstr(resource=f'{self._r}.ignoredButton3Text'),
215            control='buttonIgnored3' + self._parent_window.get_ext(),
216        )
217        v -= 42
218        self._capture_button(
219            pos=(h2, v),
220            name=bui.Lstr(resource=f'{self._r}.ignoredButton4Text'),
221            control='buttonIgnored4' + self._parent_window.get_ext(),
222        )
223        bui.textwidget(
224            parent=self._subcontainer,
225            position=(self._sub_width * 0.5, v - 14),
226            size=(0, 0),
227            text=bui.Lstr(resource=f'{self._r}.ignoredButtonDescriptionText'),
228            color=(0.7, 1, 0.7, 0.6),
229            scale=0.8,
230            maxwidth=self._sub_width * 0.8,
231            h_align='center',
232            v_align='center',
233        )
234
235        v -= 80
236        pwin = self._parent_window
237        bui.checkboxwidget(
238            parent=self._subcontainer,
239            autoselect=True,
240            position=(h + 50, v),
241            size=(400, 30),
242            text=bui.Lstr(
243                resource=f'{self._r}.startButtonActivatesDefaultText'
244            ),
245            textcolor=(0.8, 0.8, 0.8),
246            maxwidth=450,
247            scale=0.9,
248            on_value_change_call=(
249                pwin.set_start_button_activates_default_widget_value
250            ),
251            value=pwin.get_start_button_activates_default_widget_value(),
252        )
253        bui.textwidget(
254            parent=self._subcontainer,
255            position=(self._sub_width * 0.5, v - 12),
256            size=(0, 0),
257            text=bui.Lstr(
258                resource=f'{self._r}.startButtonActivatesDefaultDescriptionText'
259            ),
260            color=(0.7, 1, 0.7, 0.6),
261            maxwidth=self._sub_width * 0.8,
262            scale=0.7,
263            h_align='center',
264            v_align='center',
265        )
266
267        v -= 80
268        bui.checkboxwidget(
269            parent=self._subcontainer,
270            autoselect=True,
271            position=(h + 50, v),
272            size=(400, 30),
273            text=bui.Lstr(resource=f'{self._r}.uiOnlyText'),
274            textcolor=(0.8, 0.8, 0.8),
275            maxwidth=450,
276            scale=0.9,
277            on_value_change_call=self._parent_window.set_ui_only_value,
278            value=self._parent_window.get_ui_only_value(),
279        )
280        bui.textwidget(
281            parent=self._subcontainer,
282            position=(self._sub_width * 0.5, v - 12),
283            size=(0, 0),
284            text=bui.Lstr(resource=f'{self._r}.uiOnlyDescriptionText'),
285            color=(0.7, 1, 0.7, 0.6),
286            maxwidth=self._sub_width * 0.8,
287            scale=0.7,
288            h_align='center',
289            v_align='center',
290        )
291
292        v -= 80
293        bui.checkboxwidget(
294            parent=self._subcontainer,
295            autoselect=True,
296            position=(h + 50, v),
297            size=(400, 30),
298            text=bui.Lstr(resource=f'{self._r}.ignoreCompletelyText'),
299            textcolor=(0.8, 0.8, 0.8),
300            maxwidth=450,
301            scale=0.9,
302            on_value_change_call=pwin.set_ignore_completely_value,
303            value=self._parent_window.get_ignore_completely_value(),
304        )
305        bui.textwidget(
306            parent=self._subcontainer,
307            position=(self._sub_width * 0.5, v - 12),
308            size=(0, 0),
309            text=bui.Lstr(
310                resource=f'{self._r}.ignoreCompletelyDescriptionText'
311            ),
312            color=(0.7, 1, 0.7, 0.6),
313            maxwidth=self._sub_width * 0.8,
314            scale=0.7,
315            h_align='center',
316            v_align='center',
317        )
318
319        v -= 80
320
321        cb1 = bui.checkboxwidget(
322            parent=self._subcontainer,
323            autoselect=True,
324            position=(h + 50, v),
325            size=(400, 30),
326            text=bui.Lstr(resource=f'{self._r}.autoRecalibrateText'),
327            textcolor=(0.8, 0.8, 0.8),
328            maxwidth=450,
329            scale=0.9,
330            on_value_change_call=pwin.set_auto_recalibrate_analog_stick_value,
331            value=self._parent_window.get_auto_recalibrate_analog_stick_value(),
332        )
333        bui.textwidget(
334            parent=self._subcontainer,
335            position=(self._sub_width * 0.5, v - 12),
336            size=(0, 0),
337            text=bui.Lstr(resource=f'{self._r}.autoRecalibrateDescriptionText'),
338            color=(0.7, 1, 0.7, 0.6),
339            maxwidth=self._sub_width * 0.8,
340            scale=0.7,
341            h_align='center',
342            v_align='center',
343        )
344        v -= 80
345
346        buttons = self._config_value_editor(
347            bui.Lstr(resource=f'{self._r}.analogStickDeadZoneText'),
348            control=('analogStickDeadZone' + self._parent_window.get_ext()),
349            position=(h + 40, v),
350            min_val=0,
351            max_val=10.0,
352            increment=0.1,
353            x_offset=100,
354        )
355        bui.widget(edit=buttons[0], left_widget=cb1, up_widget=cb1)
356        bui.widget(edit=cb1, right_widget=buttons[0], down_widget=buttons[0])
357
358        bui.textwidget(
359            parent=self._subcontainer,
360            position=(self._sub_width * 0.5, v - 12),
361            size=(0, 0),
362            text=bui.Lstr(
363                resource=f'{self._r}.analogStickDeadZoneDescriptionText'
364            ),
365            color=(0.7, 1, 0.7, 0.6),
366            maxwidth=self._sub_width * 0.8,
367            scale=0.7,
368            h_align='center',
369            v_align='center',
370        )
371        v -= 100
372
373        # child joysticks cant have child joysticks.. that's just
374        # crazy talk
375        if not self._parent_window.get_is_secondary():
376            bui.buttonwidget(
377                parent=self._subcontainer,
378                autoselect=True,
379                label=bui.Lstr(resource=f'{self._r}.twoInOneSetupText'),
380                position=(40, v),
381                size=(self._sub_width - 80, 50),
382                on_activate_call=self._parent_window.show_secondary_editor,
383                up_widget=buttons[0],
384            )
385
386        # set a bigger bottom show-buffer for the widgets we just made
387        # so we can see the text below them when navigating with
388        # a gamepad
389        for child in self._subcontainer.get_children():
390            bui.widget(edit=child, show_buffer_bottom=30, show_buffer_top=30)