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-locals
 25        # pylint: disable=cyclic-import
 26        from bauiv1lib.config import ConfigNumberEdit
 27
 28        assert bui.app.classic is not None
 29        music = bui.app.classic.music
 30
 31        self._r = 'audioSettingsWindow'
 32
 33        spacing = 50.0
 34        uiscale = bui.app.ui_v1.uiscale
 35
 36        width = 1200.0 if uiscale is bui.UIScale.SMALL else 500.0
 37        height = 800.0 if uiscale is bui.UIScale.SMALL else 350.0
 38
 39        show_soundtracks = False
 40        if music.have_music_player():
 41            show_soundtracks = True
 42
 43        # Do some fancy math to fill all available screen area up to the
 44        # size of our backing container. This lets us fit to the exact
 45        # screen shape at small ui scale.
 46        screensize = bui.get_virtual_screen_size()
 47        scale = (
 48            2.2
 49            if uiscale is bui.UIScale.SMALL
 50            else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
 51        )
 52        # Calc screen size in our local container space and clamp to a
 53        # bit smaller than our container size.
 54        # target_width = min(width - 60, screensize[0] / scale)
 55        target_height = min(height - 70, screensize[1] / scale)
 56
 57        # To get top/left coords, go to the center of our window and
 58        # offset by half the width/height of our target area.
 59        yoffs = 0.5 * height + 0.5 * target_height + 30.0
 60
 61        super().__init__(
 62            root_widget=bui.containerwidget(
 63                size=(width, height),
 64                scale=scale,
 65                toolbar_visibility=(
 66                    'menu_minimal'
 67                    if uiscale is bui.UIScale.SMALL
 68                    else 'menu_full'
 69                ),
 70            ),
 71            transition=transition,
 72            origin_widget=origin_widget,
 73            # We're affected by screen size only at small ui-scale.
 74            refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL,
 75        )
 76
 77        if uiscale is bui.UIScale.SMALL:
 78            bui.containerwidget(
 79                edit=self._root_widget, on_cancel_call=self.main_window_back
 80            )
 81            self._back_button = None
 82        else:
 83            self._back_button = bui.buttonwidget(
 84                parent=self._root_widget,
 85                position=(35, yoffs - 55),
 86                size=(60, 60),
 87                scale=0.8,
 88                text_scale=1.2,
 89                label=bui.charstr(bui.SpecialChar.BACK),
 90                button_type='backSmall',
 91                on_activate_call=self.main_window_back,
 92                autoselect=True,
 93            )
 94            bui.containerwidget(
 95                edit=self._root_widget, cancel_button=self._back_button
 96            )
 97
 98        bui.textwidget(
 99            parent=self._root_widget,
100            position=(
101                width * 0.5,
102                yoffs - (48 if uiscale is bui.UIScale.SMALL else 32),
103            ),
104            size=(0, 0),
105            text=bui.Lstr(resource=f'{self._r}.titleText'),
106            color=bui.app.ui_v1.title_color,
107            maxwidth=180,
108            h_align='center',
109            v_align='center',
110        )
111
112        # Roughly center everything else in our window.
113        x = width * 0.5 - 160
114        y = height * 0.5 + (100 if show_soundtracks else 70)
115        y -= spacing * 1.0
116
117        self._sound_volume_numedit = svne = ConfigNumberEdit(
118            parent=self._root_widget,
119            position=(x, y),
120            xoffset=10,
121            configkey='Sound Volume',
122            displayname=bui.Lstr(resource=f'{self._r}.soundVolumeText'),
123            minval=0.0,
124            maxval=1.0,
125            increment=0.05,
126            as_percent=True,
127        )
128        bui.widget(
129            edit=svne.plusbutton,
130            right_widget=bui.get_special_widget('squad_button'),
131        )
132        y -= spacing
133        self._music_volume_numedit = ConfigNumberEdit(
134            parent=self._root_widget,
135            position=(x, y),
136            xoffset=10,
137            configkey='Music Volume',
138            displayname=bui.Lstr(resource=f'{self._r}.musicVolumeText'),
139            minval=0.0,
140            maxval=1.0,
141            increment=0.05,
142            callback=music.music_volume_changed,
143            changesound=False,
144            as_percent=True,
145        )
146
147        y -= 0.5 * spacing
148
149        self._soundtrack_button: bui.Widget | None
150        if show_soundtracks:
151            y -= 1.2 * spacing
152            self._soundtrack_button = bui.buttonwidget(
153                parent=self._root_widget,
154                position=(width * 0.5 - 155, y),
155                size=(310, 50),
156                autoselect=True,
157                label=bui.Lstr(resource=f'{self._r}.soundtrackButtonText'),
158                on_activate_call=self._do_soundtracks,
159            )
160            y -= spacing * 0.3
161            bui.textwidget(
162                parent=self._root_widget,
163                position=(0.5 * width, y),
164                size=(0.0, 0.0),
165                text=bui.Lstr(resource=f'{self._r}.soundtrackDescriptionText'),
166                flatness=1.0,
167                h_align='center',
168                v_align='center',
169                maxwidth=400,
170                scale=0.5,
171                color=(0.7, 0.8, 0.7, 1.0),
172            )
173        else:
174            self._soundtrack_button = None
175
176        # Tweak a few navigation bits.
177        if self._back_button is not None:
178            bui.widget(edit=self._back_button, down_widget=svne.minusbutton)
179        else:
180            spback = bui.get_special_widget('back_button')
181            bui.widget(
182                edit=svne.minusbutton, up_widget=spback, left_widget=spback
183            )
184
185        self._restore_state()
186
187    @override
188    def get_main_window_state(self) -> bui.MainWindowState:
189        # Support recreating our window for back/refresh purposes.
190        cls = type(self)
191        return bui.BasicMainWindowState(
192            create_call=lambda transition, origin_widget: cls(
193                transition=transition, origin_widget=origin_widget
194            )
195        )
196
197    @override
198    def on_main_window_close(self) -> None:
199        self._save_state()
200
201    def _do_soundtracks(self) -> None:
202        # pylint: disable=cyclic-import
203        from bauiv1lib.soundtrack.browser import SoundtrackBrowserWindow
204
205        # no-op if we're not in control.
206        if not self.main_window_has_control():
207            return
208
209        # We require disk access for soundtracks; request it if we don't
210        # have it.
211        if not bui.have_permission(bui.Permission.STORAGE):
212            bui.getsound('ding').play()
213            bui.screenmessage(
214                bui.Lstr(resource='storagePermissionAccessText'),
215                color=(0.5, 1, 0.5),
216            )
217            bui.apptimer(
218                1.0, bui.Call(bui.request_permission, bui.Permission.STORAGE)
219            )
220            return
221
222        self.main_window_replace(
223            SoundtrackBrowserWindow(origin_widget=self._soundtrack_button)
224        )
225
226    def _save_state(self) -> None:
227        try:
228            sel = self._root_widget.get_selected_child()
229            if sel == self._sound_volume_numedit.minusbutton:
230                sel_name = 'SoundMinus'
231            elif sel == self._sound_volume_numedit.plusbutton:
232                sel_name = 'SoundPlus'
233            elif sel == self._music_volume_numedit.minusbutton:
234                sel_name = 'MusicMinus'
235            elif sel == self._music_volume_numedit.plusbutton:
236                sel_name = 'MusicPlus'
237            elif sel == self._soundtrack_button:
238                sel_name = 'Soundtrack'
239            elif sel == self._back_button:
240                sel_name = 'Back'
241            else:
242                raise ValueError(f'unrecognized selection \'{sel}\'')
243            assert bui.app.classic is not None
244            bui.app.ui_v1.window_states[type(self)] = sel_name
245        except Exception:
246            logging.exception('Error saving state for %s.', self)
247
248    def _restore_state(self) -> None:
249        try:
250            assert bui.app.classic is not None
251            sel_name = bui.app.ui_v1.window_states.get(type(self))
252            sel: bui.Widget | None
253            if sel_name == 'SoundMinus':
254                sel = self._sound_volume_numedit.minusbutton
255            elif sel_name == 'SoundPlus':
256                sel = self._sound_volume_numedit.plusbutton
257            elif sel_name == 'MusicMinus':
258                sel = self._music_volume_numedit.minusbutton
259            elif sel_name == 'MusicPlus':
260                sel = self._music_volume_numedit.plusbutton
261            elif sel_name == 'Soundtrack':
262                sel = self._soundtrack_button
263            elif sel_name == 'Back':
264                sel = self._back_button
265            else:
266                sel = self._back_button
267            if sel:
268                bui.containerwidget(edit=self._root_widget, selected_child=sel)
269        except Exception:
270            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-locals
 26        # pylint: disable=cyclic-import
 27        from bauiv1lib.config import ConfigNumberEdit
 28
 29        assert bui.app.classic is not None
 30        music = bui.app.classic.music
 31
 32        self._r = 'audioSettingsWindow'
 33
 34        spacing = 50.0
 35        uiscale = bui.app.ui_v1.uiscale
 36
 37        width = 1200.0 if uiscale is bui.UIScale.SMALL else 500.0
 38        height = 800.0 if uiscale is bui.UIScale.SMALL else 350.0
 39
 40        show_soundtracks = False
 41        if music.have_music_player():
 42            show_soundtracks = True
 43
 44        # Do some fancy math to fill all available screen area up to the
 45        # size of our backing container. This lets us fit to the exact
 46        # screen shape at small ui scale.
 47        screensize = bui.get_virtual_screen_size()
 48        scale = (
 49            2.2
 50            if uiscale is bui.UIScale.SMALL
 51            else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
 52        )
 53        # Calc screen size in our local container space and clamp to a
 54        # bit smaller than our container size.
 55        # target_width = min(width - 60, screensize[0] / scale)
 56        target_height = min(height - 70, screensize[1] / scale)
 57
 58        # To get top/left coords, go to the center of our window and
 59        # offset by half the width/height of our target area.
 60        yoffs = 0.5 * height + 0.5 * target_height + 30.0
 61
 62        super().__init__(
 63            root_widget=bui.containerwidget(
 64                size=(width, height),
 65                scale=scale,
 66                toolbar_visibility=(
 67                    'menu_minimal'
 68                    if uiscale is bui.UIScale.SMALL
 69                    else 'menu_full'
 70                ),
 71            ),
 72            transition=transition,
 73            origin_widget=origin_widget,
 74            # We're affected by screen size only at small ui-scale.
 75            refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL,
 76        )
 77
 78        if uiscale is bui.UIScale.SMALL:
 79            bui.containerwidget(
 80                edit=self._root_widget, on_cancel_call=self.main_window_back
 81            )
 82            self._back_button = None
 83        else:
 84            self._back_button = bui.buttonwidget(
 85                parent=self._root_widget,
 86                position=(35, yoffs - 55),
 87                size=(60, 60),
 88                scale=0.8,
 89                text_scale=1.2,
 90                label=bui.charstr(bui.SpecialChar.BACK),
 91                button_type='backSmall',
 92                on_activate_call=self.main_window_back,
 93                autoselect=True,
 94            )
 95            bui.containerwidget(
 96                edit=self._root_widget, cancel_button=self._back_button
 97            )
 98
 99        bui.textwidget(
100            parent=self._root_widget,
101            position=(
102                width * 0.5,
103                yoffs - (48 if uiscale is bui.UIScale.SMALL else 32),
104            ),
105            size=(0, 0),
106            text=bui.Lstr(resource=f'{self._r}.titleText'),
107            color=bui.app.ui_v1.title_color,
108            maxwidth=180,
109            h_align='center',
110            v_align='center',
111        )
112
113        # Roughly center everything else in our window.
114        x = width * 0.5 - 160
115        y = height * 0.5 + (100 if show_soundtracks else 70)
116        y -= spacing * 1.0
117
118        self._sound_volume_numedit = svne = ConfigNumberEdit(
119            parent=self._root_widget,
120            position=(x, y),
121            xoffset=10,
122            configkey='Sound Volume',
123            displayname=bui.Lstr(resource=f'{self._r}.soundVolumeText'),
124            minval=0.0,
125            maxval=1.0,
126            increment=0.05,
127            as_percent=True,
128        )
129        bui.widget(
130            edit=svne.plusbutton,
131            right_widget=bui.get_special_widget('squad_button'),
132        )
133        y -= spacing
134        self._music_volume_numedit = ConfigNumberEdit(
135            parent=self._root_widget,
136            position=(x, y),
137            xoffset=10,
138            configkey='Music Volume',
139            displayname=bui.Lstr(resource=f'{self._r}.musicVolumeText'),
140            minval=0.0,
141            maxval=1.0,
142            increment=0.05,
143            callback=music.music_volume_changed,
144            changesound=False,
145            as_percent=True,
146        )
147
148        y -= 0.5 * spacing
149
150        self._soundtrack_button: bui.Widget | None
151        if show_soundtracks:
152            y -= 1.2 * spacing
153            self._soundtrack_button = bui.buttonwidget(
154                parent=self._root_widget,
155                position=(width * 0.5 - 155, y),
156                size=(310, 50),
157                autoselect=True,
158                label=bui.Lstr(resource=f'{self._r}.soundtrackButtonText'),
159                on_activate_call=self._do_soundtracks,
160            )
161            y -= spacing * 0.3
162            bui.textwidget(
163                parent=self._root_widget,
164                position=(0.5 * width, y),
165                size=(0.0, 0.0),
166                text=bui.Lstr(resource=f'{self._r}.soundtrackDescriptionText'),
167                flatness=1.0,
168                h_align='center',
169                v_align='center',
170                maxwidth=400,
171                scale=0.5,
172                color=(0.7, 0.8, 0.7, 1.0),
173            )
174        else:
175            self._soundtrack_button = None
176
177        # Tweak a few navigation bits.
178        if self._back_button is not None:
179            bui.widget(edit=self._back_button, down_widget=svne.minusbutton)
180        else:
181            spback = bui.get_special_widget('back_button')
182            bui.widget(
183                edit=svne.minusbutton, up_widget=spback, left_widget=spback
184            )
185
186        self._restore_state()
187
188    @override
189    def get_main_window_state(self) -> bui.MainWindowState:
190        # Support recreating our window for back/refresh purposes.
191        cls = type(self)
192        return bui.BasicMainWindowState(
193            create_call=lambda transition, origin_widget: cls(
194                transition=transition, origin_widget=origin_widget
195            )
196        )
197
198    @override
199    def on_main_window_close(self) -> None:
200        self._save_state()
201
202    def _do_soundtracks(self) -> None:
203        # pylint: disable=cyclic-import
204        from bauiv1lib.soundtrack.browser import SoundtrackBrowserWindow
205
206        # no-op if we're not in control.
207        if not self.main_window_has_control():
208            return
209
210        # We require disk access for soundtracks; request it if we don't
211        # have it.
212        if not bui.have_permission(bui.Permission.STORAGE):
213            bui.getsound('ding').play()
214            bui.screenmessage(
215                bui.Lstr(resource='storagePermissionAccessText'),
216                color=(0.5, 1, 0.5),
217            )
218            bui.apptimer(
219                1.0, bui.Call(bui.request_permission, bui.Permission.STORAGE)
220            )
221            return
222
223        self.main_window_replace(
224            SoundtrackBrowserWindow(origin_widget=self._soundtrack_button)
225        )
226
227    def _save_state(self) -> None:
228        try:
229            sel = self._root_widget.get_selected_child()
230            if sel == self._sound_volume_numedit.minusbutton:
231                sel_name = 'SoundMinus'
232            elif sel == self._sound_volume_numedit.plusbutton:
233                sel_name = 'SoundPlus'
234            elif sel == self._music_volume_numedit.minusbutton:
235                sel_name = 'MusicMinus'
236            elif sel == self._music_volume_numedit.plusbutton:
237                sel_name = 'MusicPlus'
238            elif sel == self._soundtrack_button:
239                sel_name = 'Soundtrack'
240            elif sel == self._back_button:
241                sel_name = 'Back'
242            else:
243                raise ValueError(f'unrecognized selection \'{sel}\'')
244            assert bui.app.classic is not None
245            bui.app.ui_v1.window_states[type(self)] = sel_name
246        except Exception:
247            logging.exception('Error saving state for %s.', self)
248
249    def _restore_state(self) -> None:
250        try:
251            assert bui.app.classic is not None
252            sel_name = bui.app.ui_v1.window_states.get(type(self))
253            sel: bui.Widget | None
254            if sel_name == 'SoundMinus':
255                sel = self._sound_volume_numedit.minusbutton
256            elif sel_name == 'SoundPlus':
257                sel = self._sound_volume_numedit.plusbutton
258            elif sel_name == 'MusicMinus':
259                sel = self._music_volume_numedit.minusbutton
260            elif sel_name == 'MusicPlus':
261                sel = self._music_volume_numedit.plusbutton
262            elif sel_name == 'Soundtrack':
263                sel = self._soundtrack_button
264            elif sel_name == 'Back':
265                sel = self._back_button
266            else:
267                sel = self._back_button
268            if sel:
269                bui.containerwidget(edit=self._root_widget, selected_child=sel)
270        except Exception:
271            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-locals
 26        # pylint: disable=cyclic-import
 27        from bauiv1lib.config import ConfigNumberEdit
 28
 29        assert bui.app.classic is not None
 30        music = bui.app.classic.music
 31
 32        self._r = 'audioSettingsWindow'
 33
 34        spacing = 50.0
 35        uiscale = bui.app.ui_v1.uiscale
 36
 37        width = 1200.0 if uiscale is bui.UIScale.SMALL else 500.0
 38        height = 800.0 if uiscale is bui.UIScale.SMALL else 350.0
 39
 40        show_soundtracks = False
 41        if music.have_music_player():
 42            show_soundtracks = True
 43
 44        # Do some fancy math to fill all available screen area up to the
 45        # size of our backing container. This lets us fit to the exact
 46        # screen shape at small ui scale.
 47        screensize = bui.get_virtual_screen_size()
 48        scale = (
 49            2.2
 50            if uiscale is bui.UIScale.SMALL
 51            else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
 52        )
 53        # Calc screen size in our local container space and clamp to a
 54        # bit smaller than our container size.
 55        # target_width = min(width - 60, screensize[0] / scale)
 56        target_height = min(height - 70, screensize[1] / scale)
 57
 58        # To get top/left coords, go to the center of our window and
 59        # offset by half the width/height of our target area.
 60        yoffs = 0.5 * height + 0.5 * target_height + 30.0
 61
 62        super().__init__(
 63            root_widget=bui.containerwidget(
 64                size=(width, height),
 65                scale=scale,
 66                toolbar_visibility=(
 67                    'menu_minimal'
 68                    if uiscale is bui.UIScale.SMALL
 69                    else 'menu_full'
 70                ),
 71            ),
 72            transition=transition,
 73            origin_widget=origin_widget,
 74            # We're affected by screen size only at small ui-scale.
 75            refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL,
 76        )
 77
 78        if uiscale is bui.UIScale.SMALL:
 79            bui.containerwidget(
 80                edit=self._root_widget, on_cancel_call=self.main_window_back
 81            )
 82            self._back_button = None
 83        else:
 84            self._back_button = bui.buttonwidget(
 85                parent=self._root_widget,
 86                position=(35, yoffs - 55),
 87                size=(60, 60),
 88                scale=0.8,
 89                text_scale=1.2,
 90                label=bui.charstr(bui.SpecialChar.BACK),
 91                button_type='backSmall',
 92                on_activate_call=self.main_window_back,
 93                autoselect=True,
 94            )
 95            bui.containerwidget(
 96                edit=self._root_widget, cancel_button=self._back_button
 97            )
 98
 99        bui.textwidget(
100            parent=self._root_widget,
101            position=(
102                width * 0.5,
103                yoffs - (48 if uiscale is bui.UIScale.SMALL else 32),
104            ),
105            size=(0, 0),
106            text=bui.Lstr(resource=f'{self._r}.titleText'),
107            color=bui.app.ui_v1.title_color,
108            maxwidth=180,
109            h_align='center',
110            v_align='center',
111        )
112
113        # Roughly center everything else in our window.
114        x = width * 0.5 - 160
115        y = height * 0.5 + (100 if show_soundtracks else 70)
116        y -= spacing * 1.0
117
118        self._sound_volume_numedit = svne = ConfigNumberEdit(
119            parent=self._root_widget,
120            position=(x, y),
121            xoffset=10,
122            configkey='Sound Volume',
123            displayname=bui.Lstr(resource=f'{self._r}.soundVolumeText'),
124            minval=0.0,
125            maxval=1.0,
126            increment=0.05,
127            as_percent=True,
128        )
129        bui.widget(
130            edit=svne.plusbutton,
131            right_widget=bui.get_special_widget('squad_button'),
132        )
133        y -= spacing
134        self._music_volume_numedit = ConfigNumberEdit(
135            parent=self._root_widget,
136            position=(x, y),
137            xoffset=10,
138            configkey='Music Volume',
139            displayname=bui.Lstr(resource=f'{self._r}.musicVolumeText'),
140            minval=0.0,
141            maxval=1.0,
142            increment=0.05,
143            callback=music.music_volume_changed,
144            changesound=False,
145            as_percent=True,
146        )
147
148        y -= 0.5 * spacing
149
150        self._soundtrack_button: bui.Widget | None
151        if show_soundtracks:
152            y -= 1.2 * spacing
153            self._soundtrack_button = bui.buttonwidget(
154                parent=self._root_widget,
155                position=(width * 0.5 - 155, y),
156                size=(310, 50),
157                autoselect=True,
158                label=bui.Lstr(resource=f'{self._r}.soundtrackButtonText'),
159                on_activate_call=self._do_soundtracks,
160            )
161            y -= spacing * 0.3
162            bui.textwidget(
163                parent=self._root_widget,
164                position=(0.5 * width, y),
165                size=(0.0, 0.0),
166                text=bui.Lstr(resource=f'{self._r}.soundtrackDescriptionText'),
167                flatness=1.0,
168                h_align='center',
169                v_align='center',
170                maxwidth=400,
171                scale=0.5,
172                color=(0.7, 0.8, 0.7, 1.0),
173            )
174        else:
175            self._soundtrack_button = None
176
177        # Tweak a few navigation bits.
178        if self._back_button is not None:
179            bui.widget(edit=self._back_button, down_widget=svne.minusbutton)
180        else:
181            spback = bui.get_special_widget('back_button')
182            bui.widget(
183                edit=svne.minusbutton, up_widget=spback, left_widget=spback
184            )
185
186        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:
188    @override
189    def get_main_window_state(self) -> bui.MainWindowState:
190        # Support recreating our window for back/refresh purposes.
191        cls = type(self)
192        return bui.BasicMainWindowState(
193            create_call=lambda transition, origin_widget: cls(
194                transition=transition, origin_widget=origin_widget
195            )
196        )

Return a WindowState to recreate this window, if supported.

@override
def on_main_window_close(self) -> None:
198    @override
199    def on_main_window_close(self) -> None:
200        self._save_state()

Called before transitioning out a main window.

A good opportunity to save window state/etc.