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

Window for editing audio settings.

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

Create a MainWindow given a root widget and transition info.

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

@override
def get_main_window_state(self) -> bauiv1.MainWindowState:
216    @override
217    def get_main_window_state(self) -> bui.MainWindowState:
218        # Support recreating our window for back/refresh purposes.
219        cls = type(self)
220        return bui.BasicMainWindowState(
221            create_call=lambda transition, origin_widget: cls(
222                transition=transition, origin_widget=origin_widget
223            )
224        )

Return a WindowState to recreate this window, if supported.

@override
def on_main_window_close(self) -> None:
226    @override
227    def on_main_window_close(self) -> None:
228        self._save_state()

Called before transitioning out a main window.

A good opportunity to save window state/etc.