bauiv1lib.playlist.addgame

Provides a window for selecting a game type to add to a playlist.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides a window for selecting a game type to add to a playlist."""
  4
  5from __future__ import annotations
  6
  7from typing import TYPE_CHECKING, override
  8
  9import bascenev1 as bs
 10import bauiv1 as bui
 11
 12if TYPE_CHECKING:
 13    from bauiv1lib.playlist.editcontroller import PlaylistEditController
 14
 15
 16class PlaylistAddGameWindow(bui.MainWindow):
 17    """Window for selecting a game type to add to a playlist."""
 18
 19    def __init__(
 20        self,
 21        editcontroller: PlaylistEditController,
 22        transition: str | None = 'in_right',
 23        origin_widget: bui.Widget | None = None,
 24    ):
 25        self._editcontroller = editcontroller
 26        self._r = 'addGameWindow'
 27        assert bui.app.classic is not None
 28        uiscale = bui.app.ui_v1.uiscale
 29        self._width = 750 if uiscale is bui.UIScale.SMALL else 650
 30        x_inset = 50 if uiscale is bui.UIScale.SMALL else 0
 31        yoffs = -44 if uiscale is bui.UIScale.SMALL else 0
 32
 33        self._height = (
 34            400
 35            if uiscale is bui.UIScale.SMALL
 36            else 400 if uiscale is bui.UIScale.MEDIUM else 460
 37        )
 38        self._scroll_width = 210
 39
 40        super().__init__(
 41            root_widget=bui.containerwidget(
 42                size=(self._width, self._height),
 43                scale=(
 44                    1.95
 45                    if uiscale is bui.UIScale.SMALL
 46                    else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
 47                ),
 48                stack_offset=(0, 1) if uiscale is bui.UIScale.SMALL else (0, 0),
 49                toolbar_visibility='menu_minimal',
 50            ),
 51            transition=transition,
 52            origin_widget=origin_widget,
 53        )
 54
 55        if uiscale is bui.UIScale.SMALL:
 56            self._back_button = bui.get_special_widget('back_button')
 57        else:
 58            self._back_button = bui.buttonwidget(
 59                parent=self._root_widget,
 60                position=(58 + x_inset, self._height - 53 + yoffs),
 61                size=(60, 48),
 62                label=bui.charstr(bui.SpecialChar.BACK),
 63                autoselect=True,
 64                button_type='backSmall',
 65                on_activate_call=self.main_window_back,
 66            )
 67        self._select_button = select_button = bui.buttonwidget(
 68            parent=self._root_widget,
 69            position=(self._width - (172 + x_inset), self._height - 50 + yoffs),
 70            autoselect=True,
 71            size=(160, 60),
 72            scale=0.75,
 73            text_scale=1.2,
 74            label=bui.Lstr(resource='selectText'),
 75            on_activate_call=self._add,
 76        )
 77
 78        bui.widget(
 79            edit=select_button,
 80            right_widget=bui.get_special_widget('squad_button'),
 81        )
 82
 83        bui.textwidget(
 84            parent=self._root_widget,
 85            position=(self._width * 0.5, self._height - 28 + yoffs),
 86            size=(0, 0),
 87            scale=1.0,
 88            text=bui.Lstr(resource=f'{self._r}.titleText'),
 89            h_align='center',
 90            color=bui.app.ui_v1.title_color,
 91            maxwidth=250,
 92            v_align='center',
 93        )
 94        v = self._height - 64 + yoffs
 95
 96        self._selected_title_text = bui.textwidget(
 97            parent=self._root_widget,
 98            position=(x_inset + self._scroll_width + 50 + 30, v - 15),
 99            size=(0, 0),
100            scale=1.0,
101            color=(0.7, 1.0, 0.7, 1.0),
102            maxwidth=self._width - self._scroll_width - 150 - x_inset * 2,
103            h_align='left',
104            v_align='center',
105        )
106        v -= 30
107
108        self._selected_description_text = bui.textwidget(
109            parent=self._root_widget,
110            position=(x_inset + self._scroll_width + 50 + 30, v),
111            size=(0, 0),
112            scale=0.7,
113            color=(0.5, 0.8, 0.5, 1.0),
114            maxwidth=self._width - self._scroll_width - 150 - x_inset * 2,
115            h_align='left',
116        )
117
118        scroll_height = self._height - (
119            160 if uiscale is bui.UIScale.SMALL else 100
120        )
121
122        v = self._height - 60 + yoffs
123
124        self._scrollwidget = bui.scrollwidget(
125            parent=self._root_widget,
126            position=(x_inset + 61, v - scroll_height),
127            size=(self._scroll_width, scroll_height),
128            highlight=False,
129        )
130        bui.widget(
131            edit=self._scrollwidget,
132            up_widget=self._back_button,
133            left_widget=self._back_button,
134            right_widget=select_button,
135        )
136        self._column: bui.Widget | None = None
137
138        v -= 35
139
140        if uiscale is bui.UIScale.SMALL:
141            bui.containerwidget(
142                edit=self._root_widget, on_cancel_call=self.main_window_back
143            )
144        else:
145            bui.containerwidget(
146                edit=self._root_widget,
147                cancel_button=self._back_button,
148            )
149        bui.containerwidget(edit=self._root_widget, start_button=select_button)
150
151        self._selected_game_type: type[bs.GameActivity] | None = None
152
153        bui.containerwidget(
154            edit=self._root_widget, selected_child=self._scrollwidget
155        )
156
157        self._game_types: list[type[bs.GameActivity]] = []
158
159        # Get actual games loading in the bg.
160        bui.app.meta.load_exported_classes(
161            bs.GameActivity,
162            self._on_game_types_loaded,
163            completion_cb_in_bg_thread=True,
164        )
165
166        # Refresh with our initial empty list. We'll refresh again once
167        # game loading is complete.
168        self._refresh()
169
170    @override
171    def get_main_window_state(self) -> bui.MainWindowState:
172        # Support recreating our window for back/refresh purposes.
173        cls = type(self)
174
175        # Avoid dereferencing self from the lambda or we'll keep
176        # ourself alive indefinitely.
177        editcontroller = self._editcontroller
178
179        return bui.BasicMainWindowState(
180            create_call=lambda transition, origin_widget: cls(
181                transition=transition,
182                origin_widget=origin_widget,
183                editcontroller=editcontroller,
184            )
185        )
186
187    def _on_game_types_loaded(
188        self, gametypes: list[type[bs.GameActivity]]
189    ) -> None:
190        assert bui.app.classic is not None
191        store = bui.app.classic.store
192
193        # We asked for a bg thread completion cb so we can do some
194        # filtering here in the bg thread where its not gonna cause hitches.
195        assert not bui.in_logic_thread()
196        sessiontype = self._editcontroller.get_session_type()
197        unowned = store.get_unowned_game_types()
198        self._game_types = [
199            gt
200            for gt in gametypes
201            if gt not in unowned and gt.supports_session_type(sessiontype)
202        ]
203
204        # Sort in the current language.
205        self._game_types.sort(key=lambda g: g.get_display_string().evaluate())
206
207        # Tell ourself to refresh back in the logic thread.
208        bui.pushcall(self._refresh, from_other_thread=True)
209
210    def _refresh(self, select_get_more_games_button: bool = False) -> None:
211        if self._column is not None:
212            self._column.delete()
213
214        self._column = bui.columnwidget(
215            parent=self._scrollwidget, border=2, margin=0
216        )
217
218        for i, gametype in enumerate(self._game_types):
219
220            def _doit() -> None:
221                if self._select_button:
222                    bui.apptimer(0.1, self._select_button.activate)
223
224            txt = bui.textwidget(
225                parent=self._column,
226                position=(0, 0),
227                size=(self._scroll_width * 1.1, 24),
228                text=gametype.get_display_string(),
229                h_align='left',
230                v_align='center',
231                color=(0.8, 0.8, 0.8, 1.0),
232                maxwidth=self._scroll_width * 0.8,
233                on_select_call=bui.Call(self._set_selected_game_type, gametype),
234                always_highlight=True,
235                selectable=True,
236                on_activate_call=_doit,
237            )
238            if i == 0:
239                bui.widget(edit=txt, up_widget=self._back_button)
240
241        self._get_more_games_button = bui.buttonwidget(
242            parent=self._column,
243            autoselect=True,
244            label=bui.Lstr(resource=f'{self._r}.getMoreGamesText'),
245            color=(0.54, 0.52, 0.67),
246            textcolor=(0.7, 0.65, 0.7),
247            on_activate_call=self._on_get_more_games_press,
248            size=(178, 50),
249        )
250        if select_get_more_games_button:
251            bui.containerwidget(
252                edit=self._column,
253                selected_child=self._get_more_games_button,
254                visible_child=self._get_more_games_button,
255            )
256
257    def _on_get_more_games_press(self) -> None:
258        from bauiv1lib.account import show_sign_in_prompt
259        from bauiv1lib.store.browser import StoreBrowserWindow
260
261        # No-op if we're not in control.
262        if self.main_window_has_control():
263            return
264
265        plus = bui.app.plus
266        assert plus is not None
267
268        if plus.get_v1_account_state() != 'signed_in':
269            show_sign_in_prompt()
270            return
271
272        self.main_window_replace(
273            StoreBrowserWindow(
274                # modal=True,
275                show_tab=StoreBrowserWindow.TabID.MINIGAMES,
276                # on_close_call=self._on_store_close,
277                origin_widget=self._get_more_games_button,
278                minimal_toolbars=True,
279            )
280        )
281
282    # def _on_store_close(self) -> None:
283    #     self._refresh(select_get_more_games_button=True)
284
285    def _add(self) -> None:
286        bui.lock_all_input()  # Make sure no more commands happen.
287        bui.apptimer(0.1, bui.unlock_all_input)
288        assert self._selected_game_type is not None
289        self._editcontroller.add_game_type_selected(
290            self._selected_game_type, from_window=self
291        )
292
293    def _set_selected_game_type(self, gametype: type[bs.GameActivity]) -> None:
294        self._selected_game_type = gametype
295        bui.textwidget(
296            edit=self._selected_title_text, text=gametype.get_display_string()
297        )
298        bui.textwidget(
299            edit=self._selected_description_text,
300            text=gametype.get_description_display_string(
301                self._editcontroller.get_session_type()
302            ),
303        )
class PlaylistAddGameWindow(bauiv1._uitypes.MainWindow):
 17class PlaylistAddGameWindow(bui.MainWindow):
 18    """Window for selecting a game type to add to a playlist."""
 19
 20    def __init__(
 21        self,
 22        editcontroller: PlaylistEditController,
 23        transition: str | None = 'in_right',
 24        origin_widget: bui.Widget | None = None,
 25    ):
 26        self._editcontroller = editcontroller
 27        self._r = 'addGameWindow'
 28        assert bui.app.classic is not None
 29        uiscale = bui.app.ui_v1.uiscale
 30        self._width = 750 if uiscale is bui.UIScale.SMALL else 650
 31        x_inset = 50 if uiscale is bui.UIScale.SMALL else 0
 32        yoffs = -44 if uiscale is bui.UIScale.SMALL else 0
 33
 34        self._height = (
 35            400
 36            if uiscale is bui.UIScale.SMALL
 37            else 400 if uiscale is bui.UIScale.MEDIUM else 460
 38        )
 39        self._scroll_width = 210
 40
 41        super().__init__(
 42            root_widget=bui.containerwidget(
 43                size=(self._width, self._height),
 44                scale=(
 45                    1.95
 46                    if uiscale is bui.UIScale.SMALL
 47                    else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
 48                ),
 49                stack_offset=(0, 1) if uiscale is bui.UIScale.SMALL else (0, 0),
 50                toolbar_visibility='menu_minimal',
 51            ),
 52            transition=transition,
 53            origin_widget=origin_widget,
 54        )
 55
 56        if uiscale is bui.UIScale.SMALL:
 57            self._back_button = bui.get_special_widget('back_button')
 58        else:
 59            self._back_button = bui.buttonwidget(
 60                parent=self._root_widget,
 61                position=(58 + x_inset, self._height - 53 + yoffs),
 62                size=(60, 48),
 63                label=bui.charstr(bui.SpecialChar.BACK),
 64                autoselect=True,
 65                button_type='backSmall',
 66                on_activate_call=self.main_window_back,
 67            )
 68        self._select_button = select_button = bui.buttonwidget(
 69            parent=self._root_widget,
 70            position=(self._width - (172 + x_inset), self._height - 50 + yoffs),
 71            autoselect=True,
 72            size=(160, 60),
 73            scale=0.75,
 74            text_scale=1.2,
 75            label=bui.Lstr(resource='selectText'),
 76            on_activate_call=self._add,
 77        )
 78
 79        bui.widget(
 80            edit=select_button,
 81            right_widget=bui.get_special_widget('squad_button'),
 82        )
 83
 84        bui.textwidget(
 85            parent=self._root_widget,
 86            position=(self._width * 0.5, self._height - 28 + yoffs),
 87            size=(0, 0),
 88            scale=1.0,
 89            text=bui.Lstr(resource=f'{self._r}.titleText'),
 90            h_align='center',
 91            color=bui.app.ui_v1.title_color,
 92            maxwidth=250,
 93            v_align='center',
 94        )
 95        v = self._height - 64 + yoffs
 96
 97        self._selected_title_text = bui.textwidget(
 98            parent=self._root_widget,
 99            position=(x_inset + self._scroll_width + 50 + 30, v - 15),
100            size=(0, 0),
101            scale=1.0,
102            color=(0.7, 1.0, 0.7, 1.0),
103            maxwidth=self._width - self._scroll_width - 150 - x_inset * 2,
104            h_align='left',
105            v_align='center',
106        )
107        v -= 30
108
109        self._selected_description_text = bui.textwidget(
110            parent=self._root_widget,
111            position=(x_inset + self._scroll_width + 50 + 30, v),
112            size=(0, 0),
113            scale=0.7,
114            color=(0.5, 0.8, 0.5, 1.0),
115            maxwidth=self._width - self._scroll_width - 150 - x_inset * 2,
116            h_align='left',
117        )
118
119        scroll_height = self._height - (
120            160 if uiscale is bui.UIScale.SMALL else 100
121        )
122
123        v = self._height - 60 + yoffs
124
125        self._scrollwidget = bui.scrollwidget(
126            parent=self._root_widget,
127            position=(x_inset + 61, v - scroll_height),
128            size=(self._scroll_width, scroll_height),
129            highlight=False,
130        )
131        bui.widget(
132            edit=self._scrollwidget,
133            up_widget=self._back_button,
134            left_widget=self._back_button,
135            right_widget=select_button,
136        )
137        self._column: bui.Widget | None = None
138
139        v -= 35
140
141        if uiscale is bui.UIScale.SMALL:
142            bui.containerwidget(
143                edit=self._root_widget, on_cancel_call=self.main_window_back
144            )
145        else:
146            bui.containerwidget(
147                edit=self._root_widget,
148                cancel_button=self._back_button,
149            )
150        bui.containerwidget(edit=self._root_widget, start_button=select_button)
151
152        self._selected_game_type: type[bs.GameActivity] | None = None
153
154        bui.containerwidget(
155            edit=self._root_widget, selected_child=self._scrollwidget
156        )
157
158        self._game_types: list[type[bs.GameActivity]] = []
159
160        # Get actual games loading in the bg.
161        bui.app.meta.load_exported_classes(
162            bs.GameActivity,
163            self._on_game_types_loaded,
164            completion_cb_in_bg_thread=True,
165        )
166
167        # Refresh with our initial empty list. We'll refresh again once
168        # game loading is complete.
169        self._refresh()
170
171    @override
172    def get_main_window_state(self) -> bui.MainWindowState:
173        # Support recreating our window for back/refresh purposes.
174        cls = type(self)
175
176        # Avoid dereferencing self from the lambda or we'll keep
177        # ourself alive indefinitely.
178        editcontroller = self._editcontroller
179
180        return bui.BasicMainWindowState(
181            create_call=lambda transition, origin_widget: cls(
182                transition=transition,
183                origin_widget=origin_widget,
184                editcontroller=editcontroller,
185            )
186        )
187
188    def _on_game_types_loaded(
189        self, gametypes: list[type[bs.GameActivity]]
190    ) -> None:
191        assert bui.app.classic is not None
192        store = bui.app.classic.store
193
194        # We asked for a bg thread completion cb so we can do some
195        # filtering here in the bg thread where its not gonna cause hitches.
196        assert not bui.in_logic_thread()
197        sessiontype = self._editcontroller.get_session_type()
198        unowned = store.get_unowned_game_types()
199        self._game_types = [
200            gt
201            for gt in gametypes
202            if gt not in unowned and gt.supports_session_type(sessiontype)
203        ]
204
205        # Sort in the current language.
206        self._game_types.sort(key=lambda g: g.get_display_string().evaluate())
207
208        # Tell ourself to refresh back in the logic thread.
209        bui.pushcall(self._refresh, from_other_thread=True)
210
211    def _refresh(self, select_get_more_games_button: bool = False) -> None:
212        if self._column is not None:
213            self._column.delete()
214
215        self._column = bui.columnwidget(
216            parent=self._scrollwidget, border=2, margin=0
217        )
218
219        for i, gametype in enumerate(self._game_types):
220
221            def _doit() -> None:
222                if self._select_button:
223                    bui.apptimer(0.1, self._select_button.activate)
224
225            txt = bui.textwidget(
226                parent=self._column,
227                position=(0, 0),
228                size=(self._scroll_width * 1.1, 24),
229                text=gametype.get_display_string(),
230                h_align='left',
231                v_align='center',
232                color=(0.8, 0.8, 0.8, 1.0),
233                maxwidth=self._scroll_width * 0.8,
234                on_select_call=bui.Call(self._set_selected_game_type, gametype),
235                always_highlight=True,
236                selectable=True,
237                on_activate_call=_doit,
238            )
239            if i == 0:
240                bui.widget(edit=txt, up_widget=self._back_button)
241
242        self._get_more_games_button = bui.buttonwidget(
243            parent=self._column,
244            autoselect=True,
245            label=bui.Lstr(resource=f'{self._r}.getMoreGamesText'),
246            color=(0.54, 0.52, 0.67),
247            textcolor=(0.7, 0.65, 0.7),
248            on_activate_call=self._on_get_more_games_press,
249            size=(178, 50),
250        )
251        if select_get_more_games_button:
252            bui.containerwidget(
253                edit=self._column,
254                selected_child=self._get_more_games_button,
255                visible_child=self._get_more_games_button,
256            )
257
258    def _on_get_more_games_press(self) -> None:
259        from bauiv1lib.account import show_sign_in_prompt
260        from bauiv1lib.store.browser import StoreBrowserWindow
261
262        # No-op if we're not in control.
263        if self.main_window_has_control():
264            return
265
266        plus = bui.app.plus
267        assert plus is not None
268
269        if plus.get_v1_account_state() != 'signed_in':
270            show_sign_in_prompt()
271            return
272
273        self.main_window_replace(
274            StoreBrowserWindow(
275                # modal=True,
276                show_tab=StoreBrowserWindow.TabID.MINIGAMES,
277                # on_close_call=self._on_store_close,
278                origin_widget=self._get_more_games_button,
279                minimal_toolbars=True,
280            )
281        )
282
283    # def _on_store_close(self) -> None:
284    #     self._refresh(select_get_more_games_button=True)
285
286    def _add(self) -> None:
287        bui.lock_all_input()  # Make sure no more commands happen.
288        bui.apptimer(0.1, bui.unlock_all_input)
289        assert self._selected_game_type is not None
290        self._editcontroller.add_game_type_selected(
291            self._selected_game_type, from_window=self
292        )
293
294    def _set_selected_game_type(self, gametype: type[bs.GameActivity]) -> None:
295        self._selected_game_type = gametype
296        bui.textwidget(
297            edit=self._selected_title_text, text=gametype.get_display_string()
298        )
299        bui.textwidget(
300            edit=self._selected_description_text,
301            text=gametype.get_description_display_string(
302                self._editcontroller.get_session_type()
303            ),
304        )

Window for selecting a game type to add to a playlist.

PlaylistAddGameWindow( editcontroller: bauiv1lib.playlist.editcontroller.PlaylistEditController, transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
 20    def __init__(
 21        self,
 22        editcontroller: PlaylistEditController,
 23        transition: str | None = 'in_right',
 24        origin_widget: bui.Widget | None = None,
 25    ):
 26        self._editcontroller = editcontroller
 27        self._r = 'addGameWindow'
 28        assert bui.app.classic is not None
 29        uiscale = bui.app.ui_v1.uiscale
 30        self._width = 750 if uiscale is bui.UIScale.SMALL else 650
 31        x_inset = 50 if uiscale is bui.UIScale.SMALL else 0
 32        yoffs = -44 if uiscale is bui.UIScale.SMALL else 0
 33
 34        self._height = (
 35            400
 36            if uiscale is bui.UIScale.SMALL
 37            else 400 if uiscale is bui.UIScale.MEDIUM else 460
 38        )
 39        self._scroll_width = 210
 40
 41        super().__init__(
 42            root_widget=bui.containerwidget(
 43                size=(self._width, self._height),
 44                scale=(
 45                    1.95
 46                    if uiscale is bui.UIScale.SMALL
 47                    else 1.5 if uiscale is bui.UIScale.MEDIUM else 1.0
 48                ),
 49                stack_offset=(0, 1) if uiscale is bui.UIScale.SMALL else (0, 0),
 50                toolbar_visibility='menu_minimal',
 51            ),
 52            transition=transition,
 53            origin_widget=origin_widget,
 54        )
 55
 56        if uiscale is bui.UIScale.SMALL:
 57            self._back_button = bui.get_special_widget('back_button')
 58        else:
 59            self._back_button = bui.buttonwidget(
 60                parent=self._root_widget,
 61                position=(58 + x_inset, self._height - 53 + yoffs),
 62                size=(60, 48),
 63                label=bui.charstr(bui.SpecialChar.BACK),
 64                autoselect=True,
 65                button_type='backSmall',
 66                on_activate_call=self.main_window_back,
 67            )
 68        self._select_button = select_button = bui.buttonwidget(
 69            parent=self._root_widget,
 70            position=(self._width - (172 + x_inset), self._height - 50 + yoffs),
 71            autoselect=True,
 72            size=(160, 60),
 73            scale=0.75,
 74            text_scale=1.2,
 75            label=bui.Lstr(resource='selectText'),
 76            on_activate_call=self._add,
 77        )
 78
 79        bui.widget(
 80            edit=select_button,
 81            right_widget=bui.get_special_widget('squad_button'),
 82        )
 83
 84        bui.textwidget(
 85            parent=self._root_widget,
 86            position=(self._width * 0.5, self._height - 28 + yoffs),
 87            size=(0, 0),
 88            scale=1.0,
 89            text=bui.Lstr(resource=f'{self._r}.titleText'),
 90            h_align='center',
 91            color=bui.app.ui_v1.title_color,
 92            maxwidth=250,
 93            v_align='center',
 94        )
 95        v = self._height - 64 + yoffs
 96
 97        self._selected_title_text = bui.textwidget(
 98            parent=self._root_widget,
 99            position=(x_inset + self._scroll_width + 50 + 30, v - 15),
100            size=(0, 0),
101            scale=1.0,
102            color=(0.7, 1.0, 0.7, 1.0),
103            maxwidth=self._width - self._scroll_width - 150 - x_inset * 2,
104            h_align='left',
105            v_align='center',
106        )
107        v -= 30
108
109        self._selected_description_text = bui.textwidget(
110            parent=self._root_widget,
111            position=(x_inset + self._scroll_width + 50 + 30, v),
112            size=(0, 0),
113            scale=0.7,
114            color=(0.5, 0.8, 0.5, 1.0),
115            maxwidth=self._width - self._scroll_width - 150 - x_inset * 2,
116            h_align='left',
117        )
118
119        scroll_height = self._height - (
120            160 if uiscale is bui.UIScale.SMALL else 100
121        )
122
123        v = self._height - 60 + yoffs
124
125        self._scrollwidget = bui.scrollwidget(
126            parent=self._root_widget,
127            position=(x_inset + 61, v - scroll_height),
128            size=(self._scroll_width, scroll_height),
129            highlight=False,
130        )
131        bui.widget(
132            edit=self._scrollwidget,
133            up_widget=self._back_button,
134            left_widget=self._back_button,
135            right_widget=select_button,
136        )
137        self._column: bui.Widget | None = None
138
139        v -= 35
140
141        if uiscale is bui.UIScale.SMALL:
142            bui.containerwidget(
143                edit=self._root_widget, on_cancel_call=self.main_window_back
144            )
145        else:
146            bui.containerwidget(
147                edit=self._root_widget,
148                cancel_button=self._back_button,
149            )
150        bui.containerwidget(edit=self._root_widget, start_button=select_button)
151
152        self._selected_game_type: type[bs.GameActivity] | None = None
153
154        bui.containerwidget(
155            edit=self._root_widget, selected_child=self._scrollwidget
156        )
157
158        self._game_types: list[type[bs.GameActivity]] = []
159
160        # Get actual games loading in the bg.
161        bui.app.meta.load_exported_classes(
162            bs.GameActivity,
163            self._on_game_types_loaded,
164            completion_cb_in_bg_thread=True,
165        )
166
167        # Refresh with our initial empty list. We'll refresh again once
168        # game loading is complete.
169        self._refresh()

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:
171    @override
172    def get_main_window_state(self) -> bui.MainWindowState:
173        # Support recreating our window for back/refresh purposes.
174        cls = type(self)
175
176        # Avoid dereferencing self from the lambda or we'll keep
177        # ourself alive indefinitely.
178        editcontroller = self._editcontroller
179
180        return bui.BasicMainWindowState(
181            create_call=lambda transition, origin_widget: cls(
182                transition=transition,
183                origin_widget=origin_widget,
184                editcontroller=editcontroller,
185            )
186        )

Return a WindowState to recreate this window, if supported.