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