bauiv1lib.mainmenu
Implements the main menu window.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Implements the main menu window.""" 4 5from __future__ import annotations 6 7from typing import TYPE_CHECKING, override 8import logging 9 10import bauiv1 as bui 11import bascenev1 as bs 12 13if TYPE_CHECKING: 14 from typing import Any, Callable 15 16 17class MainMenuWindow(bui.MainWindow): 18 """The main menu window.""" 19 20 def __init__( 21 self, 22 transition: str | None = 'in_right', 23 origin_widget: bui.Widget | None = None, 24 ): 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('Main Menu') 31 self._show_remote_app_info_on_first_launch() 32 33 # Make a vanilla container; we'll modify it to our needs in 34 # refresh. 35 super().__init__( 36 root_widget=bui.containerwidget( 37 toolbar_visibility=('menu_full_no_back') 38 ), 39 transition=transition, 40 origin_widget=origin_widget, 41 ) 42 43 # Grab this stuff in case it changes. 44 self._is_demo = bui.app.env.demo 45 self._is_arcade = bui.app.env.arcade 46 47 self._tdelay = 0.0 48 self._t_delay_inc = 0.02 49 self._t_delay_play = 1.7 50 self._use_autoselect = True 51 self._button_width = 200.0 52 self._button_height = 45.0 53 self._width = 100.0 54 self._height = 100.0 55 self._demo_menu_button: bui.Widget | None = None 56 self._gather_button: bui.Widget | None = None 57 self._play_button: bui.Widget | None = None 58 self._watch_button: bui.Widget | None = None 59 self._how_to_play_button: bui.Widget | None = None 60 self._credits_button: bui.Widget | None = None 61 62 self._refresh() 63 64 self._restore_state() 65 66 @override 67 def on_main_window_close(self) -> None: 68 self._save_state() 69 70 @override 71 def get_main_window_state(self) -> bui.MainWindowState: 72 # Support recreating our window for back/refresh purposes. 73 return self.do_get_main_window_state() 74 75 @classmethod 76 def do_get_main_window_state(cls) -> bui.MainWindowState: 77 """Classmethod to gen a windowstate for the main menu.""" 78 return bui.BasicMainWindowState( 79 create_call=lambda transition, origin_widget: cls( 80 transition=transition, origin_widget=origin_widget 81 ) 82 ) 83 84 @staticmethod 85 def _preload_modules() -> None: 86 """Preload modules we use; avoids hitches (called in bg thread).""" 87 # pylint: disable=cyclic-import 88 import bauiv1lib.getremote as _unused 89 import bauiv1lib.confirm as _unused2 90 import bauiv1lib.store.button as _unused3 91 import bauiv1lib.account.settings as _unused5 92 import bauiv1lib.store.browser as _unused6 93 import bauiv1lib.credits as _unused7 94 import bauiv1lib.helpui as _unused8 95 import bauiv1lib.settings.allsettings as _unused9 96 import bauiv1lib.gather as _unused10 97 import bauiv1lib.watch as _unused11 98 import bauiv1lib.play as _unused12 99 100 def _show_remote_app_info_on_first_launch(self) -> None: 101 app = bui.app 102 assert app.classic is not None 103 # The first time the non-in-game menu pops up, we might wanna 104 # show a 'get-remote-app' dialog in front of it. 105 if app.classic.first_main_menu: 106 app.classic.first_main_menu = False 107 try: 108 force_test = False 109 bs.get_local_active_input_devices_count() 110 if ( 111 (app.env.tv or app.classic.platform == 'mac') 112 and bui.app.config.get('launchCount', 0) <= 1 113 ) or force_test: 114 115 def _check_show_bs_remote_window() -> None: 116 try: 117 from bauiv1lib.getremote import GetBSRemoteWindow 118 119 bui.getsound('swish').play() 120 GetBSRemoteWindow() 121 except Exception: 122 logging.exception( 123 'Error showing get-remote window.' 124 ) 125 126 bui.apptimer(2.5, _check_show_bs_remote_window) 127 except Exception: 128 logging.exception('Error showing get-remote-app info.') 129 130 def get_play_button(self) -> bui.Widget | None: 131 """Return the play button.""" 132 return self._play_button 133 134 def _refresh(self) -> None: 135 # pylint: disable=too-many-statements 136 # pylint: disable=too-many-locals 137 138 classic = bui.app.classic 139 assert classic is not None 140 141 # Clear everything that was there. 142 children = self._root_widget.get_children() 143 for child in children: 144 child.delete() 145 146 self._tdelay = 0.0 147 self._t_delay_inc = 0.0 148 self._t_delay_play = 0.0 149 self._button_width = 200.0 150 self._button_height = 45.0 151 152 self._r = 'mainMenu' 153 154 app = bui.app 155 assert app.classic is not None 156 157 self._have_quit_button = app.classic.platform in ( 158 'windows', 159 'mac', 160 'linux', 161 ) 162 163 if not classic.did_menu_intro: 164 self._tdelay = 1.7 165 self._t_delay_inc = 0.05 166 self._t_delay_play = 1.7 167 classic.did_menu_intro = True 168 169 self._width = 400.0 170 self._height = 200.0 171 172 play_button_width = self._button_width * 0.65 173 play_button_height = self._button_height * 1.1 174 play_button_scale = 1.7 175 hspace = 20.0 176 side_button_width = self._button_width * 0.4 177 side_button_height = side_button_width 178 side_button_scale = 0.95 179 side_button_y_offs = 5.0 180 hspace2 = 15.0 181 side_button_2_width = self._button_width * 1.0 182 side_button_2_height = side_button_2_width * 0.3 183 side_button_2_y_offs = 10.0 184 side_button_2_scale = 0.5 185 186 uiscale = bui.app.ui_v1.uiscale 187 if uiscale is bui.UIScale.SMALL: 188 root_widget_scale = 1.3 189 button_y_offs = -20.0 190 self._button_height *= 1.3 191 elif uiscale is bui.UIScale.MEDIUM: 192 root_widget_scale = 1.3 193 button_y_offs = -55.0 194 self._button_height *= 1.25 195 else: 196 root_widget_scale = 1.0 197 button_y_offs = -90.0 198 self._button_height *= 1.2 199 200 bui.containerwidget( 201 edit=self._root_widget, 202 size=(self._width, self._height), 203 background=False, 204 scale=root_widget_scale, 205 ) 206 207 # In kiosk mode, provide a button to get back to the kiosk menu. 208 if bui.app.env.demo or bui.app.env.arcade: 209 # h, v, scale = positions[self._p_index] 210 h = self._width * 0.5 211 v = button_y_offs 212 scale = 1.0 213 this_b_width = self._button_width * 0.4 * scale 214 demo_menu_delay = ( 215 0.0 216 if self._t_delay_play == 0.0 217 else max(0, self._t_delay_play + 0.1) 218 ) 219 self._demo_menu_button = bui.buttonwidget( 220 parent=self._root_widget, 221 position=(self._width * 0.5 - this_b_width * 0.5, v + 90), 222 size=(this_b_width, 45), 223 autoselect=True, 224 color=(0.45, 0.55, 0.45), 225 textcolor=(0.7, 0.8, 0.7), 226 label=bui.Lstr( 227 resource=( 228 'modeArcadeText' 229 if bui.app.env.arcade 230 else 'modeDemoText' 231 ) 232 ), 233 transition_delay=demo_menu_delay, 234 on_activate_call=self.main_window_back, 235 ) 236 else: 237 self._demo_menu_button = None 238 239 # Gather button 240 h = self._width * 0.5 241 h = ( 242 self._width * 0.5 243 - play_button_width * play_button_scale * 0.5 244 - hspace 245 - side_button_width * side_button_scale * 0.5 246 ) 247 v = button_y_offs + side_button_y_offs 248 self._gather_button = btn = bui.buttonwidget( 249 parent=self._root_widget, 250 position=(h - side_button_width * side_button_scale * 0.5, v), 251 size=(side_button_width, side_button_height), 252 scale=side_button_scale, 253 autoselect=self._use_autoselect, 254 button_type='square', 255 label='', 256 transition_delay=self._tdelay, 257 on_activate_call=self._gather_press, 258 ) 259 bui.textwidget( 260 parent=self._root_widget, 261 position=(h, v + side_button_height * side_button_scale * 0.25), 262 size=(0, 0), 263 scale=0.75, 264 transition_delay=self._tdelay, 265 draw_controller=btn, 266 color=(0.75, 1.0, 0.7), 267 maxwidth=side_button_width * side_button_scale * 0.8, 268 text=bui.Lstr(resource='gatherWindow.titleText'), 269 h_align='center', 270 v_align='center', 271 ) 272 icon_size = side_button_width * side_button_scale * 0.63 273 bui.imagewidget( 274 parent=self._root_widget, 275 size=(icon_size, icon_size), 276 draw_controller=btn, 277 transition_delay=self._tdelay, 278 position=( 279 h - 0.5 * icon_size, 280 v 281 + 0.65 * side_button_height * side_button_scale 282 - 0.5 * icon_size, 283 ), 284 texture=bui.gettexture('usersButton'), 285 ) 286 287 h -= ( 288 side_button_width * side_button_scale * 0.5 289 + hspace2 290 + side_button_2_width * side_button_2_scale 291 ) 292 v = button_y_offs + side_button_2_y_offs 293 294 btn = bui.buttonwidget( 295 parent=self._root_widget, 296 position=(h, v), 297 autoselect=self._use_autoselect, 298 size=(side_button_2_width, side_button_2_height * 2.0), 299 button_type='square', 300 scale=side_button_2_scale, 301 label=bui.Lstr(resource=f'{self._r}.howToPlayText'), 302 transition_delay=self._tdelay, 303 on_activate_call=self._howtoplay, 304 ) 305 self._how_to_play_button = btn 306 307 # Play button. 308 h = self._width * 0.5 309 v = button_y_offs 310 assert play_button_width is not None 311 assert play_button_height is not None 312 self._play_button = start_button = bui.buttonwidget( 313 parent=self._root_widget, 314 position=(h - play_button_width * 0.5 * play_button_scale, v), 315 size=(play_button_width, play_button_height), 316 autoselect=self._use_autoselect, 317 scale=play_button_scale, 318 text_res_scale=2.0, 319 label=bui.Lstr(resource='playText'), 320 transition_delay=self._t_delay_play, 321 on_activate_call=self._play_press, 322 ) 323 bui.containerwidget( 324 edit=self._root_widget, 325 start_button=start_button, 326 selected_child=start_button, 327 ) 328 329 self._tdelay += self._t_delay_inc 330 331 h = ( 332 self._width * 0.5 333 + play_button_width * play_button_scale * 0.5 334 + hspace 335 + side_button_width * side_button_scale * 0.5 336 ) 337 v = button_y_offs + side_button_y_offs 338 self._watch_button = btn = bui.buttonwidget( 339 parent=self._root_widget, 340 position=(h - side_button_width * side_button_scale * 0.5, v), 341 size=(side_button_width, side_button_height), 342 scale=side_button_scale, 343 autoselect=self._use_autoselect, 344 button_type='square', 345 label='', 346 transition_delay=self._tdelay, 347 on_activate_call=self._watch_press, 348 ) 349 bui.textwidget( 350 parent=self._root_widget, 351 position=(h, v + side_button_height * side_button_scale * 0.25), 352 size=(0, 0), 353 scale=0.75, 354 transition_delay=self._tdelay, 355 color=(0.75, 1.0, 0.7), 356 draw_controller=btn, 357 maxwidth=side_button_width * side_button_scale * 0.8, 358 text=bui.Lstr(resource='watchWindow.titleText'), 359 h_align='center', 360 v_align='center', 361 ) 362 icon_size = side_button_width * side_button_scale * 0.63 363 bui.imagewidget( 364 parent=self._root_widget, 365 size=(icon_size, icon_size), 366 draw_controller=btn, 367 transition_delay=self._tdelay, 368 position=( 369 h - 0.5 * icon_size, 370 v 371 + 0.65 * side_button_height * side_button_scale 372 - 0.5 * icon_size, 373 ), 374 texture=bui.gettexture('tv'), 375 ) 376 377 # Credits button. 378 self._tdelay += self._t_delay_inc 379 380 h += side_button_width * side_button_scale * 0.5 + hspace2 381 v = button_y_offs + side_button_2_y_offs 382 383 if self._have_quit_button: 384 v += 1.17 * side_button_2_height * side_button_2_scale 385 386 self._credits_button = bui.buttonwidget( 387 parent=self._root_widget, 388 position=(h, v), 389 button_type=None if self._have_quit_button else 'square', 390 size=( 391 side_button_2_width, 392 side_button_2_height * (1.0 if self._have_quit_button else 2.0), 393 ), 394 scale=side_button_2_scale, 395 autoselect=self._use_autoselect, 396 label=bui.Lstr(resource=f'{self._r}.creditsText'), 397 transition_delay=self._tdelay, 398 on_activate_call=self._credits, 399 ) 400 self._tdelay += self._t_delay_inc 401 402 self._quit_button: bui.Widget | None 403 if self._have_quit_button: 404 v -= 1.1 * side_button_2_height * side_button_2_scale 405 self._quit_button = quit_button = bui.buttonwidget( 406 parent=self._root_widget, 407 autoselect=self._use_autoselect, 408 position=(h, v), 409 size=(side_button_2_width, side_button_2_height), 410 scale=side_button_2_scale, 411 label=bui.Lstr( 412 resource=self._r 413 + ( 414 '.quitText' 415 if 'Mac' in app.classic.legacy_user_agent_string 416 else '.exitGameText' 417 ) 418 ), 419 on_activate_call=self._quit, 420 transition_delay=self._tdelay, 421 ) 422 423 bui.containerwidget( 424 edit=self._root_widget, cancel_button=quit_button 425 ) 426 self._tdelay += self._t_delay_inc 427 else: 428 self._quit_button = None 429 430 # If we're not in-game, have no quit button, and this is 431 # android, we want back presses to quit our activity. 432 if app.classic.platform == 'android': 433 434 def _do_quit() -> None: 435 bui.quit(confirm=True, quit_type=bui.QuitType.BACK) 436 437 bui.containerwidget( 438 edit=self._root_widget, on_cancel_call=_do_quit 439 ) 440 441 def _quit(self) -> None: 442 # pylint: disable=cyclic-import 443 from bauiv1lib.confirm import QuitWindow 444 445 # no-op if we're not currently in control. 446 if not self.can_change_main_window(): 447 return 448 449 # Note: Normally we should go through bui.quit(confirm=True) but 450 # invoking the window directly lets us scale it up from the 451 # button. 452 QuitWindow(origin_widget=self._quit_button) 453 454 def _credits(self) -> None: 455 # pylint: disable=cyclic-import 456 from bauiv1lib.credits import CreditsWindow 457 458 # no-op if we're not currently in control. 459 if not self.can_change_main_window(): 460 return 461 462 self.main_window_replace( 463 CreditsWindow(origin_widget=self._credits_button), 464 # group_id='mainmenutop', 465 ) 466 467 def _howtoplay(self) -> None: 468 # pylint: disable=cyclic-import 469 from bauiv1lib.helpui import HelpWindow 470 471 # no-op if we're not currently in control. 472 if not self.can_change_main_window(): 473 return 474 475 self.main_window_replace( 476 HelpWindow(origin_widget=self._how_to_play_button), 477 ) 478 479 def _save_state(self) -> None: 480 try: 481 sel = self._root_widget.get_selected_child() 482 if sel == self._play_button: 483 sel_name = 'Start' 484 elif sel == self._gather_button: 485 sel_name = 'Gather' 486 elif sel == self._watch_button: 487 sel_name = 'Watch' 488 elif sel == self._how_to_play_button: 489 sel_name = 'HowToPlay' 490 elif sel == self._credits_button: 491 sel_name = 'Credits' 492 elif sel == self._quit_button: 493 sel_name = 'Quit' 494 elif sel == self._demo_menu_button: 495 sel_name = 'DemoMenu' 496 else: 497 print(f'Unknown widget in main menu selection: {sel}.') 498 sel_name = 'Start' 499 bui.app.ui_v1.window_states[type(self)] = {'sel_name': sel_name} 500 except Exception: 501 logging.exception('Error saving state for %s.', self) 502 503 def _restore_state(self) -> None: 504 try: 505 506 sel: bui.Widget | None 507 508 sel_name = bui.app.ui_v1.window_states.get(type(self), {}).get( 509 'sel_name' 510 ) 511 assert isinstance(sel_name, (str, type(None))) 512 if sel_name is None: 513 sel_name = 'Start' 514 if sel_name == 'HowToPlay': 515 sel = self._how_to_play_button 516 elif sel_name == 'Gather': 517 sel = self._gather_button 518 elif sel_name == 'Watch': 519 sel = self._watch_button 520 elif sel_name == 'Credits': 521 sel = self._credits_button 522 elif sel_name == 'Quit': 523 sel = self._quit_button 524 elif sel_name == 'DemoMenu': 525 sel = self._demo_menu_button 526 else: 527 sel = self._play_button 528 if sel is not None: 529 bui.containerwidget(edit=self._root_widget, selected_child=sel) 530 531 except Exception: 532 logging.exception('Error restoring state for %s.', self) 533 534 def _gather_press(self) -> None: 535 # pylint: disable=cyclic-import 536 from bauiv1lib.gather import GatherWindow 537 538 # no-op if we're not currently in control. 539 if not self.can_change_main_window(): 540 return 541 542 self.main_window_replace( 543 GatherWindow(origin_widget=self._gather_button) 544 ) 545 546 def _watch_press(self) -> None: 547 # pylint: disable=cyclic-import 548 from bauiv1lib.watch import WatchWindow 549 550 # no-op if we're not currently in control. 551 if not self.can_change_main_window(): 552 return 553 554 self.main_window_replace( 555 WatchWindow(origin_widget=self._watch_button), 556 ) 557 558 def _play_press(self) -> None: 559 # pylint: disable=cyclic-import 560 from bauiv1lib.play import PlayWindow 561 562 # no-op if we're not currently in control. 563 if not self.can_change_main_window(): 564 return 565 566 classic = bui.app.classic 567 if classic is not None: 568 classic.selecting_private_party_playlist = False 569 570 self.main_window_replace(PlayWindow(origin_widget=self._play_button))
class
MainMenuWindow(bauiv1._uitypes.MainWindow):
18class MainMenuWindow(bui.MainWindow): 19 """The main menu window.""" 20 21 def __init__( 22 self, 23 transition: str | None = 'in_right', 24 origin_widget: bui.Widget | None = None, 25 ): 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('Main Menu') 32 self._show_remote_app_info_on_first_launch() 33 34 # Make a vanilla container; we'll modify it to our needs in 35 # refresh. 36 super().__init__( 37 root_widget=bui.containerwidget( 38 toolbar_visibility=('menu_full_no_back') 39 ), 40 transition=transition, 41 origin_widget=origin_widget, 42 ) 43 44 # Grab this stuff in case it changes. 45 self._is_demo = bui.app.env.demo 46 self._is_arcade = bui.app.env.arcade 47 48 self._tdelay = 0.0 49 self._t_delay_inc = 0.02 50 self._t_delay_play = 1.7 51 self._use_autoselect = True 52 self._button_width = 200.0 53 self._button_height = 45.0 54 self._width = 100.0 55 self._height = 100.0 56 self._demo_menu_button: bui.Widget | None = None 57 self._gather_button: bui.Widget | None = None 58 self._play_button: bui.Widget | None = None 59 self._watch_button: bui.Widget | None = None 60 self._how_to_play_button: bui.Widget | None = None 61 self._credits_button: bui.Widget | None = None 62 63 self._refresh() 64 65 self._restore_state() 66 67 @override 68 def on_main_window_close(self) -> None: 69 self._save_state() 70 71 @override 72 def get_main_window_state(self) -> bui.MainWindowState: 73 # Support recreating our window for back/refresh purposes. 74 return self.do_get_main_window_state() 75 76 @classmethod 77 def do_get_main_window_state(cls) -> bui.MainWindowState: 78 """Classmethod to gen a windowstate for the main menu.""" 79 return bui.BasicMainWindowState( 80 create_call=lambda transition, origin_widget: cls( 81 transition=transition, origin_widget=origin_widget 82 ) 83 ) 84 85 @staticmethod 86 def _preload_modules() -> None: 87 """Preload modules we use; avoids hitches (called in bg thread).""" 88 # pylint: disable=cyclic-import 89 import bauiv1lib.getremote as _unused 90 import bauiv1lib.confirm as _unused2 91 import bauiv1lib.store.button as _unused3 92 import bauiv1lib.account.settings as _unused5 93 import bauiv1lib.store.browser as _unused6 94 import bauiv1lib.credits as _unused7 95 import bauiv1lib.helpui as _unused8 96 import bauiv1lib.settings.allsettings as _unused9 97 import bauiv1lib.gather as _unused10 98 import bauiv1lib.watch as _unused11 99 import bauiv1lib.play as _unused12 100 101 def _show_remote_app_info_on_first_launch(self) -> None: 102 app = bui.app 103 assert app.classic is not None 104 # The first time the non-in-game menu pops up, we might wanna 105 # show a 'get-remote-app' dialog in front of it. 106 if app.classic.first_main_menu: 107 app.classic.first_main_menu = False 108 try: 109 force_test = False 110 bs.get_local_active_input_devices_count() 111 if ( 112 (app.env.tv or app.classic.platform == 'mac') 113 and bui.app.config.get('launchCount', 0) <= 1 114 ) or force_test: 115 116 def _check_show_bs_remote_window() -> None: 117 try: 118 from bauiv1lib.getremote import GetBSRemoteWindow 119 120 bui.getsound('swish').play() 121 GetBSRemoteWindow() 122 except Exception: 123 logging.exception( 124 'Error showing get-remote window.' 125 ) 126 127 bui.apptimer(2.5, _check_show_bs_remote_window) 128 except Exception: 129 logging.exception('Error showing get-remote-app info.') 130 131 def get_play_button(self) -> bui.Widget | None: 132 """Return the play button.""" 133 return self._play_button 134 135 def _refresh(self) -> None: 136 # pylint: disable=too-many-statements 137 # pylint: disable=too-many-locals 138 139 classic = bui.app.classic 140 assert classic is not None 141 142 # Clear everything that was there. 143 children = self._root_widget.get_children() 144 for child in children: 145 child.delete() 146 147 self._tdelay = 0.0 148 self._t_delay_inc = 0.0 149 self._t_delay_play = 0.0 150 self._button_width = 200.0 151 self._button_height = 45.0 152 153 self._r = 'mainMenu' 154 155 app = bui.app 156 assert app.classic is not None 157 158 self._have_quit_button = app.classic.platform in ( 159 'windows', 160 'mac', 161 'linux', 162 ) 163 164 if not classic.did_menu_intro: 165 self._tdelay = 1.7 166 self._t_delay_inc = 0.05 167 self._t_delay_play = 1.7 168 classic.did_menu_intro = True 169 170 self._width = 400.0 171 self._height = 200.0 172 173 play_button_width = self._button_width * 0.65 174 play_button_height = self._button_height * 1.1 175 play_button_scale = 1.7 176 hspace = 20.0 177 side_button_width = self._button_width * 0.4 178 side_button_height = side_button_width 179 side_button_scale = 0.95 180 side_button_y_offs = 5.0 181 hspace2 = 15.0 182 side_button_2_width = self._button_width * 1.0 183 side_button_2_height = side_button_2_width * 0.3 184 side_button_2_y_offs = 10.0 185 side_button_2_scale = 0.5 186 187 uiscale = bui.app.ui_v1.uiscale 188 if uiscale is bui.UIScale.SMALL: 189 root_widget_scale = 1.3 190 button_y_offs = -20.0 191 self._button_height *= 1.3 192 elif uiscale is bui.UIScale.MEDIUM: 193 root_widget_scale = 1.3 194 button_y_offs = -55.0 195 self._button_height *= 1.25 196 else: 197 root_widget_scale = 1.0 198 button_y_offs = -90.0 199 self._button_height *= 1.2 200 201 bui.containerwidget( 202 edit=self._root_widget, 203 size=(self._width, self._height), 204 background=False, 205 scale=root_widget_scale, 206 ) 207 208 # In kiosk mode, provide a button to get back to the kiosk menu. 209 if bui.app.env.demo or bui.app.env.arcade: 210 # h, v, scale = positions[self._p_index] 211 h = self._width * 0.5 212 v = button_y_offs 213 scale = 1.0 214 this_b_width = self._button_width * 0.4 * scale 215 demo_menu_delay = ( 216 0.0 217 if self._t_delay_play == 0.0 218 else max(0, self._t_delay_play + 0.1) 219 ) 220 self._demo_menu_button = bui.buttonwidget( 221 parent=self._root_widget, 222 position=(self._width * 0.5 - this_b_width * 0.5, v + 90), 223 size=(this_b_width, 45), 224 autoselect=True, 225 color=(0.45, 0.55, 0.45), 226 textcolor=(0.7, 0.8, 0.7), 227 label=bui.Lstr( 228 resource=( 229 'modeArcadeText' 230 if bui.app.env.arcade 231 else 'modeDemoText' 232 ) 233 ), 234 transition_delay=demo_menu_delay, 235 on_activate_call=self.main_window_back, 236 ) 237 else: 238 self._demo_menu_button = None 239 240 # Gather button 241 h = self._width * 0.5 242 h = ( 243 self._width * 0.5 244 - play_button_width * play_button_scale * 0.5 245 - hspace 246 - side_button_width * side_button_scale * 0.5 247 ) 248 v = button_y_offs + side_button_y_offs 249 self._gather_button = btn = bui.buttonwidget( 250 parent=self._root_widget, 251 position=(h - side_button_width * side_button_scale * 0.5, v), 252 size=(side_button_width, side_button_height), 253 scale=side_button_scale, 254 autoselect=self._use_autoselect, 255 button_type='square', 256 label='', 257 transition_delay=self._tdelay, 258 on_activate_call=self._gather_press, 259 ) 260 bui.textwidget( 261 parent=self._root_widget, 262 position=(h, v + side_button_height * side_button_scale * 0.25), 263 size=(0, 0), 264 scale=0.75, 265 transition_delay=self._tdelay, 266 draw_controller=btn, 267 color=(0.75, 1.0, 0.7), 268 maxwidth=side_button_width * side_button_scale * 0.8, 269 text=bui.Lstr(resource='gatherWindow.titleText'), 270 h_align='center', 271 v_align='center', 272 ) 273 icon_size = side_button_width * side_button_scale * 0.63 274 bui.imagewidget( 275 parent=self._root_widget, 276 size=(icon_size, icon_size), 277 draw_controller=btn, 278 transition_delay=self._tdelay, 279 position=( 280 h - 0.5 * icon_size, 281 v 282 + 0.65 * side_button_height * side_button_scale 283 - 0.5 * icon_size, 284 ), 285 texture=bui.gettexture('usersButton'), 286 ) 287 288 h -= ( 289 side_button_width * side_button_scale * 0.5 290 + hspace2 291 + side_button_2_width * side_button_2_scale 292 ) 293 v = button_y_offs + side_button_2_y_offs 294 295 btn = bui.buttonwidget( 296 parent=self._root_widget, 297 position=(h, v), 298 autoselect=self._use_autoselect, 299 size=(side_button_2_width, side_button_2_height * 2.0), 300 button_type='square', 301 scale=side_button_2_scale, 302 label=bui.Lstr(resource=f'{self._r}.howToPlayText'), 303 transition_delay=self._tdelay, 304 on_activate_call=self._howtoplay, 305 ) 306 self._how_to_play_button = btn 307 308 # Play button. 309 h = self._width * 0.5 310 v = button_y_offs 311 assert play_button_width is not None 312 assert play_button_height is not None 313 self._play_button = start_button = bui.buttonwidget( 314 parent=self._root_widget, 315 position=(h - play_button_width * 0.5 * play_button_scale, v), 316 size=(play_button_width, play_button_height), 317 autoselect=self._use_autoselect, 318 scale=play_button_scale, 319 text_res_scale=2.0, 320 label=bui.Lstr(resource='playText'), 321 transition_delay=self._t_delay_play, 322 on_activate_call=self._play_press, 323 ) 324 bui.containerwidget( 325 edit=self._root_widget, 326 start_button=start_button, 327 selected_child=start_button, 328 ) 329 330 self._tdelay += self._t_delay_inc 331 332 h = ( 333 self._width * 0.5 334 + play_button_width * play_button_scale * 0.5 335 + hspace 336 + side_button_width * side_button_scale * 0.5 337 ) 338 v = button_y_offs + side_button_y_offs 339 self._watch_button = btn = bui.buttonwidget( 340 parent=self._root_widget, 341 position=(h - side_button_width * side_button_scale * 0.5, v), 342 size=(side_button_width, side_button_height), 343 scale=side_button_scale, 344 autoselect=self._use_autoselect, 345 button_type='square', 346 label='', 347 transition_delay=self._tdelay, 348 on_activate_call=self._watch_press, 349 ) 350 bui.textwidget( 351 parent=self._root_widget, 352 position=(h, v + side_button_height * side_button_scale * 0.25), 353 size=(0, 0), 354 scale=0.75, 355 transition_delay=self._tdelay, 356 color=(0.75, 1.0, 0.7), 357 draw_controller=btn, 358 maxwidth=side_button_width * side_button_scale * 0.8, 359 text=bui.Lstr(resource='watchWindow.titleText'), 360 h_align='center', 361 v_align='center', 362 ) 363 icon_size = side_button_width * side_button_scale * 0.63 364 bui.imagewidget( 365 parent=self._root_widget, 366 size=(icon_size, icon_size), 367 draw_controller=btn, 368 transition_delay=self._tdelay, 369 position=( 370 h - 0.5 * icon_size, 371 v 372 + 0.65 * side_button_height * side_button_scale 373 - 0.5 * icon_size, 374 ), 375 texture=bui.gettexture('tv'), 376 ) 377 378 # Credits button. 379 self._tdelay += self._t_delay_inc 380 381 h += side_button_width * side_button_scale * 0.5 + hspace2 382 v = button_y_offs + side_button_2_y_offs 383 384 if self._have_quit_button: 385 v += 1.17 * side_button_2_height * side_button_2_scale 386 387 self._credits_button = bui.buttonwidget( 388 parent=self._root_widget, 389 position=(h, v), 390 button_type=None if self._have_quit_button else 'square', 391 size=( 392 side_button_2_width, 393 side_button_2_height * (1.0 if self._have_quit_button else 2.0), 394 ), 395 scale=side_button_2_scale, 396 autoselect=self._use_autoselect, 397 label=bui.Lstr(resource=f'{self._r}.creditsText'), 398 transition_delay=self._tdelay, 399 on_activate_call=self._credits, 400 ) 401 self._tdelay += self._t_delay_inc 402 403 self._quit_button: bui.Widget | None 404 if self._have_quit_button: 405 v -= 1.1 * side_button_2_height * side_button_2_scale 406 self._quit_button = quit_button = bui.buttonwidget( 407 parent=self._root_widget, 408 autoselect=self._use_autoselect, 409 position=(h, v), 410 size=(side_button_2_width, side_button_2_height), 411 scale=side_button_2_scale, 412 label=bui.Lstr( 413 resource=self._r 414 + ( 415 '.quitText' 416 if 'Mac' in app.classic.legacy_user_agent_string 417 else '.exitGameText' 418 ) 419 ), 420 on_activate_call=self._quit, 421 transition_delay=self._tdelay, 422 ) 423 424 bui.containerwidget( 425 edit=self._root_widget, cancel_button=quit_button 426 ) 427 self._tdelay += self._t_delay_inc 428 else: 429 self._quit_button = None 430 431 # If we're not in-game, have no quit button, and this is 432 # android, we want back presses to quit our activity. 433 if app.classic.platform == 'android': 434 435 def _do_quit() -> None: 436 bui.quit(confirm=True, quit_type=bui.QuitType.BACK) 437 438 bui.containerwidget( 439 edit=self._root_widget, on_cancel_call=_do_quit 440 ) 441 442 def _quit(self) -> None: 443 # pylint: disable=cyclic-import 444 from bauiv1lib.confirm import QuitWindow 445 446 # no-op if we're not currently in control. 447 if not self.can_change_main_window(): 448 return 449 450 # Note: Normally we should go through bui.quit(confirm=True) but 451 # invoking the window directly lets us scale it up from the 452 # button. 453 QuitWindow(origin_widget=self._quit_button) 454 455 def _credits(self) -> None: 456 # pylint: disable=cyclic-import 457 from bauiv1lib.credits import CreditsWindow 458 459 # no-op if we're not currently in control. 460 if not self.can_change_main_window(): 461 return 462 463 self.main_window_replace( 464 CreditsWindow(origin_widget=self._credits_button), 465 # group_id='mainmenutop', 466 ) 467 468 def _howtoplay(self) -> None: 469 # pylint: disable=cyclic-import 470 from bauiv1lib.helpui import HelpWindow 471 472 # no-op if we're not currently in control. 473 if not self.can_change_main_window(): 474 return 475 476 self.main_window_replace( 477 HelpWindow(origin_widget=self._how_to_play_button), 478 ) 479 480 def _save_state(self) -> None: 481 try: 482 sel = self._root_widget.get_selected_child() 483 if sel == self._play_button: 484 sel_name = 'Start' 485 elif sel == self._gather_button: 486 sel_name = 'Gather' 487 elif sel == self._watch_button: 488 sel_name = 'Watch' 489 elif sel == self._how_to_play_button: 490 sel_name = 'HowToPlay' 491 elif sel == self._credits_button: 492 sel_name = 'Credits' 493 elif sel == self._quit_button: 494 sel_name = 'Quit' 495 elif sel == self._demo_menu_button: 496 sel_name = 'DemoMenu' 497 else: 498 print(f'Unknown widget in main menu selection: {sel}.') 499 sel_name = 'Start' 500 bui.app.ui_v1.window_states[type(self)] = {'sel_name': sel_name} 501 except Exception: 502 logging.exception('Error saving state for %s.', self) 503 504 def _restore_state(self) -> None: 505 try: 506 507 sel: bui.Widget | None 508 509 sel_name = bui.app.ui_v1.window_states.get(type(self), {}).get( 510 'sel_name' 511 ) 512 assert isinstance(sel_name, (str, type(None))) 513 if sel_name is None: 514 sel_name = 'Start' 515 if sel_name == 'HowToPlay': 516 sel = self._how_to_play_button 517 elif sel_name == 'Gather': 518 sel = self._gather_button 519 elif sel_name == 'Watch': 520 sel = self._watch_button 521 elif sel_name == 'Credits': 522 sel = self._credits_button 523 elif sel_name == 'Quit': 524 sel = self._quit_button 525 elif sel_name == 'DemoMenu': 526 sel = self._demo_menu_button 527 else: 528 sel = self._play_button 529 if sel is not None: 530 bui.containerwidget(edit=self._root_widget, selected_child=sel) 531 532 except Exception: 533 logging.exception('Error restoring state for %s.', self) 534 535 def _gather_press(self) -> None: 536 # pylint: disable=cyclic-import 537 from bauiv1lib.gather import GatherWindow 538 539 # no-op if we're not currently in control. 540 if not self.can_change_main_window(): 541 return 542 543 self.main_window_replace( 544 GatherWindow(origin_widget=self._gather_button) 545 ) 546 547 def _watch_press(self) -> None: 548 # pylint: disable=cyclic-import 549 from bauiv1lib.watch import WatchWindow 550 551 # no-op if we're not currently in control. 552 if not self.can_change_main_window(): 553 return 554 555 self.main_window_replace( 556 WatchWindow(origin_widget=self._watch_button), 557 ) 558 559 def _play_press(self) -> None: 560 # pylint: disable=cyclic-import 561 from bauiv1lib.play import PlayWindow 562 563 # no-op if we're not currently in control. 564 if not self.can_change_main_window(): 565 return 566 567 classic = bui.app.classic 568 if classic is not None: 569 classic.selecting_private_party_playlist = False 570 571 self.main_window_replace(PlayWindow(origin_widget=self._play_button))
The main menu window.
MainMenuWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
21 def __init__( 22 self, 23 transition: str | None = 'in_right', 24 origin_widget: bui.Widget | None = None, 25 ): 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('Main Menu') 32 self._show_remote_app_info_on_first_launch() 33 34 # Make a vanilla container; we'll modify it to our needs in 35 # refresh. 36 super().__init__( 37 root_widget=bui.containerwidget( 38 toolbar_visibility=('menu_full_no_back') 39 ), 40 transition=transition, 41 origin_widget=origin_widget, 42 ) 43 44 # Grab this stuff in case it changes. 45 self._is_demo = bui.app.env.demo 46 self._is_arcade = bui.app.env.arcade 47 48 self._tdelay = 0.0 49 self._t_delay_inc = 0.02 50 self._t_delay_play = 1.7 51 self._use_autoselect = True 52 self._button_width = 200.0 53 self._button_height = 45.0 54 self._width = 100.0 55 self._height = 100.0 56 self._demo_menu_button: bui.Widget | None = None 57 self._gather_button: bui.Widget | None = None 58 self._play_button: bui.Widget | None = None 59 self._watch_button: bui.Widget | None = None 60 self._how_to_play_button: bui.Widget | None = None 61 self._credits_button: bui.Widget | None = None 62 63 self._refresh() 64 65 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
on_main_window_close(self) -> None:
Called before transitioning out a main window.
A good opportunity to save window state/etc.
71 @override 72 def get_main_window_state(self) -> bui.MainWindowState: 73 # Support recreating our window for back/refresh purposes. 74 return self.do_get_main_window_state()
Return a WindowState to recreate this window, if supported.
76 @classmethod 77 def do_get_main_window_state(cls) -> bui.MainWindowState: 78 """Classmethod to gen a windowstate for the main menu.""" 79 return bui.BasicMainWindowState( 80 create_call=lambda transition, origin_widget: cls( 81 transition=transition, origin_widget=origin_widget 82 ) 83 )
Classmethod to gen a windowstate for the main menu.
Inherited Members
- bauiv1._uitypes.MainWindow
- main_window_back_state
- main_window_close
- can_change_main_window
- main_window_back
- main_window_replace
- bauiv1._uitypes.Window
- get_root_widget