bastd.ui.settings.audio

Provides audio settings UI.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides audio settings UI."""
  4
  5from __future__ import annotations
  6
  7from typing import TYPE_CHECKING
  8
  9import ba
 10import ba.internal
 11
 12if TYPE_CHECKING:
 13    pass
 14
 15
 16class AudioSettingsWindow(ba.Window):
 17    """Window for editing audio settings."""
 18
 19    def __init__(
 20        self,
 21        transition: str = 'in_right',
 22        origin_widget: ba.Widget | None = None,
 23    ):
 24        # pylint: disable=too-many-statements
 25        # pylint: disable=too-many-locals
 26        # pylint: disable=cyclic-import
 27        from bastd.ui.popup import PopupMenu
 28        from bastd.ui.config import ConfigNumberEdit
 29
 30        music = ba.app.music
 31
 32        # If they provided an origin-widget, scale up from that.
 33        scale_origin: tuple[float, float] | None
 34        if origin_widget is not None:
 35            self._transition_out = 'out_scale'
 36            scale_origin = origin_widget.get_screen_space_center()
 37            transition = 'in_scale'
 38        else:
 39            self._transition_out = 'out_right'
 40            scale_origin = None
 41
 42        self._r = 'audioSettingsWindow'
 43
 44        spacing = 50.0
 45        width = 460.0
 46        height = 210.0
 47
 48        # Update: hard-coding head-relative audio to true now,
 49        # so not showing options.
 50        # show_vr_head_relative_audio = True if ba.app.vr_mode else False
 51        show_vr_head_relative_audio = False
 52
 53        if show_vr_head_relative_audio:
 54            height += 70
 55
 56        show_soundtracks = False
 57        if music.have_music_player():
 58            show_soundtracks = True
 59            height += spacing * 2.0
 60
 61        uiscale = ba.app.ui.uiscale
 62        base_scale = (
 63            2.05
 64            if uiscale is ba.UIScale.SMALL
 65            else 1.6
 66            if uiscale is ba.UIScale.MEDIUM
 67            else 1.0
 68        )
 69        popup_menu_scale = base_scale * 1.2
 70
 71        super().__init__(
 72            root_widget=ba.containerwidget(
 73                size=(width, height),
 74                transition=transition,
 75                scale=base_scale,
 76                scale_origin_stack_offset=scale_origin,
 77                stack_offset=(0, -20)
 78                if uiscale is ba.UIScale.SMALL
 79                else (0, 0),
 80            )
 81        )
 82
 83        self._back_button = back_button = btn = ba.buttonwidget(
 84            parent=self._root_widget,
 85            position=(35, height - 55),
 86            size=(120, 60),
 87            scale=0.8,
 88            text_scale=1.2,
 89            label=ba.Lstr(resource='backText'),
 90            button_type='back',
 91            on_activate_call=self._back,
 92            autoselect=True,
 93        )
 94        ba.containerwidget(edit=self._root_widget, cancel_button=btn)
 95        v = height - 60
 96        v -= spacing * 1.0
 97        ba.textwidget(
 98            parent=self._root_widget,
 99            position=(width * 0.5, height - 32),
100            size=(0, 0),
101            text=ba.Lstr(resource=self._r + '.titleText'),
102            color=ba.app.ui.title_color,
103            maxwidth=180,
104            h_align='center',
105            v_align='center',
106        )
107
108        ba.buttonwidget(
109            edit=self._back_button,
110            button_type='backSmall',
111            size=(60, 60),
112            label=ba.charstr(ba.SpecialChar.BACK),
113        )
114
115        self._sound_volume_numedit = svne = ConfigNumberEdit(
116            parent=self._root_widget,
117            position=(40, v),
118            xoffset=10,
119            configkey='Sound Volume',
120            displayname=ba.Lstr(resource=self._r + '.soundVolumeText'),
121            minval=0.0,
122            maxval=1.0,
123            increment=0.1,
124        )
125        if ba.app.ui.use_toolbars:
126            ba.widget(
127                edit=svne.plusbutton,
128                right_widget=ba.internal.get_special_widget('party_button'),
129            )
130        v -= spacing
131        self._music_volume_numedit = ConfigNumberEdit(
132            parent=self._root_widget,
133            position=(40, v),
134            xoffset=10,
135            configkey='Music Volume',
136            displayname=ba.Lstr(resource=self._r + '.musicVolumeText'),
137            minval=0.0,
138            maxval=1.0,
139            increment=0.1,
140            callback=music.music_volume_changed,
141            changesound=False,
142        )
143
144        v -= 0.5 * spacing
145
146        self._vr_head_relative_audio_button: ba.Widget | None
147        if show_vr_head_relative_audio:
148            v -= 40
149            ba.textwidget(
150                parent=self._root_widget,
151                position=(40, v + 24),
152                size=(0, 0),
153                text=ba.Lstr(resource=self._r + '.headRelativeVRAudioText'),
154                color=(0.8, 0.8, 0.8),
155                maxwidth=230,
156                h_align='left',
157                v_align='center',
158            )
159
160            popup = PopupMenu(
161                parent=self._root_widget,
162                position=(290, v),
163                width=120,
164                button_size=(135, 50),
165                scale=popup_menu_scale,
166                choices=['Auto', 'On', 'Off'],
167                choices_display=[
168                    ba.Lstr(resource='autoText'),
169                    ba.Lstr(resource='onText'),
170                    ba.Lstr(resource='offText'),
171                ],
172                current_choice=ba.app.config.resolve('VR Head Relative Audio'),
173                on_value_change_call=self._set_vr_head_relative_audio,
174            )
175            self._vr_head_relative_audio_button = popup.get_button()
176            ba.textwidget(
177                parent=self._root_widget,
178                position=(width * 0.5, v - 11),
179                size=(0, 0),
180                text=ba.Lstr(resource=self._r + '.headRelativeVRAudioInfoText'),
181                scale=0.5,
182                color=(0.7, 0.8, 0.7),
183                maxwidth=400,
184                flatness=1.0,
185                h_align='center',
186                v_align='center',
187            )
188            v -= 30
189        else:
190            self._vr_head_relative_audio_button = None
191
192        self._soundtrack_button: ba.Widget | None
193        if show_soundtracks:
194            v -= 1.2 * spacing
195            self._soundtrack_button = ba.buttonwidget(
196                parent=self._root_widget,
197                position=((width - 310) / 2, v),
198                size=(310, 50),
199                autoselect=True,
200                label=ba.Lstr(resource=self._r + '.soundtrackButtonText'),
201                on_activate_call=self._do_soundtracks,
202            )
203            v -= spacing * 0.5
204            ba.textwidget(
205                parent=self._root_widget,
206                position=(0, v),
207                size=(width, 20),
208                text=ba.Lstr(resource=self._r + '.soundtrackDescriptionText'),
209                flatness=1.0,
210                h_align='center',
211                scale=0.5,
212                color=(0.7, 0.8, 0.7, 1.0),
213                maxwidth=400,
214            )
215        else:
216            self._soundtrack_button = None
217
218        # Tweak a few navigation bits.
219        try:
220            ba.widget(edit=back_button, down_widget=svne.minusbutton)
221        except Exception:
222            ba.print_exception('Error wiring AudioSettingsWindow.')
223
224        self._restore_state()
225
226    def _set_vr_head_relative_audio(self, val: str) -> None:
227        cfg = ba.app.config
228        cfg['VR Head Relative Audio'] = val
229        cfg.apply_and_commit()
230
231    def _do_soundtracks(self) -> None:
232        # pylint: disable=cyclic-import
233        from bastd.ui.soundtrack import browser as stb
234
235        # We require disk access for soundtracks;
236        # if we don't have it, request it.
237        if not ba.internal.have_permission(ba.Permission.STORAGE):
238            ba.playsound(ba.getsound('ding'))
239            ba.screenmessage(
240                ba.Lstr(resource='storagePermissionAccessText'),
241                color=(0.5, 1, 0.5),
242            )
243            ba.timer(
244                1.0,
245                ba.Call(ba.internal.request_permission, ba.Permission.STORAGE),
246                timetype=ba.TimeType.REAL,
247            )
248            return
249
250        self._save_state()
251        ba.containerwidget(edit=self._root_widget, transition='out_left')
252        ba.app.ui.set_main_menu_window(
253            stb.SoundtrackBrowserWindow(
254                origin_widget=self._soundtrack_button
255            ).get_root_widget()
256        )
257
258    def _back(self) -> None:
259        # pylint: disable=cyclic-import
260        from bastd.ui.settings import allsettings
261
262        self._save_state()
263        ba.containerwidget(
264            edit=self._root_widget, transition=self._transition_out
265        )
266        ba.app.ui.set_main_menu_window(
267            allsettings.AllSettingsWindow(
268                transition='in_left'
269            ).get_root_widget()
270        )
271
272    def _save_state(self) -> None:
273        try:
274            sel = self._root_widget.get_selected_child()
275            if sel == self._sound_volume_numedit.minusbutton:
276                sel_name = 'SoundMinus'
277            elif sel == self._sound_volume_numedit.plusbutton:
278                sel_name = 'SoundPlus'
279            elif sel == self._music_volume_numedit.minusbutton:
280                sel_name = 'MusicMinus'
281            elif sel == self._music_volume_numedit.plusbutton:
282                sel_name = 'MusicPlus'
283            elif sel == self._soundtrack_button:
284                sel_name = 'Soundtrack'
285            elif sel == self._back_button:
286                sel_name = 'Back'
287            elif sel == self._vr_head_relative_audio_button:
288                sel_name = 'VRHeadRelative'
289            else:
290                raise ValueError(f'unrecognized selection \'{sel}\'')
291            ba.app.ui.window_states[type(self)] = sel_name
292        except Exception:
293            ba.print_exception(f'Error saving state for {self.__class__}.')
294
295    def _restore_state(self) -> None:
296        try:
297            sel_name = ba.app.ui.window_states.get(type(self))
298            sel: ba.Widget | None
299            if sel_name == 'SoundMinus':
300                sel = self._sound_volume_numedit.minusbutton
301            elif sel_name == 'SoundPlus':
302                sel = self._sound_volume_numedit.plusbutton
303            elif sel_name == 'MusicMinus':
304                sel = self._music_volume_numedit.minusbutton
305            elif sel_name == 'MusicPlus':
306                sel = self._music_volume_numedit.plusbutton
307            elif sel_name == 'VRHeadRelative':
308                sel = self._vr_head_relative_audio_button
309            elif sel_name == 'Soundtrack':
310                sel = self._soundtrack_button
311            elif sel_name == 'Back':
312                sel = self._back_button
313            else:
314                sel = self._back_button
315            if sel:
316                ba.containerwidget(edit=self._root_widget, selected_child=sel)
317        except Exception:
318            ba.print_exception(f'Error restoring state for {self.__class__}.')
class AudioSettingsWindow(ba.ui.Window):
 17class AudioSettingsWindow(ba.Window):
 18    """Window for editing audio settings."""
 19
 20    def __init__(
 21        self,
 22        transition: str = 'in_right',
 23        origin_widget: ba.Widget | None = None,
 24    ):
 25        # pylint: disable=too-many-statements
 26        # pylint: disable=too-many-locals
 27        # pylint: disable=cyclic-import
 28        from bastd.ui.popup import PopupMenu
 29        from bastd.ui.config import ConfigNumberEdit
 30
 31        music = ba.app.music
 32
 33        # If they provided an origin-widget, scale up from that.
 34        scale_origin: tuple[float, float] | None
 35        if origin_widget is not None:
 36            self._transition_out = 'out_scale'
 37            scale_origin = origin_widget.get_screen_space_center()
 38            transition = 'in_scale'
 39        else:
 40            self._transition_out = 'out_right'
 41            scale_origin = None
 42
 43        self._r = 'audioSettingsWindow'
 44
 45        spacing = 50.0
 46        width = 460.0
 47        height = 210.0
 48
 49        # Update: hard-coding head-relative audio to true now,
 50        # so not showing options.
 51        # show_vr_head_relative_audio = True if ba.app.vr_mode else False
 52        show_vr_head_relative_audio = False
 53
 54        if show_vr_head_relative_audio:
 55            height += 70
 56
 57        show_soundtracks = False
 58        if music.have_music_player():
 59            show_soundtracks = True
 60            height += spacing * 2.0
 61
 62        uiscale = ba.app.ui.uiscale
 63        base_scale = (
 64            2.05
 65            if uiscale is ba.UIScale.SMALL
 66            else 1.6
 67            if uiscale is ba.UIScale.MEDIUM
 68            else 1.0
 69        )
 70        popup_menu_scale = base_scale * 1.2
 71
 72        super().__init__(
 73            root_widget=ba.containerwidget(
 74                size=(width, height),
 75                transition=transition,
 76                scale=base_scale,
 77                scale_origin_stack_offset=scale_origin,
 78                stack_offset=(0, -20)
 79                if uiscale is ba.UIScale.SMALL
 80                else (0, 0),
 81            )
 82        )
 83
 84        self._back_button = back_button = btn = ba.buttonwidget(
 85            parent=self._root_widget,
 86            position=(35, height - 55),
 87            size=(120, 60),
 88            scale=0.8,
 89            text_scale=1.2,
 90            label=ba.Lstr(resource='backText'),
 91            button_type='back',
 92            on_activate_call=self._back,
 93            autoselect=True,
 94        )
 95        ba.containerwidget(edit=self._root_widget, cancel_button=btn)
 96        v = height - 60
 97        v -= spacing * 1.0
 98        ba.textwidget(
 99            parent=self._root_widget,
100            position=(width * 0.5, height - 32),
101            size=(0, 0),
102            text=ba.Lstr(resource=self._r + '.titleText'),
103            color=ba.app.ui.title_color,
104            maxwidth=180,
105            h_align='center',
106            v_align='center',
107        )
108
109        ba.buttonwidget(
110            edit=self._back_button,
111            button_type='backSmall',
112            size=(60, 60),
113            label=ba.charstr(ba.SpecialChar.BACK),
114        )
115
116        self._sound_volume_numedit = svne = ConfigNumberEdit(
117            parent=self._root_widget,
118            position=(40, v),
119            xoffset=10,
120            configkey='Sound Volume',
121            displayname=ba.Lstr(resource=self._r + '.soundVolumeText'),
122            minval=0.0,
123            maxval=1.0,
124            increment=0.1,
125        )
126        if ba.app.ui.use_toolbars:
127            ba.widget(
128                edit=svne.plusbutton,
129                right_widget=ba.internal.get_special_widget('party_button'),
130            )
131        v -= spacing
132        self._music_volume_numedit = ConfigNumberEdit(
133            parent=self._root_widget,
134            position=(40, v),
135            xoffset=10,
136            configkey='Music Volume',
137            displayname=ba.Lstr(resource=self._r + '.musicVolumeText'),
138            minval=0.0,
139            maxval=1.0,
140            increment=0.1,
141            callback=music.music_volume_changed,
142            changesound=False,
143        )
144
145        v -= 0.5 * spacing
146
147        self._vr_head_relative_audio_button: ba.Widget | None
148        if show_vr_head_relative_audio:
149            v -= 40
150            ba.textwidget(
151                parent=self._root_widget,
152                position=(40, v + 24),
153                size=(0, 0),
154                text=ba.Lstr(resource=self._r + '.headRelativeVRAudioText'),
155                color=(0.8, 0.8, 0.8),
156                maxwidth=230,
157                h_align='left',
158                v_align='center',
159            )
160
161            popup = PopupMenu(
162                parent=self._root_widget,
163                position=(290, v),
164                width=120,
165                button_size=(135, 50),
166                scale=popup_menu_scale,
167                choices=['Auto', 'On', 'Off'],
168                choices_display=[
169                    ba.Lstr(resource='autoText'),
170                    ba.Lstr(resource='onText'),
171                    ba.Lstr(resource='offText'),
172                ],
173                current_choice=ba.app.config.resolve('VR Head Relative Audio'),
174                on_value_change_call=self._set_vr_head_relative_audio,
175            )
176            self._vr_head_relative_audio_button = popup.get_button()
177            ba.textwidget(
178                parent=self._root_widget,
179                position=(width * 0.5, v - 11),
180                size=(0, 0),
181                text=ba.Lstr(resource=self._r + '.headRelativeVRAudioInfoText'),
182                scale=0.5,
183                color=(0.7, 0.8, 0.7),
184                maxwidth=400,
185                flatness=1.0,
186                h_align='center',
187                v_align='center',
188            )
189            v -= 30
190        else:
191            self._vr_head_relative_audio_button = None
192
193        self._soundtrack_button: ba.Widget | None
194        if show_soundtracks:
195            v -= 1.2 * spacing
196            self._soundtrack_button = ba.buttonwidget(
197                parent=self._root_widget,
198                position=((width - 310) / 2, v),
199                size=(310, 50),
200                autoselect=True,
201                label=ba.Lstr(resource=self._r + '.soundtrackButtonText'),
202                on_activate_call=self._do_soundtracks,
203            )
204            v -= spacing * 0.5
205            ba.textwidget(
206                parent=self._root_widget,
207                position=(0, v),
208                size=(width, 20),
209                text=ba.Lstr(resource=self._r + '.soundtrackDescriptionText'),
210                flatness=1.0,
211                h_align='center',
212                scale=0.5,
213                color=(0.7, 0.8, 0.7, 1.0),
214                maxwidth=400,
215            )
216        else:
217            self._soundtrack_button = None
218
219        # Tweak a few navigation bits.
220        try:
221            ba.widget(edit=back_button, down_widget=svne.minusbutton)
222        except Exception:
223            ba.print_exception('Error wiring AudioSettingsWindow.')
224
225        self._restore_state()
226
227    def _set_vr_head_relative_audio(self, val: str) -> None:
228        cfg = ba.app.config
229        cfg['VR Head Relative Audio'] = val
230        cfg.apply_and_commit()
231
232    def _do_soundtracks(self) -> None:
233        # pylint: disable=cyclic-import
234        from bastd.ui.soundtrack import browser as stb
235
236        # We require disk access for soundtracks;
237        # if we don't have it, request it.
238        if not ba.internal.have_permission(ba.Permission.STORAGE):
239            ba.playsound(ba.getsound('ding'))
240            ba.screenmessage(
241                ba.Lstr(resource='storagePermissionAccessText'),
242                color=(0.5, 1, 0.5),
243            )
244            ba.timer(
245                1.0,
246                ba.Call(ba.internal.request_permission, ba.Permission.STORAGE),
247                timetype=ba.TimeType.REAL,
248            )
249            return
250
251        self._save_state()
252        ba.containerwidget(edit=self._root_widget, transition='out_left')
253        ba.app.ui.set_main_menu_window(
254            stb.SoundtrackBrowserWindow(
255                origin_widget=self._soundtrack_button
256            ).get_root_widget()
257        )
258
259    def _back(self) -> None:
260        # pylint: disable=cyclic-import
261        from bastd.ui.settings import allsettings
262
263        self._save_state()
264        ba.containerwidget(
265            edit=self._root_widget, transition=self._transition_out
266        )
267        ba.app.ui.set_main_menu_window(
268            allsettings.AllSettingsWindow(
269                transition='in_left'
270            ).get_root_widget()
271        )
272
273    def _save_state(self) -> None:
274        try:
275            sel = self._root_widget.get_selected_child()
276            if sel == self._sound_volume_numedit.minusbutton:
277                sel_name = 'SoundMinus'
278            elif sel == self._sound_volume_numedit.plusbutton:
279                sel_name = 'SoundPlus'
280            elif sel == self._music_volume_numedit.minusbutton:
281                sel_name = 'MusicMinus'
282            elif sel == self._music_volume_numedit.plusbutton:
283                sel_name = 'MusicPlus'
284            elif sel == self._soundtrack_button:
285                sel_name = 'Soundtrack'
286            elif sel == self._back_button:
287                sel_name = 'Back'
288            elif sel == self._vr_head_relative_audio_button:
289                sel_name = 'VRHeadRelative'
290            else:
291                raise ValueError(f'unrecognized selection \'{sel}\'')
292            ba.app.ui.window_states[type(self)] = sel_name
293        except Exception:
294            ba.print_exception(f'Error saving state for {self.__class__}.')
295
296    def _restore_state(self) -> None:
297        try:
298            sel_name = ba.app.ui.window_states.get(type(self))
299            sel: ba.Widget | None
300            if sel_name == 'SoundMinus':
301                sel = self._sound_volume_numedit.minusbutton
302            elif sel_name == 'SoundPlus':
303                sel = self._sound_volume_numedit.plusbutton
304            elif sel_name == 'MusicMinus':
305                sel = self._music_volume_numedit.minusbutton
306            elif sel_name == 'MusicPlus':
307                sel = self._music_volume_numedit.plusbutton
308            elif sel_name == 'VRHeadRelative':
309                sel = self._vr_head_relative_audio_button
310            elif sel_name == 'Soundtrack':
311                sel = self._soundtrack_button
312            elif sel_name == 'Back':
313                sel = self._back_button
314            else:
315                sel = self._back_button
316            if sel:
317                ba.containerwidget(edit=self._root_widget, selected_child=sel)
318        except Exception:
319            ba.print_exception(f'Error restoring state for {self.__class__}.')

Window for editing audio settings.

AudioSettingsWindow( transition: str = 'in_right', origin_widget: _ba.Widget | None = None)
 20    def __init__(
 21        self,
 22        transition: str = 'in_right',
 23        origin_widget: ba.Widget | None = None,
 24    ):
 25        # pylint: disable=too-many-statements
 26        # pylint: disable=too-many-locals
 27        # pylint: disable=cyclic-import
 28        from bastd.ui.popup import PopupMenu
 29        from bastd.ui.config import ConfigNumberEdit
 30
 31        music = ba.app.music
 32
 33        # If they provided an origin-widget, scale up from that.
 34        scale_origin: tuple[float, float] | None
 35        if origin_widget is not None:
 36            self._transition_out = 'out_scale'
 37            scale_origin = origin_widget.get_screen_space_center()
 38            transition = 'in_scale'
 39        else:
 40            self._transition_out = 'out_right'
 41            scale_origin = None
 42
 43        self._r = 'audioSettingsWindow'
 44
 45        spacing = 50.0
 46        width = 460.0
 47        height = 210.0
 48
 49        # Update: hard-coding head-relative audio to true now,
 50        # so not showing options.
 51        # show_vr_head_relative_audio = True if ba.app.vr_mode else False
 52        show_vr_head_relative_audio = False
 53
 54        if show_vr_head_relative_audio:
 55            height += 70
 56
 57        show_soundtracks = False
 58        if music.have_music_player():
 59            show_soundtracks = True
 60            height += spacing * 2.0
 61
 62        uiscale = ba.app.ui.uiscale
 63        base_scale = (
 64            2.05
 65            if uiscale is ba.UIScale.SMALL
 66            else 1.6
 67            if uiscale is ba.UIScale.MEDIUM
 68            else 1.0
 69        )
 70        popup_menu_scale = base_scale * 1.2
 71
 72        super().__init__(
 73            root_widget=ba.containerwidget(
 74                size=(width, height),
 75                transition=transition,
 76                scale=base_scale,
 77                scale_origin_stack_offset=scale_origin,
 78                stack_offset=(0, -20)
 79                if uiscale is ba.UIScale.SMALL
 80                else (0, 0),
 81            )
 82        )
 83
 84        self._back_button = back_button = btn = ba.buttonwidget(
 85            parent=self._root_widget,
 86            position=(35, height - 55),
 87            size=(120, 60),
 88            scale=0.8,
 89            text_scale=1.2,
 90            label=ba.Lstr(resource='backText'),
 91            button_type='back',
 92            on_activate_call=self._back,
 93            autoselect=True,
 94        )
 95        ba.containerwidget(edit=self._root_widget, cancel_button=btn)
 96        v = height - 60
 97        v -= spacing * 1.0
 98        ba.textwidget(
 99            parent=self._root_widget,
100            position=(width * 0.5, height - 32),
101            size=(0, 0),
102            text=ba.Lstr(resource=self._r + '.titleText'),
103            color=ba.app.ui.title_color,
104            maxwidth=180,
105            h_align='center',
106            v_align='center',
107        )
108
109        ba.buttonwidget(
110            edit=self._back_button,
111            button_type='backSmall',
112            size=(60, 60),
113            label=ba.charstr(ba.SpecialChar.BACK),
114        )
115
116        self._sound_volume_numedit = svne = ConfigNumberEdit(
117            parent=self._root_widget,
118            position=(40, v),
119            xoffset=10,
120            configkey='Sound Volume',
121            displayname=ba.Lstr(resource=self._r + '.soundVolumeText'),
122            minval=0.0,
123            maxval=1.0,
124            increment=0.1,
125        )
126        if ba.app.ui.use_toolbars:
127            ba.widget(
128                edit=svne.plusbutton,
129                right_widget=ba.internal.get_special_widget('party_button'),
130            )
131        v -= spacing
132        self._music_volume_numedit = ConfigNumberEdit(
133            parent=self._root_widget,
134            position=(40, v),
135            xoffset=10,
136            configkey='Music Volume',
137            displayname=ba.Lstr(resource=self._r + '.musicVolumeText'),
138            minval=0.0,
139            maxval=1.0,
140            increment=0.1,
141            callback=music.music_volume_changed,
142            changesound=False,
143        )
144
145        v -= 0.5 * spacing
146
147        self._vr_head_relative_audio_button: ba.Widget | None
148        if show_vr_head_relative_audio:
149            v -= 40
150            ba.textwidget(
151                parent=self._root_widget,
152                position=(40, v + 24),
153                size=(0, 0),
154                text=ba.Lstr(resource=self._r + '.headRelativeVRAudioText'),
155                color=(0.8, 0.8, 0.8),
156                maxwidth=230,
157                h_align='left',
158                v_align='center',
159            )
160
161            popup = PopupMenu(
162                parent=self._root_widget,
163                position=(290, v),
164                width=120,
165                button_size=(135, 50),
166                scale=popup_menu_scale,
167                choices=['Auto', 'On', 'Off'],
168                choices_display=[
169                    ba.Lstr(resource='autoText'),
170                    ba.Lstr(resource='onText'),
171                    ba.Lstr(resource='offText'),
172                ],
173                current_choice=ba.app.config.resolve('VR Head Relative Audio'),
174                on_value_change_call=self._set_vr_head_relative_audio,
175            )
176            self._vr_head_relative_audio_button = popup.get_button()
177            ba.textwidget(
178                parent=self._root_widget,
179                position=(width * 0.5, v - 11),
180                size=(0, 0),
181                text=ba.Lstr(resource=self._r + '.headRelativeVRAudioInfoText'),
182                scale=0.5,
183                color=(0.7, 0.8, 0.7),
184                maxwidth=400,
185                flatness=1.0,
186                h_align='center',
187                v_align='center',
188            )
189            v -= 30
190        else:
191            self._vr_head_relative_audio_button = None
192
193        self._soundtrack_button: ba.Widget | None
194        if show_soundtracks:
195            v -= 1.2 * spacing
196            self._soundtrack_button = ba.buttonwidget(
197                parent=self._root_widget,
198                position=((width - 310) / 2, v),
199                size=(310, 50),
200                autoselect=True,
201                label=ba.Lstr(resource=self._r + '.soundtrackButtonText'),
202                on_activate_call=self._do_soundtracks,
203            )
204            v -= spacing * 0.5
205            ba.textwidget(
206                parent=self._root_widget,
207                position=(0, v),
208                size=(width, 20),
209                text=ba.Lstr(resource=self._r + '.soundtrackDescriptionText'),
210                flatness=1.0,
211                h_align='center',
212                scale=0.5,
213                color=(0.7, 0.8, 0.7, 1.0),
214                maxwidth=400,
215            )
216        else:
217            self._soundtrack_button = None
218
219        # Tweak a few navigation bits.
220        try:
221            ba.widget(edit=back_button, down_widget=svne.minusbutton)
222        except Exception:
223            ba.print_exception('Error wiring AudioSettingsWindow.')
224
225        self._restore_state()
Inherited Members
ba.ui.Window
get_root_widget