bauiv1lib.playlist.mapselect

Provides UI for selecting maps in playlists.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides UI for selecting maps in playlists."""
  4
  5from __future__ import annotations
  6
  7import math
  8from typing import TYPE_CHECKING
  9
 10import bauiv1 as bui
 11
 12if TYPE_CHECKING:
 13    from typing import Any, Callable
 14
 15    import bascenev1 as bs
 16
 17
 18class PlaylistMapSelectWindow(bui.MainWindow):
 19    """Window to select a map."""
 20
 21    def __init__(
 22        self,
 23        gametype: type[bs.GameActivity],
 24        sessiontype: type[bs.Session],
 25        config: dict[str, Any],
 26        edit_info: dict[str, Any],
 27        completion_call: Callable[[dict[str, Any] | None], Any],
 28        transition: str | None = 'in_right',
 29        origin_widget: bui.Widget | None = None,
 30    ):
 31        # pylint: disable=too-many-locals
 32
 33        from bascenev1 import get_filtered_map_name
 34
 35        self._gametype = gametype
 36        self._sessiontype = sessiontype
 37        self._config = config
 38        self._completion_call = completion_call
 39        self._edit_info = edit_info
 40        self._maps: list[tuple[str, bui.Texture]] = []
 41        try:
 42            self._previous_map = get_filtered_map_name(
 43                config['settings']['map']
 44            )
 45        except Exception:
 46            self._previous_map = ''
 47
 48        assert bui.app.classic is not None
 49        uiscale = bui.app.ui_v1.uiscale
 50        width = 815 if uiscale is bui.UIScale.SMALL else 615
 51        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 52        height = (
 53            400
 54            if uiscale is bui.UIScale.SMALL
 55            else 480 if uiscale is bui.UIScale.MEDIUM else 600
 56        )
 57
 58        top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
 59        super().__init__(
 60            root_widget=bui.containerwidget(
 61                size=(width, height + top_extra),
 62                # transition=transition,
 63                scale=(
 64                    1.95
 65                    if uiscale is bui.UIScale.SMALL
 66                    else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0
 67                ),
 68                stack_offset=(
 69                    (0, -27) if uiscale is bui.UIScale.SMALL else (0, 0)
 70                ),
 71            ),
 72            transition=transition,
 73            origin_widget=origin_widget,
 74        )
 75
 76        self._cancel_button = btn = bui.buttonwidget(
 77            parent=self._root_widget,
 78            position=(38 + x_inset, height - 67),
 79            size=(140, 50),
 80            scale=0.9,
 81            text_scale=1.0,
 82            autoselect=True,
 83            label=bui.Lstr(resource='cancelText'),
 84            on_activate_call=self.main_window_back,
 85        )
 86
 87        bui.containerwidget(edit=self._root_widget, cancel_button=btn)
 88        bui.textwidget(
 89            parent=self._root_widget,
 90            position=(width * 0.5, height - 46),
 91            size=(0, 0),
 92            maxwidth=260,
 93            scale=1.1,
 94            text=bui.Lstr(
 95                resource='mapSelectTitleText',
 96                subs=[('${GAME}', self._gametype.get_display_string())],
 97            ),
 98            color=bui.app.ui_v1.title_color,
 99            h_align='center',
100            v_align='center',
101        )
102        v = height - 70
103        self._scroll_width = width - (80 + 2 * x_inset)
104        self._scroll_height = height - 140
105
106        self._scrollwidget = bui.scrollwidget(
107            parent=self._root_widget,
108            position=(40 + x_inset, v - self._scroll_height),
109            size=(self._scroll_width, self._scroll_height),
110        )
111        bui.containerwidget(
112            edit=self._root_widget, selected_child=self._scrollwidget
113        )
114        bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
115
116        self._subcontainer: bui.Widget | None = None
117        self._refresh()
118
119    def _refresh(self, select_get_more_maps_button: bool = False) -> None:
120        # pylint: disable=too-many-statements
121        # pylint: disable=too-many-branches
122        # pylint: disable=too-many-locals
123        from bascenev1 import (
124            get_map_class,
125            get_map_display_string,
126        )
127
128        assert bui.app.classic is not None
129        store = bui.app.classic.store
130        # Kill old.
131        if self._subcontainer is not None:
132            self._subcontainer.delete()
133
134        mesh_opaque = bui.getmesh('level_select_button_opaque')
135        mesh_transparent = bui.getmesh('level_select_button_transparent')
136
137        self._maps = []
138        map_list = self._gametype.get_supported_maps(self._sessiontype)
139        map_list_sorted = list(map_list)
140        map_list_sorted.sort()
141        unowned_maps = store.get_unowned_maps()
142
143        for mapname in map_list_sorted:
144            # Disallow ones we don't own.
145            if mapname in unowned_maps:
146                continue
147            map_tex_name = get_map_class(mapname).get_preview_texture_name()
148            if map_tex_name is not None:
149                try:
150                    map_tex = bui.gettexture(map_tex_name)
151                    self._maps.append((mapname, map_tex))
152                except Exception:
153                    print(f'Invalid map preview texture: "{map_tex_name}".')
154            else:
155                print('Error: no map preview texture for map:', mapname)
156
157        count = len(self._maps)
158        columns = 2
159        rows = int(math.ceil(float(count) / columns))
160        button_width = 220
161        button_height = button_width * 0.5
162        button_buffer_h = 16
163        button_buffer_v = 19
164        self._sub_width = self._scroll_width * 0.95
165        self._sub_height = (
166            5 + rows * (button_height + 2 * button_buffer_v) + 100
167        )
168        self._subcontainer = bui.containerwidget(
169            parent=self._scrollwidget,
170            size=(self._sub_width, self._sub_height),
171            background=False,
172        )
173        index = 0
174        mask_texture = bui.gettexture('mapPreviewMask')
175        h_offs = 130 if len(self._maps) == 1 else 0
176        for y in range(rows):
177            for x in range(columns):
178                pos = (
179                    x * (button_width + 2 * button_buffer_h)
180                    + button_buffer_h
181                    + h_offs,
182                    self._sub_height
183                    - (y + 1) * (button_height + 2 * button_buffer_v)
184                    + 12,
185                )
186                btn = bui.buttonwidget(
187                    parent=self._subcontainer,
188                    button_type='square',
189                    size=(button_width, button_height),
190                    autoselect=True,
191                    texture=self._maps[index][1],
192                    mask_texture=mask_texture,
193                    mesh_opaque=mesh_opaque,
194                    mesh_transparent=mesh_transparent,
195                    label='',
196                    color=(1, 1, 1),
197                    on_activate_call=bui.Call(
198                        self._select_with_delay, self._maps[index][0]
199                    ),
200                    position=pos,
201                )
202                if x == 0:
203                    bui.widget(edit=btn, left_widget=self._cancel_button)
204                if y == 0:
205                    bui.widget(edit=btn, up_widget=self._cancel_button)
206                if x == columns - 1:
207                    bui.widget(
208                        edit=btn,
209                        right_widget=bui.get_special_widget('squad_button'),
210                    )
211
212                bui.widget(edit=btn, show_buffer_top=60, show_buffer_bottom=60)
213                if self._maps[index][0] == self._previous_map:
214                    bui.containerwidget(
215                        edit=self._subcontainer,
216                        selected_child=btn,
217                        visible_child=btn,
218                    )
219                name = get_map_display_string(self._maps[index][0])
220                bui.textwidget(
221                    parent=self._subcontainer,
222                    text=name,
223                    position=(pos[0] + button_width * 0.5, pos[1] - 12),
224                    size=(0, 0),
225                    scale=0.5,
226                    maxwidth=button_width,
227                    draw_controller=btn,
228                    h_align='center',
229                    v_align='center',
230                    color=(0.8, 0.8, 0.8, 0.8),
231                )
232                index += 1
233
234                if index >= count:
235                    break
236            if index >= count:
237                break
238        self._get_more_maps_button = btn = bui.buttonwidget(
239            parent=self._subcontainer,
240            size=(self._sub_width * 0.8, 60),
241            position=(self._sub_width * 0.1, 30),
242            label=bui.Lstr(resource='mapSelectGetMoreMapsText'),
243            on_activate_call=self._on_store_press,
244            color=(0.6, 0.53, 0.63),
245            textcolor=(0.75, 0.7, 0.8),
246            autoselect=True,
247        )
248        bui.widget(edit=btn, show_buffer_top=30, show_buffer_bottom=30)
249        if select_get_more_maps_button:
250            bui.containerwidget(
251                edit=self._subcontainer, selected_child=btn, visible_child=btn
252            )
253
254    def _on_store_press(self) -> None:
255        from bauiv1lib import account
256        from bauiv1lib.store.browser import StoreBrowserWindow
257
258        plus = bui.app.plus
259        assert plus is not None
260
261        if plus.get_v1_account_state() != 'signed_in':
262            account.show_sign_in_prompt()
263            return
264        StoreBrowserWindow(
265            modal=True,
266            show_tab=StoreBrowserWindow.TabID.MAPS,
267            on_close_call=self._on_store_close,
268            origin_widget=self._get_more_maps_button,
269        )
270
271    def _on_store_close(self) -> None:
272        self._refresh(select_get_more_maps_button=True)
273
274    def _select(self, map_name: str) -> None:
275        # from bauiv1lib.playlist.editgame import PlaylistEditGameWindow
276
277        # no-op if our underlying widget is dead or on its way out.
278        if not self._root_widget or self._root_widget.transitioning_out:
279            return
280
281        self._config['settings']['map'] = map_name
282        self.main_window_back()
283        # bui.containerwidget(edit=self._root_widget, transition='out_right')
284        # assert bui.app.classic is not None
285        # bui.app.ui_v1.set_main_window(
286        #     PlaylistEditGameWindow(
287        #         self._gametype,
288        #         self._sessiontype,
289        #         self._config,
290        #         self._completion_call,
291        #         default_selection='map',
292        #         transition='in_left',
293        #         edit_info=self._edit_info,
294        #     ),
295        #     from_window=self,
296        # )
297
298    def _select_with_delay(self, map_name: str) -> None:
299        bui.lock_all_input()
300        bui.apptimer(0.1, bui.unlock_all_input)
301        bui.apptimer(0.1, bui.WeakCall(self._select, map_name))
302
303    # def _cancel(self) -> None:
304    #     from bauiv1lib.playlist.editgame import PlaylistEditGameWindow
305
306    #     # no-op if our underlying widget is dead or on its way out.
307    #     if not self._root_widget or self._root_widget.transitioning_out:
308    #         return
309
310    #     bui.containerwidget(edit=self._root_widget, transition='out_right')
311    #     assert bui.app.classic is not None
312    #     bui.app.ui_v1.set_main_window(
313    #         PlaylistEditGameWindow(
314    #             self._gametype,
315    #             self._sessiontype,
316    #             self._config,
317    #             self._completion_call,
318    #             default_selection='map',
319    #             transition='in_left',
320    #             edit_info=self._edit_info,
321    #         ),
322    #         from_window=self,
323    #         is_back=True,
324    #     )
class PlaylistMapSelectWindow(bauiv1._uitypes.MainWindow):
 19class PlaylistMapSelectWindow(bui.MainWindow):
 20    """Window to select a map."""
 21
 22    def __init__(
 23        self,
 24        gametype: type[bs.GameActivity],
 25        sessiontype: type[bs.Session],
 26        config: dict[str, Any],
 27        edit_info: dict[str, Any],
 28        completion_call: Callable[[dict[str, Any] | None], Any],
 29        transition: str | None = 'in_right',
 30        origin_widget: bui.Widget | None = None,
 31    ):
 32        # pylint: disable=too-many-locals
 33
 34        from bascenev1 import get_filtered_map_name
 35
 36        self._gametype = gametype
 37        self._sessiontype = sessiontype
 38        self._config = config
 39        self._completion_call = completion_call
 40        self._edit_info = edit_info
 41        self._maps: list[tuple[str, bui.Texture]] = []
 42        try:
 43            self._previous_map = get_filtered_map_name(
 44                config['settings']['map']
 45            )
 46        except Exception:
 47            self._previous_map = ''
 48
 49        assert bui.app.classic is not None
 50        uiscale = bui.app.ui_v1.uiscale
 51        width = 815 if uiscale is bui.UIScale.SMALL else 615
 52        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 53        height = (
 54            400
 55            if uiscale is bui.UIScale.SMALL
 56            else 480 if uiscale is bui.UIScale.MEDIUM else 600
 57        )
 58
 59        top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
 60        super().__init__(
 61            root_widget=bui.containerwidget(
 62                size=(width, height + top_extra),
 63                # transition=transition,
 64                scale=(
 65                    1.95
 66                    if uiscale is bui.UIScale.SMALL
 67                    else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0
 68                ),
 69                stack_offset=(
 70                    (0, -27) if uiscale is bui.UIScale.SMALL else (0, 0)
 71                ),
 72            ),
 73            transition=transition,
 74            origin_widget=origin_widget,
 75        )
 76
 77        self._cancel_button = btn = bui.buttonwidget(
 78            parent=self._root_widget,
 79            position=(38 + x_inset, height - 67),
 80            size=(140, 50),
 81            scale=0.9,
 82            text_scale=1.0,
 83            autoselect=True,
 84            label=bui.Lstr(resource='cancelText'),
 85            on_activate_call=self.main_window_back,
 86        )
 87
 88        bui.containerwidget(edit=self._root_widget, cancel_button=btn)
 89        bui.textwidget(
 90            parent=self._root_widget,
 91            position=(width * 0.5, height - 46),
 92            size=(0, 0),
 93            maxwidth=260,
 94            scale=1.1,
 95            text=bui.Lstr(
 96                resource='mapSelectTitleText',
 97                subs=[('${GAME}', self._gametype.get_display_string())],
 98            ),
 99            color=bui.app.ui_v1.title_color,
100            h_align='center',
101            v_align='center',
102        )
103        v = height - 70
104        self._scroll_width = width - (80 + 2 * x_inset)
105        self._scroll_height = height - 140
106
107        self._scrollwidget = bui.scrollwidget(
108            parent=self._root_widget,
109            position=(40 + x_inset, v - self._scroll_height),
110            size=(self._scroll_width, self._scroll_height),
111        )
112        bui.containerwidget(
113            edit=self._root_widget, selected_child=self._scrollwidget
114        )
115        bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
116
117        self._subcontainer: bui.Widget | None = None
118        self._refresh()
119
120    def _refresh(self, select_get_more_maps_button: bool = False) -> None:
121        # pylint: disable=too-many-statements
122        # pylint: disable=too-many-branches
123        # pylint: disable=too-many-locals
124        from bascenev1 import (
125            get_map_class,
126            get_map_display_string,
127        )
128
129        assert bui.app.classic is not None
130        store = bui.app.classic.store
131        # Kill old.
132        if self._subcontainer is not None:
133            self._subcontainer.delete()
134
135        mesh_opaque = bui.getmesh('level_select_button_opaque')
136        mesh_transparent = bui.getmesh('level_select_button_transparent')
137
138        self._maps = []
139        map_list = self._gametype.get_supported_maps(self._sessiontype)
140        map_list_sorted = list(map_list)
141        map_list_sorted.sort()
142        unowned_maps = store.get_unowned_maps()
143
144        for mapname in map_list_sorted:
145            # Disallow ones we don't own.
146            if mapname in unowned_maps:
147                continue
148            map_tex_name = get_map_class(mapname).get_preview_texture_name()
149            if map_tex_name is not None:
150                try:
151                    map_tex = bui.gettexture(map_tex_name)
152                    self._maps.append((mapname, map_tex))
153                except Exception:
154                    print(f'Invalid map preview texture: "{map_tex_name}".')
155            else:
156                print('Error: no map preview texture for map:', mapname)
157
158        count = len(self._maps)
159        columns = 2
160        rows = int(math.ceil(float(count) / columns))
161        button_width = 220
162        button_height = button_width * 0.5
163        button_buffer_h = 16
164        button_buffer_v = 19
165        self._sub_width = self._scroll_width * 0.95
166        self._sub_height = (
167            5 + rows * (button_height + 2 * button_buffer_v) + 100
168        )
169        self._subcontainer = bui.containerwidget(
170            parent=self._scrollwidget,
171            size=(self._sub_width, self._sub_height),
172            background=False,
173        )
174        index = 0
175        mask_texture = bui.gettexture('mapPreviewMask')
176        h_offs = 130 if len(self._maps) == 1 else 0
177        for y in range(rows):
178            for x in range(columns):
179                pos = (
180                    x * (button_width + 2 * button_buffer_h)
181                    + button_buffer_h
182                    + h_offs,
183                    self._sub_height
184                    - (y + 1) * (button_height + 2 * button_buffer_v)
185                    + 12,
186                )
187                btn = bui.buttonwidget(
188                    parent=self._subcontainer,
189                    button_type='square',
190                    size=(button_width, button_height),
191                    autoselect=True,
192                    texture=self._maps[index][1],
193                    mask_texture=mask_texture,
194                    mesh_opaque=mesh_opaque,
195                    mesh_transparent=mesh_transparent,
196                    label='',
197                    color=(1, 1, 1),
198                    on_activate_call=bui.Call(
199                        self._select_with_delay, self._maps[index][0]
200                    ),
201                    position=pos,
202                )
203                if x == 0:
204                    bui.widget(edit=btn, left_widget=self._cancel_button)
205                if y == 0:
206                    bui.widget(edit=btn, up_widget=self._cancel_button)
207                if x == columns - 1:
208                    bui.widget(
209                        edit=btn,
210                        right_widget=bui.get_special_widget('squad_button'),
211                    )
212
213                bui.widget(edit=btn, show_buffer_top=60, show_buffer_bottom=60)
214                if self._maps[index][0] == self._previous_map:
215                    bui.containerwidget(
216                        edit=self._subcontainer,
217                        selected_child=btn,
218                        visible_child=btn,
219                    )
220                name = get_map_display_string(self._maps[index][0])
221                bui.textwidget(
222                    parent=self._subcontainer,
223                    text=name,
224                    position=(pos[0] + button_width * 0.5, pos[1] - 12),
225                    size=(0, 0),
226                    scale=0.5,
227                    maxwidth=button_width,
228                    draw_controller=btn,
229                    h_align='center',
230                    v_align='center',
231                    color=(0.8, 0.8, 0.8, 0.8),
232                )
233                index += 1
234
235                if index >= count:
236                    break
237            if index >= count:
238                break
239        self._get_more_maps_button = btn = bui.buttonwidget(
240            parent=self._subcontainer,
241            size=(self._sub_width * 0.8, 60),
242            position=(self._sub_width * 0.1, 30),
243            label=bui.Lstr(resource='mapSelectGetMoreMapsText'),
244            on_activate_call=self._on_store_press,
245            color=(0.6, 0.53, 0.63),
246            textcolor=(0.75, 0.7, 0.8),
247            autoselect=True,
248        )
249        bui.widget(edit=btn, show_buffer_top=30, show_buffer_bottom=30)
250        if select_get_more_maps_button:
251            bui.containerwidget(
252                edit=self._subcontainer, selected_child=btn, visible_child=btn
253            )
254
255    def _on_store_press(self) -> None:
256        from bauiv1lib import account
257        from bauiv1lib.store.browser import StoreBrowserWindow
258
259        plus = bui.app.plus
260        assert plus is not None
261
262        if plus.get_v1_account_state() != 'signed_in':
263            account.show_sign_in_prompt()
264            return
265        StoreBrowserWindow(
266            modal=True,
267            show_tab=StoreBrowserWindow.TabID.MAPS,
268            on_close_call=self._on_store_close,
269            origin_widget=self._get_more_maps_button,
270        )
271
272    def _on_store_close(self) -> None:
273        self._refresh(select_get_more_maps_button=True)
274
275    def _select(self, map_name: str) -> None:
276        # from bauiv1lib.playlist.editgame import PlaylistEditGameWindow
277
278        # no-op if our underlying widget is dead or on its way out.
279        if not self._root_widget or self._root_widget.transitioning_out:
280            return
281
282        self._config['settings']['map'] = map_name
283        self.main_window_back()
284        # bui.containerwidget(edit=self._root_widget, transition='out_right')
285        # assert bui.app.classic is not None
286        # bui.app.ui_v1.set_main_window(
287        #     PlaylistEditGameWindow(
288        #         self._gametype,
289        #         self._sessiontype,
290        #         self._config,
291        #         self._completion_call,
292        #         default_selection='map',
293        #         transition='in_left',
294        #         edit_info=self._edit_info,
295        #     ),
296        #     from_window=self,
297        # )
298
299    def _select_with_delay(self, map_name: str) -> None:
300        bui.lock_all_input()
301        bui.apptimer(0.1, bui.unlock_all_input)
302        bui.apptimer(0.1, bui.WeakCall(self._select, map_name))
303
304    # def _cancel(self) -> None:
305    #     from bauiv1lib.playlist.editgame import PlaylistEditGameWindow
306
307    #     # no-op if our underlying widget is dead or on its way out.
308    #     if not self._root_widget or self._root_widget.transitioning_out:
309    #         return
310
311    #     bui.containerwidget(edit=self._root_widget, transition='out_right')
312    #     assert bui.app.classic is not None
313    #     bui.app.ui_v1.set_main_window(
314    #         PlaylistEditGameWindow(
315    #             self._gametype,
316    #             self._sessiontype,
317    #             self._config,
318    #             self._completion_call,
319    #             default_selection='map',
320    #             transition='in_left',
321    #             edit_info=self._edit_info,
322    #         ),
323    #         from_window=self,
324    #         is_back=True,
325    #     )

Window to select a map.

PlaylistMapSelectWindow( gametype: type[bascenev1.GameActivity], sessiontype: type[bascenev1.Session], config: dict[str, typing.Any], edit_info: dict[str, typing.Any], completion_call: Callable[[dict[str, Any] | None], Any], transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
 22    def __init__(
 23        self,
 24        gametype: type[bs.GameActivity],
 25        sessiontype: type[bs.Session],
 26        config: dict[str, Any],
 27        edit_info: dict[str, Any],
 28        completion_call: Callable[[dict[str, Any] | None], Any],
 29        transition: str | None = 'in_right',
 30        origin_widget: bui.Widget | None = None,
 31    ):
 32        # pylint: disable=too-many-locals
 33
 34        from bascenev1 import get_filtered_map_name
 35
 36        self._gametype = gametype
 37        self._sessiontype = sessiontype
 38        self._config = config
 39        self._completion_call = completion_call
 40        self._edit_info = edit_info
 41        self._maps: list[tuple[str, bui.Texture]] = []
 42        try:
 43            self._previous_map = get_filtered_map_name(
 44                config['settings']['map']
 45            )
 46        except Exception:
 47            self._previous_map = ''
 48
 49        assert bui.app.classic is not None
 50        uiscale = bui.app.ui_v1.uiscale
 51        width = 815 if uiscale is bui.UIScale.SMALL else 615
 52        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 53        height = (
 54            400
 55            if uiscale is bui.UIScale.SMALL
 56            else 480 if uiscale is bui.UIScale.MEDIUM else 600
 57        )
 58
 59        top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
 60        super().__init__(
 61            root_widget=bui.containerwidget(
 62                size=(width, height + top_extra),
 63                # transition=transition,
 64                scale=(
 65                    1.95
 66                    if uiscale is bui.UIScale.SMALL
 67                    else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0
 68                ),
 69                stack_offset=(
 70                    (0, -27) if uiscale is bui.UIScale.SMALL else (0, 0)
 71                ),
 72            ),
 73            transition=transition,
 74            origin_widget=origin_widget,
 75        )
 76
 77        self._cancel_button = btn = bui.buttonwidget(
 78            parent=self._root_widget,
 79            position=(38 + x_inset, height - 67),
 80            size=(140, 50),
 81            scale=0.9,
 82            text_scale=1.0,
 83            autoselect=True,
 84            label=bui.Lstr(resource='cancelText'),
 85            on_activate_call=self.main_window_back,
 86        )
 87
 88        bui.containerwidget(edit=self._root_widget, cancel_button=btn)
 89        bui.textwidget(
 90            parent=self._root_widget,
 91            position=(width * 0.5, height - 46),
 92            size=(0, 0),
 93            maxwidth=260,
 94            scale=1.1,
 95            text=bui.Lstr(
 96                resource='mapSelectTitleText',
 97                subs=[('${GAME}', self._gametype.get_display_string())],
 98            ),
 99            color=bui.app.ui_v1.title_color,
100            h_align='center',
101            v_align='center',
102        )
103        v = height - 70
104        self._scroll_width = width - (80 + 2 * x_inset)
105        self._scroll_height = height - 140
106
107        self._scrollwidget = bui.scrollwidget(
108            parent=self._root_widget,
109            position=(40 + x_inset, v - self._scroll_height),
110            size=(self._scroll_width, self._scroll_height),
111        )
112        bui.containerwidget(
113            edit=self._root_widget, selected_child=self._scrollwidget
114        )
115        bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
116
117        self._subcontainer: bui.Widget | None = None
118        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.

Inherited Members
bauiv1._uitypes.MainWindow
main_window_back_state
main_window_close
can_change_main_window
main_window_back
main_window_replace
on_main_window_close
get_main_window_state
bauiv1._uitypes.Window
get_root_widget