bauiv1lib.settings.allsettings

UI for top level settings categories.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""UI for top level settings categories."""
  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 AllSettingsWindow(bui.MainWindow):
 17    """Window for selecting a settings category."""
 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
 27        # Preload some modules we use in a background thread so we won't
 28        # have a visual hitch when the user taps them.
 29        bui.app.threadpool_submit_no_wait(self._preload_modules)
 30
 31        bui.set_analytics_screen('Settings Window')
 32        assert bui.app.classic is not None
 33        uiscale = bui.app.ui_v1.uiscale
 34        width = 1000 if uiscale is bui.UIScale.SMALL else 580
 35        x_inset = 125 if uiscale is bui.UIScale.SMALL else 0
 36        height = 500 if uiscale is bui.UIScale.SMALL else 435
 37        self._r = 'settingsWindow'
 38        top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
 39        yoffs = -30 if uiscale is bui.UIScale.SMALL else 0
 40
 41        uiscale = bui.app.ui_v1.uiscale
 42        super().__init__(
 43            root_widget=bui.containerwidget(
 44                size=(width, height + top_extra),
 45                toolbar_visibility=(
 46                    'menu_minimal'
 47                    if uiscale is bui.UIScale.SMALL
 48                    else 'menu_full'
 49                ),
 50                scale=(
 51                    1.5
 52                    if uiscale is bui.UIScale.SMALL
 53                    else 1.25 if uiscale is bui.UIScale.MEDIUM else 1.0
 54                ),
 55                stack_offset=(
 56                    (0, 0) if uiscale is bui.UIScale.SMALL else (0, 0)
 57                ),
 58            ),
 59            transition=transition,
 60            origin_widget=origin_widget,
 61        )
 62
 63        if uiscale is bui.UIScale.SMALL:
 64            self._back_button = None
 65            bui.containerwidget(
 66                edit=self._root_widget, on_cancel_call=self.main_window_back
 67            )
 68        else:
 69            self._back_button = btn = bui.buttonwidget(
 70                parent=self._root_widget,
 71                autoselect=True,
 72                position=(40 + x_inset, height - 55 + yoffs),
 73                size=(130, 60),
 74                scale=0.8,
 75                text_scale=1.2,
 76                label=bui.Lstr(resource='backText'),
 77                button_type='back',
 78                on_activate_call=self.main_window_back,
 79            )
 80            bui.containerwidget(edit=self._root_widget, cancel_button=btn)
 81
 82        bui.textwidget(
 83            parent=self._root_widget,
 84            position=(0, height - 44 + yoffs),
 85            size=(width, 25),
 86            text=bui.Lstr(resource=f'{self._r}.titleText'),
 87            color=bui.app.ui_v1.title_color,
 88            h_align='center',
 89            v_align='center',
 90            maxwidth=130,
 91        )
 92
 93        if self._back_button is not None:
 94            bui.buttonwidget(
 95                edit=self._back_button,
 96                button_type='backSmall',
 97                size=(60, 60),
 98                label=bui.charstr(bui.SpecialChar.BACK),
 99            )
100
101        v = height - 80 + yoffs
102        v -= 145
103
104        basew = 280 if uiscale is bui.UIScale.SMALL else 230
105        baseh = 170
106        x_offs = (
107            x_inset + (105 if uiscale is bui.UIScale.SMALL else 72) - basew
108        )  # now unused
109        x_offs2 = x_offs + basew - 7
110        x_offs3 = x_offs + 2 * (basew - 7)
111        x_offs4 = x_offs2
112        x_offs5 = x_offs3
113
114        def _b_title(
115            x: float, y: float, button: bui.Widget, text: str | bui.Lstr
116        ) -> None:
117            bui.textwidget(
118                parent=self._root_widget,
119                text=text,
120                position=(x + basew * 0.47, y + baseh * 0.22),
121                maxwidth=basew * 0.7,
122                size=(0, 0),
123                h_align='center',
124                v_align='center',
125                draw_controller=button,
126                color=(0.7, 0.9, 0.7, 1.0),
127            )
128
129        ctb = self._controllers_button = bui.buttonwidget(
130            parent=self._root_widget,
131            autoselect=True,
132            position=(x_offs2, v),
133            size=(basew, baseh),
134            button_type='square',
135            label='',
136            on_activate_call=self._do_controllers,
137        )
138        if self._back_button is None:
139            bbtn = bui.get_special_widget('back_button')
140            bui.widget(edit=ctb, left_widget=bbtn)
141        _b_title(
142            x_offs2, v, ctb, bui.Lstr(resource=f'{self._r}.controllersText')
143        )
144        imgw = imgh = 130
145        bui.imagewidget(
146            parent=self._root_widget,
147            position=(x_offs2 + basew * 0.49 - imgw * 0.5, v + 35),
148            size=(imgw, imgh),
149            texture=bui.gettexture('controllerIcon'),
150            draw_controller=ctb,
151        )
152
153        gfxb = self._graphics_button = bui.buttonwidget(
154            parent=self._root_widget,
155            autoselect=True,
156            position=(x_offs3, v),
157            size=(basew, baseh),
158            button_type='square',
159            label='',
160            on_activate_call=self._do_graphics,
161        )
162        pbtn = bui.get_special_widget('squad_button')
163        bui.widget(edit=gfxb, up_widget=pbtn, right_widget=pbtn)
164        _b_title(x_offs3, v, gfxb, bui.Lstr(resource=f'{self._r}.graphicsText'))
165        imgw = imgh = 110
166        bui.imagewidget(
167            parent=self._root_widget,
168            position=(x_offs3 + basew * 0.49 - imgw * 0.5, v + 42),
169            size=(imgw, imgh),
170            texture=bui.gettexture('graphicsIcon'),
171            draw_controller=gfxb,
172        )
173
174        v -= baseh - 5
175
176        abtn = self._audio_button = bui.buttonwidget(
177            parent=self._root_widget,
178            autoselect=True,
179            position=(x_offs4, v),
180            size=(basew, baseh),
181            button_type='square',
182            label='',
183            on_activate_call=self._do_audio,
184        )
185        _b_title(x_offs4, v, abtn, bui.Lstr(resource=f'{self._r}.audioText'))
186        imgw = imgh = 120
187        bui.imagewidget(
188            parent=self._root_widget,
189            position=(x_offs4 + basew * 0.49 - imgw * 0.5 + 5, v + 35),
190            size=(imgw, imgh),
191            color=(1, 1, 0),
192            texture=bui.gettexture('audioIcon'),
193            draw_controller=abtn,
194        )
195
196        avb = self._advanced_button = bui.buttonwidget(
197            parent=self._root_widget,
198            autoselect=True,
199            position=(x_offs5, v),
200            size=(basew, baseh),
201            button_type='square',
202            label='',
203            on_activate_call=self._do_advanced,
204        )
205        _b_title(x_offs5, v, avb, bui.Lstr(resource=f'{self._r}.advancedText'))
206        imgw = imgh = 120
207        bui.imagewidget(
208            parent=self._root_widget,
209            position=(x_offs5 + basew * 0.49 - imgw * 0.5 + 5, v + 35),
210            size=(imgw, imgh),
211            color=(0.8, 0.95, 1),
212            texture=bui.gettexture('advancedIcon'),
213            draw_controller=avb,
214        )
215        self._restore_state()
216
217    @override
218    def get_main_window_state(self) -> bui.MainWindowState:
219        # Support recreating our window for back/refresh purposes.
220        cls = type(self)
221        return bui.BasicMainWindowState(
222            create_call=lambda transition, origin_widget: cls(
223                transition=transition, origin_widget=origin_widget
224            )
225        )
226
227    @override
228    def on_main_window_close(self) -> None:
229        self._save_state()
230
231    @staticmethod
232    def _preload_modules() -> None:
233        """Preload modules we use; avoids hitches (called in bg thread)."""
234        import bauiv1lib.mainmenu as _unused1
235        import bauiv1lib.settings.controls as _unused2
236        import bauiv1lib.settings.graphics as _unused3
237        import bauiv1lib.settings.audio as _unused4
238        import bauiv1lib.settings.advanced as _unused5
239
240    def _do_controllers(self) -> None:
241        # pylint: disable=cyclic-import
242        from bauiv1lib.settings.controls import ControlsSettingsWindow
243
244        # no-op if we're not in control.
245        if not self.main_window_has_control():
246            return
247
248        self.main_window_replace(
249            ControlsSettingsWindow(origin_widget=self._controllers_button)
250        )
251
252    def _do_graphics(self) -> None:
253        # pylint: disable=cyclic-import
254        from bauiv1lib.settings.graphics import GraphicsSettingsWindow
255
256        # no-op if we're not in control.
257        if not self.main_window_has_control():
258            return
259
260        self.main_window_replace(
261            GraphicsSettingsWindow(origin_widget=self._graphics_button)
262        )
263
264    def _do_audio(self) -> None:
265        # pylint: disable=cyclic-import
266        from bauiv1lib.settings.audio import AudioSettingsWindow
267
268        # no-op if we're not in control.
269        if not self.main_window_has_control():
270            return
271
272        self.main_window_replace(
273            AudioSettingsWindow(origin_widget=self._audio_button)
274        )
275
276    def _do_advanced(self) -> None:
277        # pylint: disable=cyclic-import
278        from bauiv1lib.settings.advanced import AdvancedSettingsWindow
279
280        # no-op if we're not in control.
281        if not self.main_window_has_control():
282            return
283
284        self.main_window_replace(
285            AdvancedSettingsWindow(origin_widget=self._advanced_button)
286        )
287
288    def _save_state(self) -> None:
289        try:
290            sel = self._root_widget.get_selected_child()
291            if sel == self._controllers_button:
292                sel_name = 'Controllers'
293            elif sel == self._graphics_button:
294                sel_name = 'Graphics'
295            elif sel == self._audio_button:
296                sel_name = 'Audio'
297            elif sel == self._advanced_button:
298                sel_name = 'Advanced'
299            elif sel == self._back_button:
300                sel_name = 'Back'
301            else:
302                raise ValueError(f'unrecognized selection \'{sel}\'')
303            assert bui.app.classic is not None
304            bui.app.ui_v1.window_states[type(self)] = {'sel_name': sel_name}
305        except Exception:
306            logging.exception('Error saving state for %s.', self)
307
308    def _restore_state(self) -> None:
309        try:
310            assert bui.app.classic is not None
311            sel_name = bui.app.ui_v1.window_states.get(type(self), {}).get(
312                'sel_name'
313            )
314            sel: bui.Widget | None
315            if sel_name == 'Controllers':
316                sel = self._controllers_button
317            elif sel_name == 'Graphics':
318                sel = self._graphics_button
319            elif sel_name == 'Audio':
320                sel = self._audio_button
321            elif sel_name == 'Advanced':
322                sel = self._advanced_button
323            elif sel_name == 'Back':
324                sel = self._back_button
325            else:
326                sel = self._controllers_button
327            if sel is not None:
328                bui.containerwidget(edit=self._root_widget, selected_child=sel)
329        except Exception:
330            logging.exception('Error restoring state for %s.', self)
class AllSettingsWindow(bauiv1._uitypes.MainWindow):
 17class AllSettingsWindow(bui.MainWindow):
 18    """Window for selecting a settings category."""
 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
 28        # Preload some modules we use in a background thread so we won't
 29        # have a visual hitch when the user taps them.
 30        bui.app.threadpool_submit_no_wait(self._preload_modules)
 31
 32        bui.set_analytics_screen('Settings Window')
 33        assert bui.app.classic is not None
 34        uiscale = bui.app.ui_v1.uiscale
 35        width = 1000 if uiscale is bui.UIScale.SMALL else 580
 36        x_inset = 125 if uiscale is bui.UIScale.SMALL else 0
 37        height = 500 if uiscale is bui.UIScale.SMALL else 435
 38        self._r = 'settingsWindow'
 39        top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
 40        yoffs = -30 if uiscale is bui.UIScale.SMALL else 0
 41
 42        uiscale = bui.app.ui_v1.uiscale
 43        super().__init__(
 44            root_widget=bui.containerwidget(
 45                size=(width, height + top_extra),
 46                toolbar_visibility=(
 47                    'menu_minimal'
 48                    if uiscale is bui.UIScale.SMALL
 49                    else 'menu_full'
 50                ),
 51                scale=(
 52                    1.5
 53                    if uiscale is bui.UIScale.SMALL
 54                    else 1.25 if uiscale is bui.UIScale.MEDIUM else 1.0
 55                ),
 56                stack_offset=(
 57                    (0, 0) if uiscale is bui.UIScale.SMALL else (0, 0)
 58                ),
 59            ),
 60            transition=transition,
 61            origin_widget=origin_widget,
 62        )
 63
 64        if uiscale is bui.UIScale.SMALL:
 65            self._back_button = None
 66            bui.containerwidget(
 67                edit=self._root_widget, on_cancel_call=self.main_window_back
 68            )
 69        else:
 70            self._back_button = btn = bui.buttonwidget(
 71                parent=self._root_widget,
 72                autoselect=True,
 73                position=(40 + x_inset, height - 55 + yoffs),
 74                size=(130, 60),
 75                scale=0.8,
 76                text_scale=1.2,
 77                label=bui.Lstr(resource='backText'),
 78                button_type='back',
 79                on_activate_call=self.main_window_back,
 80            )
 81            bui.containerwidget(edit=self._root_widget, cancel_button=btn)
 82
 83        bui.textwidget(
 84            parent=self._root_widget,
 85            position=(0, height - 44 + yoffs),
 86            size=(width, 25),
 87            text=bui.Lstr(resource=f'{self._r}.titleText'),
 88            color=bui.app.ui_v1.title_color,
 89            h_align='center',
 90            v_align='center',
 91            maxwidth=130,
 92        )
 93
 94        if self._back_button is not None:
 95            bui.buttonwidget(
 96                edit=self._back_button,
 97                button_type='backSmall',
 98                size=(60, 60),
 99                label=bui.charstr(bui.SpecialChar.BACK),
100            )
101
102        v = height - 80 + yoffs
103        v -= 145
104
105        basew = 280 if uiscale is bui.UIScale.SMALL else 230
106        baseh = 170
107        x_offs = (
108            x_inset + (105 if uiscale is bui.UIScale.SMALL else 72) - basew
109        )  # now unused
110        x_offs2 = x_offs + basew - 7
111        x_offs3 = x_offs + 2 * (basew - 7)
112        x_offs4 = x_offs2
113        x_offs5 = x_offs3
114
115        def _b_title(
116            x: float, y: float, button: bui.Widget, text: str | bui.Lstr
117        ) -> None:
118            bui.textwidget(
119                parent=self._root_widget,
120                text=text,
121                position=(x + basew * 0.47, y + baseh * 0.22),
122                maxwidth=basew * 0.7,
123                size=(0, 0),
124                h_align='center',
125                v_align='center',
126                draw_controller=button,
127                color=(0.7, 0.9, 0.7, 1.0),
128            )
129
130        ctb = self._controllers_button = bui.buttonwidget(
131            parent=self._root_widget,
132            autoselect=True,
133            position=(x_offs2, v),
134            size=(basew, baseh),
135            button_type='square',
136            label='',
137            on_activate_call=self._do_controllers,
138        )
139        if self._back_button is None:
140            bbtn = bui.get_special_widget('back_button')
141            bui.widget(edit=ctb, left_widget=bbtn)
142        _b_title(
143            x_offs2, v, ctb, bui.Lstr(resource=f'{self._r}.controllersText')
144        )
145        imgw = imgh = 130
146        bui.imagewidget(
147            parent=self._root_widget,
148            position=(x_offs2 + basew * 0.49 - imgw * 0.5, v + 35),
149            size=(imgw, imgh),
150            texture=bui.gettexture('controllerIcon'),
151            draw_controller=ctb,
152        )
153
154        gfxb = self._graphics_button = bui.buttonwidget(
155            parent=self._root_widget,
156            autoselect=True,
157            position=(x_offs3, v),
158            size=(basew, baseh),
159            button_type='square',
160            label='',
161            on_activate_call=self._do_graphics,
162        )
163        pbtn = bui.get_special_widget('squad_button')
164        bui.widget(edit=gfxb, up_widget=pbtn, right_widget=pbtn)
165        _b_title(x_offs3, v, gfxb, bui.Lstr(resource=f'{self._r}.graphicsText'))
166        imgw = imgh = 110
167        bui.imagewidget(
168            parent=self._root_widget,
169            position=(x_offs3 + basew * 0.49 - imgw * 0.5, v + 42),
170            size=(imgw, imgh),
171            texture=bui.gettexture('graphicsIcon'),
172            draw_controller=gfxb,
173        )
174
175        v -= baseh - 5
176
177        abtn = self._audio_button = bui.buttonwidget(
178            parent=self._root_widget,
179            autoselect=True,
180            position=(x_offs4, v),
181            size=(basew, baseh),
182            button_type='square',
183            label='',
184            on_activate_call=self._do_audio,
185        )
186        _b_title(x_offs4, v, abtn, bui.Lstr(resource=f'{self._r}.audioText'))
187        imgw = imgh = 120
188        bui.imagewidget(
189            parent=self._root_widget,
190            position=(x_offs4 + basew * 0.49 - imgw * 0.5 + 5, v + 35),
191            size=(imgw, imgh),
192            color=(1, 1, 0),
193            texture=bui.gettexture('audioIcon'),
194            draw_controller=abtn,
195        )
196
197        avb = self._advanced_button = bui.buttonwidget(
198            parent=self._root_widget,
199            autoselect=True,
200            position=(x_offs5, v),
201            size=(basew, baseh),
202            button_type='square',
203            label='',
204            on_activate_call=self._do_advanced,
205        )
206        _b_title(x_offs5, v, avb, bui.Lstr(resource=f'{self._r}.advancedText'))
207        imgw = imgh = 120
208        bui.imagewidget(
209            parent=self._root_widget,
210            position=(x_offs5 + basew * 0.49 - imgw * 0.5 + 5, v + 35),
211            size=(imgw, imgh),
212            color=(0.8, 0.95, 1),
213            texture=bui.gettexture('advancedIcon'),
214            draw_controller=avb,
215        )
216        self._restore_state()
217
218    @override
219    def get_main_window_state(self) -> bui.MainWindowState:
220        # Support recreating our window for back/refresh purposes.
221        cls = type(self)
222        return bui.BasicMainWindowState(
223            create_call=lambda transition, origin_widget: cls(
224                transition=transition, origin_widget=origin_widget
225            )
226        )
227
228    @override
229    def on_main_window_close(self) -> None:
230        self._save_state()
231
232    @staticmethod
233    def _preload_modules() -> None:
234        """Preload modules we use; avoids hitches (called in bg thread)."""
235        import bauiv1lib.mainmenu as _unused1
236        import bauiv1lib.settings.controls as _unused2
237        import bauiv1lib.settings.graphics as _unused3
238        import bauiv1lib.settings.audio as _unused4
239        import bauiv1lib.settings.advanced as _unused5
240
241    def _do_controllers(self) -> None:
242        # pylint: disable=cyclic-import
243        from bauiv1lib.settings.controls import ControlsSettingsWindow
244
245        # no-op if we're not in control.
246        if not self.main_window_has_control():
247            return
248
249        self.main_window_replace(
250            ControlsSettingsWindow(origin_widget=self._controllers_button)
251        )
252
253    def _do_graphics(self) -> None:
254        # pylint: disable=cyclic-import
255        from bauiv1lib.settings.graphics import GraphicsSettingsWindow
256
257        # no-op if we're not in control.
258        if not self.main_window_has_control():
259            return
260
261        self.main_window_replace(
262            GraphicsSettingsWindow(origin_widget=self._graphics_button)
263        )
264
265    def _do_audio(self) -> None:
266        # pylint: disable=cyclic-import
267        from bauiv1lib.settings.audio import AudioSettingsWindow
268
269        # no-op if we're not in control.
270        if not self.main_window_has_control():
271            return
272
273        self.main_window_replace(
274            AudioSettingsWindow(origin_widget=self._audio_button)
275        )
276
277    def _do_advanced(self) -> None:
278        # pylint: disable=cyclic-import
279        from bauiv1lib.settings.advanced import AdvancedSettingsWindow
280
281        # no-op if we're not in control.
282        if not self.main_window_has_control():
283            return
284
285        self.main_window_replace(
286            AdvancedSettingsWindow(origin_widget=self._advanced_button)
287        )
288
289    def _save_state(self) -> None:
290        try:
291            sel = self._root_widget.get_selected_child()
292            if sel == self._controllers_button:
293                sel_name = 'Controllers'
294            elif sel == self._graphics_button:
295                sel_name = 'Graphics'
296            elif sel == self._audio_button:
297                sel_name = 'Audio'
298            elif sel == self._advanced_button:
299                sel_name = 'Advanced'
300            elif sel == self._back_button:
301                sel_name = 'Back'
302            else:
303                raise ValueError(f'unrecognized selection \'{sel}\'')
304            assert bui.app.classic is not None
305            bui.app.ui_v1.window_states[type(self)] = {'sel_name': sel_name}
306        except Exception:
307            logging.exception('Error saving state for %s.', self)
308
309    def _restore_state(self) -> None:
310        try:
311            assert bui.app.classic is not None
312            sel_name = bui.app.ui_v1.window_states.get(type(self), {}).get(
313                'sel_name'
314            )
315            sel: bui.Widget | None
316            if sel_name == 'Controllers':
317                sel = self._controllers_button
318            elif sel_name == 'Graphics':
319                sel = self._graphics_button
320            elif sel_name == 'Audio':
321                sel = self._audio_button
322            elif sel_name == 'Advanced':
323                sel = self._advanced_button
324            elif sel_name == 'Back':
325                sel = self._back_button
326            else:
327                sel = self._controllers_button
328            if sel is not None:
329                bui.containerwidget(edit=self._root_widget, selected_child=sel)
330        except Exception:
331            logging.exception('Error restoring state for %s.', self)

Window for selecting a settings category.

AllSettingsWindow( 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
 28        # Preload some modules we use in a background thread so we won't
 29        # have a visual hitch when the user taps them.
 30        bui.app.threadpool_submit_no_wait(self._preload_modules)
 31
 32        bui.set_analytics_screen('Settings Window')
 33        assert bui.app.classic is not None
 34        uiscale = bui.app.ui_v1.uiscale
 35        width = 1000 if uiscale is bui.UIScale.SMALL else 580
 36        x_inset = 125 if uiscale is bui.UIScale.SMALL else 0
 37        height = 500 if uiscale is bui.UIScale.SMALL else 435
 38        self._r = 'settingsWindow'
 39        top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
 40        yoffs = -30 if uiscale is bui.UIScale.SMALL else 0
 41
 42        uiscale = bui.app.ui_v1.uiscale
 43        super().__init__(
 44            root_widget=bui.containerwidget(
 45                size=(width, height + top_extra),
 46                toolbar_visibility=(
 47                    'menu_minimal'
 48                    if uiscale is bui.UIScale.SMALL
 49                    else 'menu_full'
 50                ),
 51                scale=(
 52                    1.5
 53                    if uiscale is bui.UIScale.SMALL
 54                    else 1.25 if uiscale is bui.UIScale.MEDIUM else 1.0
 55                ),
 56                stack_offset=(
 57                    (0, 0) if uiscale is bui.UIScale.SMALL else (0, 0)
 58                ),
 59            ),
 60            transition=transition,
 61            origin_widget=origin_widget,
 62        )
 63
 64        if uiscale is bui.UIScale.SMALL:
 65            self._back_button = None
 66            bui.containerwidget(
 67                edit=self._root_widget, on_cancel_call=self.main_window_back
 68            )
 69        else:
 70            self._back_button = btn = bui.buttonwidget(
 71                parent=self._root_widget,
 72                autoselect=True,
 73                position=(40 + x_inset, height - 55 + yoffs),
 74                size=(130, 60),
 75                scale=0.8,
 76                text_scale=1.2,
 77                label=bui.Lstr(resource='backText'),
 78                button_type='back',
 79                on_activate_call=self.main_window_back,
 80            )
 81            bui.containerwidget(edit=self._root_widget, cancel_button=btn)
 82
 83        bui.textwidget(
 84            parent=self._root_widget,
 85            position=(0, height - 44 + yoffs),
 86            size=(width, 25),
 87            text=bui.Lstr(resource=f'{self._r}.titleText'),
 88            color=bui.app.ui_v1.title_color,
 89            h_align='center',
 90            v_align='center',
 91            maxwidth=130,
 92        )
 93
 94        if self._back_button is not None:
 95            bui.buttonwidget(
 96                edit=self._back_button,
 97                button_type='backSmall',
 98                size=(60, 60),
 99                label=bui.charstr(bui.SpecialChar.BACK),
100            )
101
102        v = height - 80 + yoffs
103        v -= 145
104
105        basew = 280 if uiscale is bui.UIScale.SMALL else 230
106        baseh = 170
107        x_offs = (
108            x_inset + (105 if uiscale is bui.UIScale.SMALL else 72) - basew
109        )  # now unused
110        x_offs2 = x_offs + basew - 7
111        x_offs3 = x_offs + 2 * (basew - 7)
112        x_offs4 = x_offs2
113        x_offs5 = x_offs3
114
115        def _b_title(
116            x: float, y: float, button: bui.Widget, text: str | bui.Lstr
117        ) -> None:
118            bui.textwidget(
119                parent=self._root_widget,
120                text=text,
121                position=(x + basew * 0.47, y + baseh * 0.22),
122                maxwidth=basew * 0.7,
123                size=(0, 0),
124                h_align='center',
125                v_align='center',
126                draw_controller=button,
127                color=(0.7, 0.9, 0.7, 1.0),
128            )
129
130        ctb = self._controllers_button = bui.buttonwidget(
131            parent=self._root_widget,
132            autoselect=True,
133            position=(x_offs2, v),
134            size=(basew, baseh),
135            button_type='square',
136            label='',
137            on_activate_call=self._do_controllers,
138        )
139        if self._back_button is None:
140            bbtn = bui.get_special_widget('back_button')
141            bui.widget(edit=ctb, left_widget=bbtn)
142        _b_title(
143            x_offs2, v, ctb, bui.Lstr(resource=f'{self._r}.controllersText')
144        )
145        imgw = imgh = 130
146        bui.imagewidget(
147            parent=self._root_widget,
148            position=(x_offs2 + basew * 0.49 - imgw * 0.5, v + 35),
149            size=(imgw, imgh),
150            texture=bui.gettexture('controllerIcon'),
151            draw_controller=ctb,
152        )
153
154        gfxb = self._graphics_button = bui.buttonwidget(
155            parent=self._root_widget,
156            autoselect=True,
157            position=(x_offs3, v),
158            size=(basew, baseh),
159            button_type='square',
160            label='',
161            on_activate_call=self._do_graphics,
162        )
163        pbtn = bui.get_special_widget('squad_button')
164        bui.widget(edit=gfxb, up_widget=pbtn, right_widget=pbtn)
165        _b_title(x_offs3, v, gfxb, bui.Lstr(resource=f'{self._r}.graphicsText'))
166        imgw = imgh = 110
167        bui.imagewidget(
168            parent=self._root_widget,
169            position=(x_offs3 + basew * 0.49 - imgw * 0.5, v + 42),
170            size=(imgw, imgh),
171            texture=bui.gettexture('graphicsIcon'),
172            draw_controller=gfxb,
173        )
174
175        v -= baseh - 5
176
177        abtn = self._audio_button = bui.buttonwidget(
178            parent=self._root_widget,
179            autoselect=True,
180            position=(x_offs4, v),
181            size=(basew, baseh),
182            button_type='square',
183            label='',
184            on_activate_call=self._do_audio,
185        )
186        _b_title(x_offs4, v, abtn, bui.Lstr(resource=f'{self._r}.audioText'))
187        imgw = imgh = 120
188        bui.imagewidget(
189            parent=self._root_widget,
190            position=(x_offs4 + basew * 0.49 - imgw * 0.5 + 5, v + 35),
191            size=(imgw, imgh),
192            color=(1, 1, 0),
193            texture=bui.gettexture('audioIcon'),
194            draw_controller=abtn,
195        )
196
197        avb = self._advanced_button = bui.buttonwidget(
198            parent=self._root_widget,
199            autoselect=True,
200            position=(x_offs5, v),
201            size=(basew, baseh),
202            button_type='square',
203            label='',
204            on_activate_call=self._do_advanced,
205        )
206        _b_title(x_offs5, v, avb, bui.Lstr(resource=f'{self._r}.advancedText'))
207        imgw = imgh = 120
208        bui.imagewidget(
209            parent=self._root_widget,
210            position=(x_offs5 + basew * 0.49 - imgw * 0.5 + 5, v + 35),
211            size=(imgw, imgh),
212            color=(0.8, 0.95, 1),
213            texture=bui.gettexture('advancedIcon'),
214            draw_controller=avb,
215        )
216        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:
218    @override
219    def get_main_window_state(self) -> bui.MainWindowState:
220        # Support recreating our window for back/refresh purposes.
221        cls = type(self)
222        return bui.BasicMainWindowState(
223            create_call=lambda transition, origin_widget: cls(
224                transition=transition, origin_widget=origin_widget
225            )
226        )

Return a WindowState to recreate this window, if supported.

@override
def on_main_window_close(self) -> None:
228    @override
229    def on_main_window_close(self) -> None:
230        self._save_state()

Called before transitioning out a main window.

A good opportunity to save window state/etc.