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

Return a WindowState to recreate this window, if supported.

@override
def on_main_window_close(self) -> None:
209    @override
210    def on_main_window_close(self) -> None:
211        self._save_state()

Called before transitioning out a main window.

A good opportunity to save window state/etc.