bauiv1lib.play
Provides the top level play window.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides the top level play window.""" 4 5from __future__ import annotations 6 7import logging 8from typing import override, TYPE_CHECKING 9 10import bascenev1 as bs 11import bauiv1 as bui 12 13if TYPE_CHECKING: 14 from bauiv1 import MainWindowState 15 16 17class PlaylistSelectContext: 18 """For using PlayWindow to select a playlist instead of running game.""" 19 20 back_state: MainWindowState | None = None 21 22 23class PlayWindow(bui.MainWindow): 24 """Window for selecting overall play type.""" 25 26 def __init__( 27 self, 28 transition: str | None = 'in_right', 29 origin_widget: bui.Widget | None = None, 30 playlist_select_context: PlaylistSelectContext | None = None, 31 ): 32 # pylint: disable=too-many-statements 33 # pylint: disable=too-many-locals 34 35 import bacommon.cloud 36 37 # TEMP TESTING 38 if bool(False): 39 print('HELLO FROM TEST') 40 plus = bui.app.plus 41 assert plus is not None 42 plus.cloud.send_message_cb( 43 bacommon.cloud.SecureDataCheckMessage( 44 data=b'fo', signature=b'mo' 45 ), 46 on_response=lambda r: print('GOT CHECK RESPONSE', r), 47 ) 48 plus.cloud.send_message_cb( 49 bacommon.cloud.SecureDataCheckerRequest(), 50 on_response=lambda r: print('GOT CHECKER RESPONSE', r), 51 ) 52 53 # Preload some modules we use in a background thread so we won't 54 # have a visual hitch when the user taps them. 55 bui.app.threadpool.submit_no_wait(self._preload_modules) 56 57 classic = bui.app.classic 58 assert classic is not None 59 60 self._playlist_select_context = playlist_select_context 61 62 uiscale = bui.app.ui_v1.uiscale 63 width = 1300 if uiscale is bui.UIScale.SMALL else 1000 64 height = 1000 if uiscale is bui.UIScale.SMALL else 550 65 66 button_width = 400.0 67 button_height = 360.0 68 button_spacing = 3.0 69 70 if origin_widget is not None: 71 72 # Need to store this ourself since we can function as a 73 # non-main window. 74 self._transition_out = 'out_scale' 75 else: 76 self._transition_out = 'out_right' 77 78 self._r = 'playWindow' 79 80 # Do some fancy math to fill all available screen area up to the 81 # size of our backing container. This lets us fit to the exact 82 # screen shape at small ui scale. 83 screensize = bui.get_virtual_screen_size() 84 safesize = bui.get_virtual_safe_area_size() 85 86 # We're a generally widescreen shaped window, so bump our 87 # overall scale up a bit when screen width is wider than safe 88 # bounds to take advantage of the extra space. 89 smallscale = min(1.6, 1.35 * screensize[0] / safesize[0]) 90 91 scale = ( 92 smallscale 93 if uiscale is bui.UIScale.SMALL 94 else 0.9 if uiscale is bui.UIScale.MEDIUM else 0.8 95 ) 96 # Calc screen size in our local container space and clamp to a 97 # bit smaller than our container size. 98 target_height = min(height - 80, screensize[1] / scale) 99 100 # To get top/left coords, go to the center of our window and 101 # offset by half the width/height of our target area. 102 yoffs = 0.5 * height + 0.5 * target_height + 30.0 103 104 super().__init__( 105 root_widget=bui.containerwidget( 106 size=(width, height), 107 toolbar_visibility=( 108 'menu_full' 109 if playlist_select_context is None 110 else 'menu_minimal' 111 ), 112 scale=scale, 113 ), 114 transition=transition, 115 origin_widget=origin_widget, 116 # We're affected by screen size only at small ui-scale. 117 refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, 118 ) 119 120 self._back_button: bui.Widget | None 121 if uiscale is bui.UIScale.SMALL: 122 self._back_button = None 123 bui.containerwidget( 124 edit=self._root_widget, 125 on_cancel_call=self.main_window_back, 126 ) 127 else: 128 self._back_button = bui.buttonwidget( 129 parent=self._root_widget, 130 position=(50, yoffs - 100), 131 size=(60, 60), 132 scale=1.1, 133 text_res_scale=1.5, 134 text_scale=1.2, 135 autoselect=True, 136 label=bui.charstr(bui.SpecialChar.BACK), 137 button_type='backSmall', 138 on_activate_call=self.main_window_back, 139 ) 140 bui.containerwidget( 141 edit=self._root_widget, cancel_button=self._back_button 142 ) 143 144 bui.textwidget( 145 parent=self._root_widget, 146 position=( 147 width * 0.5, 148 yoffs - (50 if uiscale is bui.UIScale.SMALL else 70), 149 ), 150 size=(0, 0), 151 text=bui.Lstr( 152 resource=( 153 (f'{self._r}.titleText') 154 if self._playlist_select_context is None 155 else 'playlistsText' 156 ) 157 ), 158 scale=1.2 if uiscale is bui.UIScale.SMALL else 1.7, 159 res_scale=2.0, 160 maxwidth=250, 161 color=bui.app.ui_v1.heading_color, 162 h_align='center', 163 v_align='center', 164 ) 165 166 scl = 0.75 if self._playlist_select_context is None else 0.68 167 v = height * 0.5 - button_height * scl * 0.5 - 20.0 168 clr = (0.6, 0.7, 0.6, 1.0) 169 170 bcount = 3 if self._playlist_select_context is None else 2 171 172 total_b_width = ( 173 bcount * button_width * scl + (bcount - 1) * button_spacing 174 ) 175 hoffs = (width - total_b_width) * 0.5 176 177 self._lineup_tex = bui.gettexture('playerLineup') 178 angry_computer_transparent_mesh = bui.getmesh( 179 'angryComputerTransparent' 180 ) 181 self._lineup_1_transparent_mesh = bui.getmesh( 182 'playerLineup1Transparent' 183 ) 184 self._lineup_2_transparent_mesh = bui.getmesh( 185 'playerLineup2Transparent' 186 ) 187 self._lineup_3_transparent_mesh = bui.getmesh( 188 'playerLineup3Transparent' 189 ) 190 self._lineup_4_transparent_mesh = bui.getmesh( 191 'playerLineup4Transparent' 192 ) 193 self._eyes_mesh = bui.getmesh('plasticEyesTransparent') 194 195 self._coop_button: bui.Widget | None = None 196 197 # Only show coop button in regular variant. 198 if self._playlist_select_context is None: 199 self._coop_button = btn = bui.buttonwidget( 200 parent=self._root_widget, 201 position=(hoffs, v), 202 size=( 203 scl * button_width, 204 scl * button_height, 205 ), 206 extra_touch_border_scale=0.1, 207 autoselect=True, 208 label='', 209 button_type='square', 210 on_activate_call=self._coop, 211 ) 212 213 if uiscale is bui.UIScale.SMALL: 214 bui.widget( 215 edit=btn, 216 left_widget=bui.get_special_widget('back_button'), 217 ) 218 bui.widget( 219 edit=btn, 220 up_widget=bui.get_special_widget('account_button'), 221 ) 222 bui.widget( 223 edit=btn, 224 down_widget=bui.get_special_widget('settings_button'), 225 ) 226 227 self._draw_dude( 228 0, 229 btn, 230 hoffs, 231 v, 232 scl, 233 position=(140, 30), 234 color=(0.72, 0.4, 1.0), 235 ) 236 self._draw_dude( 237 1, 238 btn, 239 hoffs, 240 v, 241 scl, 242 position=(185, 53), 243 color=(0.71, 0.5, 1.0), 244 ) 245 self._draw_dude( 246 2, 247 btn, 248 hoffs, 249 v, 250 scl, 251 position=(220, 27), 252 color=(0.67, 0.44, 1.0), 253 ) 254 self._draw_dude( 255 3, btn, hoffs, v, scl, position=(255, 57), color=(0.7, 0.3, 1.0) 256 ) 257 bui.imagewidget( 258 parent=self._root_widget, 259 draw_controller=btn, 260 position=(hoffs + scl * 230, v + scl * 153), 261 size=(scl * 115, scl * 115), 262 texture=self._lineup_tex, 263 mesh_transparent=angry_computer_transparent_mesh, 264 ) 265 266 bui.textwidget( 267 parent=self._root_widget, 268 draw_controller=btn, 269 position=(hoffs + scl * (-10), v + scl * 95), 270 size=(scl * button_width, scl * 50), 271 text=bui.Lstr( 272 resource='playModes.singlePlayerCoopText', 273 fallback_resource='playModes.coopText', 274 ), 275 maxwidth=scl * button_width * 0.7, 276 res_scale=1.5, 277 h_align='center', 278 v_align='center', 279 color=(0.7, 0.9, 0.7, 1.0), 280 scale=scl * 1.5, 281 ) 282 283 bui.textwidget( 284 parent=self._root_widget, 285 draw_controller=btn, 286 position=(hoffs + scl * (-10), v + (scl * 54)), 287 size=(scl * button_width, scl * 30), 288 text=bui.Lstr(resource=f'{self._r}.oneToFourPlayersText'), 289 h_align='center', 290 v_align='center', 291 scale=0.83 * scl, 292 flatness=1.0, 293 maxwidth=scl * button_width * 0.7, 294 color=clr, 295 ) 296 297 hoffs += scl * button_width + button_spacing 298 299 self._teams_button = btn = bui.buttonwidget( 300 parent=self._root_widget, 301 position=(hoffs, v), 302 size=( 303 scl * button_width, 304 scl * button_height, 305 ), 306 extra_touch_border_scale=0.1, 307 autoselect=True, 308 label='', 309 button_type='square', 310 on_activate_call=self._team_tourney, 311 ) 312 313 xxx = -14 314 self._draw_dude( 315 2, 316 btn, 317 hoffs, 318 v, 319 scl, 320 position=(xxx + 148, 30), 321 color=(0.2, 0.4, 1.0), 322 ) 323 self._draw_dude( 324 3, 325 btn, 326 hoffs, 327 v, 328 scl, 329 position=(xxx + 181, 53), 330 color=(0.3, 0.4, 1.0), 331 ) 332 self._draw_dude( 333 1, 334 btn, 335 hoffs, 336 v, 337 scl, 338 position=(xxx + 216, 33), 339 color=(0.3, 0.5, 1.0), 340 ) 341 self._draw_dude( 342 0, 343 btn, 344 hoffs, 345 v, 346 scl, 347 position=(xxx + 245, 57), 348 color=(0.3, 0.5, 1.0), 349 ) 350 351 xxx = 155 352 self._draw_dude( 353 0, 354 btn, 355 hoffs, 356 v, 357 scl, 358 position=(xxx + 151, 30), 359 color=(1.0, 0.5, 0.4), 360 ) 361 self._draw_dude( 362 1, 363 btn, 364 hoffs, 365 v, 366 scl, 367 position=(xxx + 189, 53), 368 color=(1.0, 0.58, 0.58), 369 ) 370 self._draw_dude( 371 3, 372 btn, 373 hoffs, 374 v, 375 scl, 376 position=(xxx + 223, 27), 377 color=(1.0, 0.5, 0.5), 378 ) 379 self._draw_dude( 380 2, 381 btn, 382 hoffs, 383 v, 384 scl, 385 position=(xxx + 257, 57), 386 color=(1.0, 0.5, 0.5), 387 ) 388 389 bui.textwidget( 390 parent=self._root_widget, 391 draw_controller=btn, 392 position=(hoffs + scl * (-10), v + scl * 95), 393 size=(scl * button_width, scl * 50), 394 text=bui.Lstr( 395 resource='playModes.teamsText', fallback_resource='teamsText' 396 ), 397 res_scale=1.5, 398 maxwidth=scl * button_width * 0.7, 399 h_align='center', 400 v_align='center', 401 color=(0.7, 0.9, 0.7, 1.0), 402 scale=scl * 1.5, 403 ) 404 bui.textwidget( 405 parent=self._root_widget, 406 draw_controller=btn, 407 position=(hoffs + scl * (-10), v + (scl * 54)), 408 size=(scl * button_width, scl * 30), 409 text=bui.Lstr(resource=f'{self._r}.twoToEightPlayersText'), 410 h_align='center', 411 v_align='center', 412 res_scale=1.5, 413 scale=0.83 * scl, 414 flatness=1.0, 415 maxwidth=scl * button_width * 0.7, 416 color=clr, 417 ) 418 419 hoffs += scl * button_width + button_spacing 420 self._free_for_all_button = btn = bui.buttonwidget( 421 parent=self._root_widget, 422 position=(hoffs, v), 423 size=(scl * button_width, scl * button_height), 424 extra_touch_border_scale=0.1, 425 autoselect=True, 426 label='', 427 button_type='square', 428 on_activate_call=self._free_for_all, 429 ) 430 431 xxx = -5 432 self._draw_dude( 433 0, 434 btn, 435 hoffs, 436 v, 437 scl, 438 position=(xxx + 140, 30), 439 color=(0.4, 1.0, 0.4), 440 ) 441 self._draw_dude( 442 3, 443 btn, 444 hoffs, 445 v, 446 scl, 447 position=(xxx + 185, 53), 448 color=(1.0, 0.4, 0.5), 449 ) 450 self._draw_dude( 451 1, 452 btn, 453 hoffs, 454 v, 455 scl, 456 position=(xxx + 220, 27), 457 color=(0.4, 0.5, 1.0), 458 ) 459 self._draw_dude( 460 2, 461 btn, 462 hoffs, 463 v, 464 scl, 465 position=(xxx + 255, 57), 466 color=(0.5, 1.0, 0.4), 467 ) 468 xxx = 140 469 self._draw_dude( 470 2, 471 btn, 472 hoffs, 473 v, 474 scl, 475 position=(xxx + 148, 30), 476 color=(1.0, 0.9, 0.4), 477 ) 478 self._draw_dude( 479 0, 480 btn, 481 hoffs, 482 v, 483 scl, 484 position=(xxx + 182, 53), 485 color=(0.7, 1.0, 0.5), 486 ) 487 self._draw_dude( 488 3, 489 btn, 490 hoffs, 491 v, 492 scl, 493 position=(xxx + 233, 27), 494 color=(0.7, 0.5, 0.9), 495 ) 496 self._draw_dude( 497 1, 498 btn, 499 hoffs, 500 v, 501 scl, 502 position=(xxx + 266, 53), 503 color=(0.4, 0.5, 0.8), 504 ) 505 bui.textwidget( 506 parent=self._root_widget, 507 draw_controller=btn, 508 position=(hoffs + scl * (-10), v + scl * 95), 509 size=(scl * button_width, scl * 50), 510 text=bui.Lstr( 511 resource='playModes.freeForAllText', 512 fallback_resource='freeForAllText', 513 ), 514 maxwidth=scl * button_width * 0.7, 515 h_align='center', 516 v_align='center', 517 color=(0.7, 0.9, 0.7, 1.0), 518 scale=scl * 1.5, 519 ) 520 bui.textwidget( 521 parent=self._root_widget, 522 draw_controller=btn, 523 position=(hoffs + scl * (-10), v + (scl * 54)), 524 size=(scl * button_width, scl * 30), 525 text=bui.Lstr(resource=f'{self._r}.twoToEightPlayersText'), 526 h_align='center', 527 v_align='center', 528 scale=0.83 * scl, 529 flatness=1.0, 530 maxwidth=scl * button_width * 0.7, 531 color=clr, 532 ) 533 534 if uiscale is bui.UIScale.SMALL: 535 bui.containerwidget( 536 edit=self._root_widget, 537 selected_child=( 538 self._coop_button 539 if self._playlist_select_context is None 540 else self._teams_button 541 ), 542 ) 543 else: 544 bui.containerwidget( 545 edit=self._root_widget, 546 selected_child=( 547 self._coop_button 548 if self._playlist_select_context is None 549 else self._teams_button 550 ), 551 ) 552 553 self._restore_state() 554 555 @override 556 def get_main_window_state(self) -> bui.MainWindowState: 557 # Support recreating our window for back/refresh purposes. 558 cls = type(self) 559 560 # Pull any values out of self here; if we do it in the lambda 561 # we'll keep our window alive inadvertantly. 562 playlist_select_context = self._playlist_select_context 563 return bui.BasicMainWindowState( 564 create_call=lambda transition, origin_widget: cls( 565 transition=transition, 566 origin_widget=origin_widget, 567 playlist_select_context=playlist_select_context, 568 ) 569 ) 570 571 @override 572 def on_main_window_close(self) -> None: 573 self._save_state() 574 575 @staticmethod 576 def _preload_modules() -> None: 577 """Preload modules we use; avoids hitches (called in bg thread).""" 578 import bauiv1lib.mainmenu as _unused1 579 import bauiv1lib.account as _unused2 580 import bauiv1lib.coop.browser as _unused3 581 import bauiv1lib.playlist.browser as _unused4 582 583 def _coop(self) -> None: 584 # pylint: disable=cyclic-import 585 from bauiv1lib.account.signin import show_sign_in_prompt 586 from bauiv1lib.coop.browser import CoopBrowserWindow 587 588 # no-op if we're not currently in control. 589 if not self.main_window_has_control(): 590 return 591 592 plus = bui.app.plus 593 assert plus is not None 594 595 if plus.get_v1_account_state() != 'signed_in': 596 show_sign_in_prompt() 597 return 598 599 self.main_window_replace( 600 CoopBrowserWindow(origin_widget=self._coop_button) 601 ) 602 603 def _team_tourney(self) -> None: 604 # pylint: disable=cyclic-import 605 from bauiv1lib.playlist.browser import PlaylistBrowserWindow 606 607 # no-op if we're not currently in control. 608 if not self.main_window_has_control(): 609 return 610 611 self.main_window_replace( 612 PlaylistBrowserWindow( 613 origin_widget=self._teams_button, 614 sessiontype=bs.DualTeamSession, 615 playlist_select_context=self._playlist_select_context, 616 ) 617 ) 618 619 def _free_for_all(self) -> None: 620 # pylint: disable=cyclic-import 621 from bauiv1lib.playlist.browser import PlaylistBrowserWindow 622 623 # no-op if we're not currently in control. 624 if not self.main_window_has_control(): 625 return 626 627 self.main_window_replace( 628 PlaylistBrowserWindow( 629 origin_widget=self._free_for_all_button, 630 sessiontype=bs.FreeForAllSession, 631 playlist_select_context=self._playlist_select_context, 632 ) 633 ) 634 635 def _draw_dude( 636 self, 637 i: int, 638 btn: bui.Widget, 639 hoffs: float, 640 v: float, 641 scl: float, 642 position: tuple[float, float], 643 color: tuple[float, float, float], 644 ) -> None: 645 # pylint: disable=too-many-positional-arguments 646 h_extra = -100 647 v_extra = 130 648 eye_color = ( 649 0.7 * 1.0 + 0.3 * color[0], 650 0.7 * 1.0 + 0.3 * color[1], 651 0.7 * 1.0 + 0.3 * color[2], 652 ) 653 if i == 0: 654 bui.imagewidget( 655 parent=self._root_widget, 656 draw_controller=btn, 657 position=( 658 hoffs + scl * (h_extra + position[0]), 659 v + scl * (v_extra + position[1]), 660 ), 661 size=(scl * 60, scl * 80), 662 color=color, 663 texture=self._lineup_tex, 664 mesh_transparent=self._lineup_1_transparent_mesh, 665 ) 666 bui.imagewidget( 667 parent=self._root_widget, 668 draw_controller=btn, 669 position=( 670 hoffs + scl * (h_extra + position[0] + 12), 671 v + scl * (v_extra + position[1] + 53), 672 ), 673 size=(scl * 36, scl * 18), 674 texture=self._lineup_tex, 675 color=eye_color, 676 mesh_transparent=self._eyes_mesh, 677 ) 678 elif i == 1: 679 bui.imagewidget( 680 parent=self._root_widget, 681 draw_controller=btn, 682 position=( 683 hoffs + scl * (h_extra + position[0]), 684 v + scl * (v_extra + position[1]), 685 ), 686 size=(scl * 45, scl * 90), 687 color=color, 688 texture=self._lineup_tex, 689 mesh_transparent=self._lineup_2_transparent_mesh, 690 ) 691 bui.imagewidget( 692 parent=self._root_widget, 693 draw_controller=btn, 694 position=( 695 hoffs + scl * (h_extra + position[0] + 5), 696 v + scl * (v_extra + position[1] + 67), 697 ), 698 size=(scl * 32, scl * 16), 699 texture=self._lineup_tex, 700 color=eye_color, 701 mesh_transparent=self._eyes_mesh, 702 ) 703 elif i == 2: 704 bui.imagewidget( 705 parent=self._root_widget, 706 draw_controller=btn, 707 position=( 708 hoffs + scl * (h_extra + position[0]), 709 v + scl * (v_extra + position[1]), 710 ), 711 size=(scl * 45, scl * 90), 712 color=color, 713 texture=self._lineup_tex, 714 mesh_transparent=self._lineup_3_transparent_mesh, 715 ) 716 bui.imagewidget( 717 parent=self._root_widget, 718 draw_controller=btn, 719 position=( 720 hoffs + scl * (h_extra + position[0] + 5), 721 v + scl * (v_extra + position[1] + 59), 722 ), 723 size=(scl * 34, scl * 17), 724 texture=self._lineup_tex, 725 color=eye_color, 726 mesh_transparent=self._eyes_mesh, 727 ) 728 elif i == 3: 729 bui.imagewidget( 730 parent=self._root_widget, 731 draw_controller=btn, 732 position=( 733 hoffs + scl * (h_extra + position[0]), 734 v + scl * (v_extra + position[1]), 735 ), 736 size=(scl * 48, scl * 96), 737 color=color, 738 texture=self._lineup_tex, 739 mesh_transparent=self._lineup_4_transparent_mesh, 740 ) 741 bui.imagewidget( 742 parent=self._root_widget, 743 draw_controller=btn, 744 position=( 745 hoffs + scl * (h_extra + position[0] + 2), 746 v + scl * (v_extra + position[1] + 62), 747 ), 748 size=(scl * 38, scl * 19), 749 texture=self._lineup_tex, 750 color=eye_color, 751 mesh_transparent=self._eyes_mesh, 752 ) 753 754 def _save_state(self) -> None: 755 try: 756 sel = self._root_widget.get_selected_child() 757 if sel == self._teams_button: 758 sel_name = 'Team Games' 759 elif self._coop_button is not None and sel == self._coop_button: 760 sel_name = 'Co-op Games' 761 elif sel == self._free_for_all_button: 762 sel_name = 'Free-for-All Games' 763 elif sel == self._back_button: 764 sel_name = 'Back' 765 else: 766 raise ValueError(f'unrecognized selection {sel}') 767 assert bui.app.classic is not None 768 bui.app.ui_v1.window_states[type(self)] = sel_name 769 except Exception: 770 logging.exception('Error saving state for %s.', self) 771 772 def _restore_state(self) -> None: 773 try: 774 assert bui.app.classic is not None 775 sel_name = bui.app.ui_v1.window_states.get(type(self)) 776 if sel_name == 'Team Games': 777 sel = self._teams_button 778 elif sel_name == 'Co-op Games' and self._coop_button is not None: 779 sel = self._coop_button 780 elif sel_name == 'Free-for-All Games': 781 sel = self._free_for_all_button 782 elif sel_name == 'Back' and self._back_button is not None: 783 sel = self._back_button 784 else: 785 sel = ( 786 self._coop_button 787 if self._coop_button is not None 788 else self._teams_button 789 ) 790 bui.containerwidget(edit=self._root_widget, selected_child=sel) 791 except Exception: 792 logging.exception('Error restoring state for %s.', self)
class
PlaylistSelectContext:
18class PlaylistSelectContext: 19 """For using PlayWindow to select a playlist instead of running game.""" 20 21 back_state: MainWindowState | None = None
For using PlayWindow to select a playlist instead of running game.
class
PlayWindow(bauiv1._uitypes.MainWindow):
24class PlayWindow(bui.MainWindow): 25 """Window for selecting overall play type.""" 26 27 def __init__( 28 self, 29 transition: str | None = 'in_right', 30 origin_widget: bui.Widget | None = None, 31 playlist_select_context: PlaylistSelectContext | None = None, 32 ): 33 # pylint: disable=too-many-statements 34 # pylint: disable=too-many-locals 35 36 import bacommon.cloud 37 38 # TEMP TESTING 39 if bool(False): 40 print('HELLO FROM TEST') 41 plus = bui.app.plus 42 assert plus is not None 43 plus.cloud.send_message_cb( 44 bacommon.cloud.SecureDataCheckMessage( 45 data=b'fo', signature=b'mo' 46 ), 47 on_response=lambda r: print('GOT CHECK RESPONSE', r), 48 ) 49 plus.cloud.send_message_cb( 50 bacommon.cloud.SecureDataCheckerRequest(), 51 on_response=lambda r: print('GOT CHECKER RESPONSE', r), 52 ) 53 54 # Preload some modules we use in a background thread so we won't 55 # have a visual hitch when the user taps them. 56 bui.app.threadpool.submit_no_wait(self._preload_modules) 57 58 classic = bui.app.classic 59 assert classic is not None 60 61 self._playlist_select_context = playlist_select_context 62 63 uiscale = bui.app.ui_v1.uiscale 64 width = 1300 if uiscale is bui.UIScale.SMALL else 1000 65 height = 1000 if uiscale is bui.UIScale.SMALL else 550 66 67 button_width = 400.0 68 button_height = 360.0 69 button_spacing = 3.0 70 71 if origin_widget is not None: 72 73 # Need to store this ourself since we can function as a 74 # non-main window. 75 self._transition_out = 'out_scale' 76 else: 77 self._transition_out = 'out_right' 78 79 self._r = 'playWindow' 80 81 # Do some fancy math to fill all available screen area up to the 82 # size of our backing container. This lets us fit to the exact 83 # screen shape at small ui scale. 84 screensize = bui.get_virtual_screen_size() 85 safesize = bui.get_virtual_safe_area_size() 86 87 # We're a generally widescreen shaped window, so bump our 88 # overall scale up a bit when screen width is wider than safe 89 # bounds to take advantage of the extra space. 90 smallscale = min(1.6, 1.35 * screensize[0] / safesize[0]) 91 92 scale = ( 93 smallscale 94 if uiscale is bui.UIScale.SMALL 95 else 0.9 if uiscale is bui.UIScale.MEDIUM else 0.8 96 ) 97 # Calc screen size in our local container space and clamp to a 98 # bit smaller than our container size. 99 target_height = min(height - 80, screensize[1] / scale) 100 101 # To get top/left coords, go to the center of our window and 102 # offset by half the width/height of our target area. 103 yoffs = 0.5 * height + 0.5 * target_height + 30.0 104 105 super().__init__( 106 root_widget=bui.containerwidget( 107 size=(width, height), 108 toolbar_visibility=( 109 'menu_full' 110 if playlist_select_context is None 111 else 'menu_minimal' 112 ), 113 scale=scale, 114 ), 115 transition=transition, 116 origin_widget=origin_widget, 117 # We're affected by screen size only at small ui-scale. 118 refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, 119 ) 120 121 self._back_button: bui.Widget | None 122 if uiscale is bui.UIScale.SMALL: 123 self._back_button = None 124 bui.containerwidget( 125 edit=self._root_widget, 126 on_cancel_call=self.main_window_back, 127 ) 128 else: 129 self._back_button = bui.buttonwidget( 130 parent=self._root_widget, 131 position=(50, yoffs - 100), 132 size=(60, 60), 133 scale=1.1, 134 text_res_scale=1.5, 135 text_scale=1.2, 136 autoselect=True, 137 label=bui.charstr(bui.SpecialChar.BACK), 138 button_type='backSmall', 139 on_activate_call=self.main_window_back, 140 ) 141 bui.containerwidget( 142 edit=self._root_widget, cancel_button=self._back_button 143 ) 144 145 bui.textwidget( 146 parent=self._root_widget, 147 position=( 148 width * 0.5, 149 yoffs - (50 if uiscale is bui.UIScale.SMALL else 70), 150 ), 151 size=(0, 0), 152 text=bui.Lstr( 153 resource=( 154 (f'{self._r}.titleText') 155 if self._playlist_select_context is None 156 else 'playlistsText' 157 ) 158 ), 159 scale=1.2 if uiscale is bui.UIScale.SMALL else 1.7, 160 res_scale=2.0, 161 maxwidth=250, 162 color=bui.app.ui_v1.heading_color, 163 h_align='center', 164 v_align='center', 165 ) 166 167 scl = 0.75 if self._playlist_select_context is None else 0.68 168 v = height * 0.5 - button_height * scl * 0.5 - 20.0 169 clr = (0.6, 0.7, 0.6, 1.0) 170 171 bcount = 3 if self._playlist_select_context is None else 2 172 173 total_b_width = ( 174 bcount * button_width * scl + (bcount - 1) * button_spacing 175 ) 176 hoffs = (width - total_b_width) * 0.5 177 178 self._lineup_tex = bui.gettexture('playerLineup') 179 angry_computer_transparent_mesh = bui.getmesh( 180 'angryComputerTransparent' 181 ) 182 self._lineup_1_transparent_mesh = bui.getmesh( 183 'playerLineup1Transparent' 184 ) 185 self._lineup_2_transparent_mesh = bui.getmesh( 186 'playerLineup2Transparent' 187 ) 188 self._lineup_3_transparent_mesh = bui.getmesh( 189 'playerLineup3Transparent' 190 ) 191 self._lineup_4_transparent_mesh = bui.getmesh( 192 'playerLineup4Transparent' 193 ) 194 self._eyes_mesh = bui.getmesh('plasticEyesTransparent') 195 196 self._coop_button: bui.Widget | None = None 197 198 # Only show coop button in regular variant. 199 if self._playlist_select_context is None: 200 self._coop_button = btn = bui.buttonwidget( 201 parent=self._root_widget, 202 position=(hoffs, v), 203 size=( 204 scl * button_width, 205 scl * button_height, 206 ), 207 extra_touch_border_scale=0.1, 208 autoselect=True, 209 label='', 210 button_type='square', 211 on_activate_call=self._coop, 212 ) 213 214 if uiscale is bui.UIScale.SMALL: 215 bui.widget( 216 edit=btn, 217 left_widget=bui.get_special_widget('back_button'), 218 ) 219 bui.widget( 220 edit=btn, 221 up_widget=bui.get_special_widget('account_button'), 222 ) 223 bui.widget( 224 edit=btn, 225 down_widget=bui.get_special_widget('settings_button'), 226 ) 227 228 self._draw_dude( 229 0, 230 btn, 231 hoffs, 232 v, 233 scl, 234 position=(140, 30), 235 color=(0.72, 0.4, 1.0), 236 ) 237 self._draw_dude( 238 1, 239 btn, 240 hoffs, 241 v, 242 scl, 243 position=(185, 53), 244 color=(0.71, 0.5, 1.0), 245 ) 246 self._draw_dude( 247 2, 248 btn, 249 hoffs, 250 v, 251 scl, 252 position=(220, 27), 253 color=(0.67, 0.44, 1.0), 254 ) 255 self._draw_dude( 256 3, btn, hoffs, v, scl, position=(255, 57), color=(0.7, 0.3, 1.0) 257 ) 258 bui.imagewidget( 259 parent=self._root_widget, 260 draw_controller=btn, 261 position=(hoffs + scl * 230, v + scl * 153), 262 size=(scl * 115, scl * 115), 263 texture=self._lineup_tex, 264 mesh_transparent=angry_computer_transparent_mesh, 265 ) 266 267 bui.textwidget( 268 parent=self._root_widget, 269 draw_controller=btn, 270 position=(hoffs + scl * (-10), v + scl * 95), 271 size=(scl * button_width, scl * 50), 272 text=bui.Lstr( 273 resource='playModes.singlePlayerCoopText', 274 fallback_resource='playModes.coopText', 275 ), 276 maxwidth=scl * button_width * 0.7, 277 res_scale=1.5, 278 h_align='center', 279 v_align='center', 280 color=(0.7, 0.9, 0.7, 1.0), 281 scale=scl * 1.5, 282 ) 283 284 bui.textwidget( 285 parent=self._root_widget, 286 draw_controller=btn, 287 position=(hoffs + scl * (-10), v + (scl * 54)), 288 size=(scl * button_width, scl * 30), 289 text=bui.Lstr(resource=f'{self._r}.oneToFourPlayersText'), 290 h_align='center', 291 v_align='center', 292 scale=0.83 * scl, 293 flatness=1.0, 294 maxwidth=scl * button_width * 0.7, 295 color=clr, 296 ) 297 298 hoffs += scl * button_width + button_spacing 299 300 self._teams_button = btn = bui.buttonwidget( 301 parent=self._root_widget, 302 position=(hoffs, v), 303 size=( 304 scl * button_width, 305 scl * button_height, 306 ), 307 extra_touch_border_scale=0.1, 308 autoselect=True, 309 label='', 310 button_type='square', 311 on_activate_call=self._team_tourney, 312 ) 313 314 xxx = -14 315 self._draw_dude( 316 2, 317 btn, 318 hoffs, 319 v, 320 scl, 321 position=(xxx + 148, 30), 322 color=(0.2, 0.4, 1.0), 323 ) 324 self._draw_dude( 325 3, 326 btn, 327 hoffs, 328 v, 329 scl, 330 position=(xxx + 181, 53), 331 color=(0.3, 0.4, 1.0), 332 ) 333 self._draw_dude( 334 1, 335 btn, 336 hoffs, 337 v, 338 scl, 339 position=(xxx + 216, 33), 340 color=(0.3, 0.5, 1.0), 341 ) 342 self._draw_dude( 343 0, 344 btn, 345 hoffs, 346 v, 347 scl, 348 position=(xxx + 245, 57), 349 color=(0.3, 0.5, 1.0), 350 ) 351 352 xxx = 155 353 self._draw_dude( 354 0, 355 btn, 356 hoffs, 357 v, 358 scl, 359 position=(xxx + 151, 30), 360 color=(1.0, 0.5, 0.4), 361 ) 362 self._draw_dude( 363 1, 364 btn, 365 hoffs, 366 v, 367 scl, 368 position=(xxx + 189, 53), 369 color=(1.0, 0.58, 0.58), 370 ) 371 self._draw_dude( 372 3, 373 btn, 374 hoffs, 375 v, 376 scl, 377 position=(xxx + 223, 27), 378 color=(1.0, 0.5, 0.5), 379 ) 380 self._draw_dude( 381 2, 382 btn, 383 hoffs, 384 v, 385 scl, 386 position=(xxx + 257, 57), 387 color=(1.0, 0.5, 0.5), 388 ) 389 390 bui.textwidget( 391 parent=self._root_widget, 392 draw_controller=btn, 393 position=(hoffs + scl * (-10), v + scl * 95), 394 size=(scl * button_width, scl * 50), 395 text=bui.Lstr( 396 resource='playModes.teamsText', fallback_resource='teamsText' 397 ), 398 res_scale=1.5, 399 maxwidth=scl * button_width * 0.7, 400 h_align='center', 401 v_align='center', 402 color=(0.7, 0.9, 0.7, 1.0), 403 scale=scl * 1.5, 404 ) 405 bui.textwidget( 406 parent=self._root_widget, 407 draw_controller=btn, 408 position=(hoffs + scl * (-10), v + (scl * 54)), 409 size=(scl * button_width, scl * 30), 410 text=bui.Lstr(resource=f'{self._r}.twoToEightPlayersText'), 411 h_align='center', 412 v_align='center', 413 res_scale=1.5, 414 scale=0.83 * scl, 415 flatness=1.0, 416 maxwidth=scl * button_width * 0.7, 417 color=clr, 418 ) 419 420 hoffs += scl * button_width + button_spacing 421 self._free_for_all_button = btn = bui.buttonwidget( 422 parent=self._root_widget, 423 position=(hoffs, v), 424 size=(scl * button_width, scl * button_height), 425 extra_touch_border_scale=0.1, 426 autoselect=True, 427 label='', 428 button_type='square', 429 on_activate_call=self._free_for_all, 430 ) 431 432 xxx = -5 433 self._draw_dude( 434 0, 435 btn, 436 hoffs, 437 v, 438 scl, 439 position=(xxx + 140, 30), 440 color=(0.4, 1.0, 0.4), 441 ) 442 self._draw_dude( 443 3, 444 btn, 445 hoffs, 446 v, 447 scl, 448 position=(xxx + 185, 53), 449 color=(1.0, 0.4, 0.5), 450 ) 451 self._draw_dude( 452 1, 453 btn, 454 hoffs, 455 v, 456 scl, 457 position=(xxx + 220, 27), 458 color=(0.4, 0.5, 1.0), 459 ) 460 self._draw_dude( 461 2, 462 btn, 463 hoffs, 464 v, 465 scl, 466 position=(xxx + 255, 57), 467 color=(0.5, 1.0, 0.4), 468 ) 469 xxx = 140 470 self._draw_dude( 471 2, 472 btn, 473 hoffs, 474 v, 475 scl, 476 position=(xxx + 148, 30), 477 color=(1.0, 0.9, 0.4), 478 ) 479 self._draw_dude( 480 0, 481 btn, 482 hoffs, 483 v, 484 scl, 485 position=(xxx + 182, 53), 486 color=(0.7, 1.0, 0.5), 487 ) 488 self._draw_dude( 489 3, 490 btn, 491 hoffs, 492 v, 493 scl, 494 position=(xxx + 233, 27), 495 color=(0.7, 0.5, 0.9), 496 ) 497 self._draw_dude( 498 1, 499 btn, 500 hoffs, 501 v, 502 scl, 503 position=(xxx + 266, 53), 504 color=(0.4, 0.5, 0.8), 505 ) 506 bui.textwidget( 507 parent=self._root_widget, 508 draw_controller=btn, 509 position=(hoffs + scl * (-10), v + scl * 95), 510 size=(scl * button_width, scl * 50), 511 text=bui.Lstr( 512 resource='playModes.freeForAllText', 513 fallback_resource='freeForAllText', 514 ), 515 maxwidth=scl * button_width * 0.7, 516 h_align='center', 517 v_align='center', 518 color=(0.7, 0.9, 0.7, 1.0), 519 scale=scl * 1.5, 520 ) 521 bui.textwidget( 522 parent=self._root_widget, 523 draw_controller=btn, 524 position=(hoffs + scl * (-10), v + (scl * 54)), 525 size=(scl * button_width, scl * 30), 526 text=bui.Lstr(resource=f'{self._r}.twoToEightPlayersText'), 527 h_align='center', 528 v_align='center', 529 scale=0.83 * scl, 530 flatness=1.0, 531 maxwidth=scl * button_width * 0.7, 532 color=clr, 533 ) 534 535 if uiscale is bui.UIScale.SMALL: 536 bui.containerwidget( 537 edit=self._root_widget, 538 selected_child=( 539 self._coop_button 540 if self._playlist_select_context is None 541 else self._teams_button 542 ), 543 ) 544 else: 545 bui.containerwidget( 546 edit=self._root_widget, 547 selected_child=( 548 self._coop_button 549 if self._playlist_select_context is None 550 else self._teams_button 551 ), 552 ) 553 554 self._restore_state() 555 556 @override 557 def get_main_window_state(self) -> bui.MainWindowState: 558 # Support recreating our window for back/refresh purposes. 559 cls = type(self) 560 561 # Pull any values out of self here; if we do it in the lambda 562 # we'll keep our window alive inadvertantly. 563 playlist_select_context = self._playlist_select_context 564 return bui.BasicMainWindowState( 565 create_call=lambda transition, origin_widget: cls( 566 transition=transition, 567 origin_widget=origin_widget, 568 playlist_select_context=playlist_select_context, 569 ) 570 ) 571 572 @override 573 def on_main_window_close(self) -> None: 574 self._save_state() 575 576 @staticmethod 577 def _preload_modules() -> None: 578 """Preload modules we use; avoids hitches (called in bg thread).""" 579 import bauiv1lib.mainmenu as _unused1 580 import bauiv1lib.account as _unused2 581 import bauiv1lib.coop.browser as _unused3 582 import bauiv1lib.playlist.browser as _unused4 583 584 def _coop(self) -> None: 585 # pylint: disable=cyclic-import 586 from bauiv1lib.account.signin import show_sign_in_prompt 587 from bauiv1lib.coop.browser import CoopBrowserWindow 588 589 # no-op if we're not currently in control. 590 if not self.main_window_has_control(): 591 return 592 593 plus = bui.app.plus 594 assert plus is not None 595 596 if plus.get_v1_account_state() != 'signed_in': 597 show_sign_in_prompt() 598 return 599 600 self.main_window_replace( 601 CoopBrowserWindow(origin_widget=self._coop_button) 602 ) 603 604 def _team_tourney(self) -> None: 605 # pylint: disable=cyclic-import 606 from bauiv1lib.playlist.browser import PlaylistBrowserWindow 607 608 # no-op if we're not currently in control. 609 if not self.main_window_has_control(): 610 return 611 612 self.main_window_replace( 613 PlaylistBrowserWindow( 614 origin_widget=self._teams_button, 615 sessiontype=bs.DualTeamSession, 616 playlist_select_context=self._playlist_select_context, 617 ) 618 ) 619 620 def _free_for_all(self) -> None: 621 # pylint: disable=cyclic-import 622 from bauiv1lib.playlist.browser import PlaylistBrowserWindow 623 624 # no-op if we're not currently in control. 625 if not self.main_window_has_control(): 626 return 627 628 self.main_window_replace( 629 PlaylistBrowserWindow( 630 origin_widget=self._free_for_all_button, 631 sessiontype=bs.FreeForAllSession, 632 playlist_select_context=self._playlist_select_context, 633 ) 634 ) 635 636 def _draw_dude( 637 self, 638 i: int, 639 btn: bui.Widget, 640 hoffs: float, 641 v: float, 642 scl: float, 643 position: tuple[float, float], 644 color: tuple[float, float, float], 645 ) -> None: 646 # pylint: disable=too-many-positional-arguments 647 h_extra = -100 648 v_extra = 130 649 eye_color = ( 650 0.7 * 1.0 + 0.3 * color[0], 651 0.7 * 1.0 + 0.3 * color[1], 652 0.7 * 1.0 + 0.3 * color[2], 653 ) 654 if i == 0: 655 bui.imagewidget( 656 parent=self._root_widget, 657 draw_controller=btn, 658 position=( 659 hoffs + scl * (h_extra + position[0]), 660 v + scl * (v_extra + position[1]), 661 ), 662 size=(scl * 60, scl * 80), 663 color=color, 664 texture=self._lineup_tex, 665 mesh_transparent=self._lineup_1_transparent_mesh, 666 ) 667 bui.imagewidget( 668 parent=self._root_widget, 669 draw_controller=btn, 670 position=( 671 hoffs + scl * (h_extra + position[0] + 12), 672 v + scl * (v_extra + position[1] + 53), 673 ), 674 size=(scl * 36, scl * 18), 675 texture=self._lineup_tex, 676 color=eye_color, 677 mesh_transparent=self._eyes_mesh, 678 ) 679 elif i == 1: 680 bui.imagewidget( 681 parent=self._root_widget, 682 draw_controller=btn, 683 position=( 684 hoffs + scl * (h_extra + position[0]), 685 v + scl * (v_extra + position[1]), 686 ), 687 size=(scl * 45, scl * 90), 688 color=color, 689 texture=self._lineup_tex, 690 mesh_transparent=self._lineup_2_transparent_mesh, 691 ) 692 bui.imagewidget( 693 parent=self._root_widget, 694 draw_controller=btn, 695 position=( 696 hoffs + scl * (h_extra + position[0] + 5), 697 v + scl * (v_extra + position[1] + 67), 698 ), 699 size=(scl * 32, scl * 16), 700 texture=self._lineup_tex, 701 color=eye_color, 702 mesh_transparent=self._eyes_mesh, 703 ) 704 elif i == 2: 705 bui.imagewidget( 706 parent=self._root_widget, 707 draw_controller=btn, 708 position=( 709 hoffs + scl * (h_extra + position[0]), 710 v + scl * (v_extra + position[1]), 711 ), 712 size=(scl * 45, scl * 90), 713 color=color, 714 texture=self._lineup_tex, 715 mesh_transparent=self._lineup_3_transparent_mesh, 716 ) 717 bui.imagewidget( 718 parent=self._root_widget, 719 draw_controller=btn, 720 position=( 721 hoffs + scl * (h_extra + position[0] + 5), 722 v + scl * (v_extra + position[1] + 59), 723 ), 724 size=(scl * 34, scl * 17), 725 texture=self._lineup_tex, 726 color=eye_color, 727 mesh_transparent=self._eyes_mesh, 728 ) 729 elif i == 3: 730 bui.imagewidget( 731 parent=self._root_widget, 732 draw_controller=btn, 733 position=( 734 hoffs + scl * (h_extra + position[0]), 735 v + scl * (v_extra + position[1]), 736 ), 737 size=(scl * 48, scl * 96), 738 color=color, 739 texture=self._lineup_tex, 740 mesh_transparent=self._lineup_4_transparent_mesh, 741 ) 742 bui.imagewidget( 743 parent=self._root_widget, 744 draw_controller=btn, 745 position=( 746 hoffs + scl * (h_extra + position[0] + 2), 747 v + scl * (v_extra + position[1] + 62), 748 ), 749 size=(scl * 38, scl * 19), 750 texture=self._lineup_tex, 751 color=eye_color, 752 mesh_transparent=self._eyes_mesh, 753 ) 754 755 def _save_state(self) -> None: 756 try: 757 sel = self._root_widget.get_selected_child() 758 if sel == self._teams_button: 759 sel_name = 'Team Games' 760 elif self._coop_button is not None and sel == self._coop_button: 761 sel_name = 'Co-op Games' 762 elif sel == self._free_for_all_button: 763 sel_name = 'Free-for-All Games' 764 elif sel == self._back_button: 765 sel_name = 'Back' 766 else: 767 raise ValueError(f'unrecognized selection {sel}') 768 assert bui.app.classic is not None 769 bui.app.ui_v1.window_states[type(self)] = sel_name 770 except Exception: 771 logging.exception('Error saving state for %s.', self) 772 773 def _restore_state(self) -> None: 774 try: 775 assert bui.app.classic is not None 776 sel_name = bui.app.ui_v1.window_states.get(type(self)) 777 if sel_name == 'Team Games': 778 sel = self._teams_button 779 elif sel_name == 'Co-op Games' and self._coop_button is not None: 780 sel = self._coop_button 781 elif sel_name == 'Free-for-All Games': 782 sel = self._free_for_all_button 783 elif sel_name == 'Back' and self._back_button is not None: 784 sel = self._back_button 785 else: 786 sel = ( 787 self._coop_button 788 if self._coop_button is not None 789 else self._teams_button 790 ) 791 bui.containerwidget(edit=self._root_widget, selected_child=sel) 792 except Exception: 793 logging.exception('Error restoring state for %s.', self)
Window for selecting overall play type.
PlayWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None, playlist_select_context: PlaylistSelectContext | None = None)
27 def __init__( 28 self, 29 transition: str | None = 'in_right', 30 origin_widget: bui.Widget | None = None, 31 playlist_select_context: PlaylistSelectContext | None = None, 32 ): 33 # pylint: disable=too-many-statements 34 # pylint: disable=too-many-locals 35 36 import bacommon.cloud 37 38 # TEMP TESTING 39 if bool(False): 40 print('HELLO FROM TEST') 41 plus = bui.app.plus 42 assert plus is not None 43 plus.cloud.send_message_cb( 44 bacommon.cloud.SecureDataCheckMessage( 45 data=b'fo', signature=b'mo' 46 ), 47 on_response=lambda r: print('GOT CHECK RESPONSE', r), 48 ) 49 plus.cloud.send_message_cb( 50 bacommon.cloud.SecureDataCheckerRequest(), 51 on_response=lambda r: print('GOT CHECKER RESPONSE', r), 52 ) 53 54 # Preload some modules we use in a background thread so we won't 55 # have a visual hitch when the user taps them. 56 bui.app.threadpool.submit_no_wait(self._preload_modules) 57 58 classic = bui.app.classic 59 assert classic is not None 60 61 self._playlist_select_context = playlist_select_context 62 63 uiscale = bui.app.ui_v1.uiscale 64 width = 1300 if uiscale is bui.UIScale.SMALL else 1000 65 height = 1000 if uiscale is bui.UIScale.SMALL else 550 66 67 button_width = 400.0 68 button_height = 360.0 69 button_spacing = 3.0 70 71 if origin_widget is not None: 72 73 # Need to store this ourself since we can function as a 74 # non-main window. 75 self._transition_out = 'out_scale' 76 else: 77 self._transition_out = 'out_right' 78 79 self._r = 'playWindow' 80 81 # Do some fancy math to fill all available screen area up to the 82 # size of our backing container. This lets us fit to the exact 83 # screen shape at small ui scale. 84 screensize = bui.get_virtual_screen_size() 85 safesize = bui.get_virtual_safe_area_size() 86 87 # We're a generally widescreen shaped window, so bump our 88 # overall scale up a bit when screen width is wider than safe 89 # bounds to take advantage of the extra space. 90 smallscale = min(1.6, 1.35 * screensize[0] / safesize[0]) 91 92 scale = ( 93 smallscale 94 if uiscale is bui.UIScale.SMALL 95 else 0.9 if uiscale is bui.UIScale.MEDIUM else 0.8 96 ) 97 # Calc screen size in our local container space and clamp to a 98 # bit smaller than our container size. 99 target_height = min(height - 80, screensize[1] / scale) 100 101 # To get top/left coords, go to the center of our window and 102 # offset by half the width/height of our target area. 103 yoffs = 0.5 * height + 0.5 * target_height + 30.0 104 105 super().__init__( 106 root_widget=bui.containerwidget( 107 size=(width, height), 108 toolbar_visibility=( 109 'menu_full' 110 if playlist_select_context is None 111 else 'menu_minimal' 112 ), 113 scale=scale, 114 ), 115 transition=transition, 116 origin_widget=origin_widget, 117 # We're affected by screen size only at small ui-scale. 118 refresh_on_screen_size_changes=uiscale is bui.UIScale.SMALL, 119 ) 120 121 self._back_button: bui.Widget | None 122 if uiscale is bui.UIScale.SMALL: 123 self._back_button = None 124 bui.containerwidget( 125 edit=self._root_widget, 126 on_cancel_call=self.main_window_back, 127 ) 128 else: 129 self._back_button = bui.buttonwidget( 130 parent=self._root_widget, 131 position=(50, yoffs - 100), 132 size=(60, 60), 133 scale=1.1, 134 text_res_scale=1.5, 135 text_scale=1.2, 136 autoselect=True, 137 label=bui.charstr(bui.SpecialChar.BACK), 138 button_type='backSmall', 139 on_activate_call=self.main_window_back, 140 ) 141 bui.containerwidget( 142 edit=self._root_widget, cancel_button=self._back_button 143 ) 144 145 bui.textwidget( 146 parent=self._root_widget, 147 position=( 148 width * 0.5, 149 yoffs - (50 if uiscale is bui.UIScale.SMALL else 70), 150 ), 151 size=(0, 0), 152 text=bui.Lstr( 153 resource=( 154 (f'{self._r}.titleText') 155 if self._playlist_select_context is None 156 else 'playlistsText' 157 ) 158 ), 159 scale=1.2 if uiscale is bui.UIScale.SMALL else 1.7, 160 res_scale=2.0, 161 maxwidth=250, 162 color=bui.app.ui_v1.heading_color, 163 h_align='center', 164 v_align='center', 165 ) 166 167 scl = 0.75 if self._playlist_select_context is None else 0.68 168 v = height * 0.5 - button_height * scl * 0.5 - 20.0 169 clr = (0.6, 0.7, 0.6, 1.0) 170 171 bcount = 3 if self._playlist_select_context is None else 2 172 173 total_b_width = ( 174 bcount * button_width * scl + (bcount - 1) * button_spacing 175 ) 176 hoffs = (width - total_b_width) * 0.5 177 178 self._lineup_tex = bui.gettexture('playerLineup') 179 angry_computer_transparent_mesh = bui.getmesh( 180 'angryComputerTransparent' 181 ) 182 self._lineup_1_transparent_mesh = bui.getmesh( 183 'playerLineup1Transparent' 184 ) 185 self._lineup_2_transparent_mesh = bui.getmesh( 186 'playerLineup2Transparent' 187 ) 188 self._lineup_3_transparent_mesh = bui.getmesh( 189 'playerLineup3Transparent' 190 ) 191 self._lineup_4_transparent_mesh = bui.getmesh( 192 'playerLineup4Transparent' 193 ) 194 self._eyes_mesh = bui.getmesh('plasticEyesTransparent') 195 196 self._coop_button: bui.Widget | None = None 197 198 # Only show coop button in regular variant. 199 if self._playlist_select_context is None: 200 self._coop_button = btn = bui.buttonwidget( 201 parent=self._root_widget, 202 position=(hoffs, v), 203 size=( 204 scl * button_width, 205 scl * button_height, 206 ), 207 extra_touch_border_scale=0.1, 208 autoselect=True, 209 label='', 210 button_type='square', 211 on_activate_call=self._coop, 212 ) 213 214 if uiscale is bui.UIScale.SMALL: 215 bui.widget( 216 edit=btn, 217 left_widget=bui.get_special_widget('back_button'), 218 ) 219 bui.widget( 220 edit=btn, 221 up_widget=bui.get_special_widget('account_button'), 222 ) 223 bui.widget( 224 edit=btn, 225 down_widget=bui.get_special_widget('settings_button'), 226 ) 227 228 self._draw_dude( 229 0, 230 btn, 231 hoffs, 232 v, 233 scl, 234 position=(140, 30), 235 color=(0.72, 0.4, 1.0), 236 ) 237 self._draw_dude( 238 1, 239 btn, 240 hoffs, 241 v, 242 scl, 243 position=(185, 53), 244 color=(0.71, 0.5, 1.0), 245 ) 246 self._draw_dude( 247 2, 248 btn, 249 hoffs, 250 v, 251 scl, 252 position=(220, 27), 253 color=(0.67, 0.44, 1.0), 254 ) 255 self._draw_dude( 256 3, btn, hoffs, v, scl, position=(255, 57), color=(0.7, 0.3, 1.0) 257 ) 258 bui.imagewidget( 259 parent=self._root_widget, 260 draw_controller=btn, 261 position=(hoffs + scl * 230, v + scl * 153), 262 size=(scl * 115, scl * 115), 263 texture=self._lineup_tex, 264 mesh_transparent=angry_computer_transparent_mesh, 265 ) 266 267 bui.textwidget( 268 parent=self._root_widget, 269 draw_controller=btn, 270 position=(hoffs + scl * (-10), v + scl * 95), 271 size=(scl * button_width, scl * 50), 272 text=bui.Lstr( 273 resource='playModes.singlePlayerCoopText', 274 fallback_resource='playModes.coopText', 275 ), 276 maxwidth=scl * button_width * 0.7, 277 res_scale=1.5, 278 h_align='center', 279 v_align='center', 280 color=(0.7, 0.9, 0.7, 1.0), 281 scale=scl * 1.5, 282 ) 283 284 bui.textwidget( 285 parent=self._root_widget, 286 draw_controller=btn, 287 position=(hoffs + scl * (-10), v + (scl * 54)), 288 size=(scl * button_width, scl * 30), 289 text=bui.Lstr(resource=f'{self._r}.oneToFourPlayersText'), 290 h_align='center', 291 v_align='center', 292 scale=0.83 * scl, 293 flatness=1.0, 294 maxwidth=scl * button_width * 0.7, 295 color=clr, 296 ) 297 298 hoffs += scl * button_width + button_spacing 299 300 self._teams_button = btn = bui.buttonwidget( 301 parent=self._root_widget, 302 position=(hoffs, v), 303 size=( 304 scl * button_width, 305 scl * button_height, 306 ), 307 extra_touch_border_scale=0.1, 308 autoselect=True, 309 label='', 310 button_type='square', 311 on_activate_call=self._team_tourney, 312 ) 313 314 xxx = -14 315 self._draw_dude( 316 2, 317 btn, 318 hoffs, 319 v, 320 scl, 321 position=(xxx + 148, 30), 322 color=(0.2, 0.4, 1.0), 323 ) 324 self._draw_dude( 325 3, 326 btn, 327 hoffs, 328 v, 329 scl, 330 position=(xxx + 181, 53), 331 color=(0.3, 0.4, 1.0), 332 ) 333 self._draw_dude( 334 1, 335 btn, 336 hoffs, 337 v, 338 scl, 339 position=(xxx + 216, 33), 340 color=(0.3, 0.5, 1.0), 341 ) 342 self._draw_dude( 343 0, 344 btn, 345 hoffs, 346 v, 347 scl, 348 position=(xxx + 245, 57), 349 color=(0.3, 0.5, 1.0), 350 ) 351 352 xxx = 155 353 self._draw_dude( 354 0, 355 btn, 356 hoffs, 357 v, 358 scl, 359 position=(xxx + 151, 30), 360 color=(1.0, 0.5, 0.4), 361 ) 362 self._draw_dude( 363 1, 364 btn, 365 hoffs, 366 v, 367 scl, 368 position=(xxx + 189, 53), 369 color=(1.0, 0.58, 0.58), 370 ) 371 self._draw_dude( 372 3, 373 btn, 374 hoffs, 375 v, 376 scl, 377 position=(xxx + 223, 27), 378 color=(1.0, 0.5, 0.5), 379 ) 380 self._draw_dude( 381 2, 382 btn, 383 hoffs, 384 v, 385 scl, 386 position=(xxx + 257, 57), 387 color=(1.0, 0.5, 0.5), 388 ) 389 390 bui.textwidget( 391 parent=self._root_widget, 392 draw_controller=btn, 393 position=(hoffs + scl * (-10), v + scl * 95), 394 size=(scl * button_width, scl * 50), 395 text=bui.Lstr( 396 resource='playModes.teamsText', fallback_resource='teamsText' 397 ), 398 res_scale=1.5, 399 maxwidth=scl * button_width * 0.7, 400 h_align='center', 401 v_align='center', 402 color=(0.7, 0.9, 0.7, 1.0), 403 scale=scl * 1.5, 404 ) 405 bui.textwidget( 406 parent=self._root_widget, 407 draw_controller=btn, 408 position=(hoffs + scl * (-10), v + (scl * 54)), 409 size=(scl * button_width, scl * 30), 410 text=bui.Lstr(resource=f'{self._r}.twoToEightPlayersText'), 411 h_align='center', 412 v_align='center', 413 res_scale=1.5, 414 scale=0.83 * scl, 415 flatness=1.0, 416 maxwidth=scl * button_width * 0.7, 417 color=clr, 418 ) 419 420 hoffs += scl * button_width + button_spacing 421 self._free_for_all_button = btn = bui.buttonwidget( 422 parent=self._root_widget, 423 position=(hoffs, v), 424 size=(scl * button_width, scl * button_height), 425 extra_touch_border_scale=0.1, 426 autoselect=True, 427 label='', 428 button_type='square', 429 on_activate_call=self._free_for_all, 430 ) 431 432 xxx = -5 433 self._draw_dude( 434 0, 435 btn, 436 hoffs, 437 v, 438 scl, 439 position=(xxx + 140, 30), 440 color=(0.4, 1.0, 0.4), 441 ) 442 self._draw_dude( 443 3, 444 btn, 445 hoffs, 446 v, 447 scl, 448 position=(xxx + 185, 53), 449 color=(1.0, 0.4, 0.5), 450 ) 451 self._draw_dude( 452 1, 453 btn, 454 hoffs, 455 v, 456 scl, 457 position=(xxx + 220, 27), 458 color=(0.4, 0.5, 1.0), 459 ) 460 self._draw_dude( 461 2, 462 btn, 463 hoffs, 464 v, 465 scl, 466 position=(xxx + 255, 57), 467 color=(0.5, 1.0, 0.4), 468 ) 469 xxx = 140 470 self._draw_dude( 471 2, 472 btn, 473 hoffs, 474 v, 475 scl, 476 position=(xxx + 148, 30), 477 color=(1.0, 0.9, 0.4), 478 ) 479 self._draw_dude( 480 0, 481 btn, 482 hoffs, 483 v, 484 scl, 485 position=(xxx + 182, 53), 486 color=(0.7, 1.0, 0.5), 487 ) 488 self._draw_dude( 489 3, 490 btn, 491 hoffs, 492 v, 493 scl, 494 position=(xxx + 233, 27), 495 color=(0.7, 0.5, 0.9), 496 ) 497 self._draw_dude( 498 1, 499 btn, 500 hoffs, 501 v, 502 scl, 503 position=(xxx + 266, 53), 504 color=(0.4, 0.5, 0.8), 505 ) 506 bui.textwidget( 507 parent=self._root_widget, 508 draw_controller=btn, 509 position=(hoffs + scl * (-10), v + scl * 95), 510 size=(scl * button_width, scl * 50), 511 text=bui.Lstr( 512 resource='playModes.freeForAllText', 513 fallback_resource='freeForAllText', 514 ), 515 maxwidth=scl * button_width * 0.7, 516 h_align='center', 517 v_align='center', 518 color=(0.7, 0.9, 0.7, 1.0), 519 scale=scl * 1.5, 520 ) 521 bui.textwidget( 522 parent=self._root_widget, 523 draw_controller=btn, 524 position=(hoffs + scl * (-10), v + (scl * 54)), 525 size=(scl * button_width, scl * 30), 526 text=bui.Lstr(resource=f'{self._r}.twoToEightPlayersText'), 527 h_align='center', 528 v_align='center', 529 scale=0.83 * scl, 530 flatness=1.0, 531 maxwidth=scl * button_width * 0.7, 532 color=clr, 533 ) 534 535 if uiscale is bui.UIScale.SMALL: 536 bui.containerwidget( 537 edit=self._root_widget, 538 selected_child=( 539 self._coop_button 540 if self._playlist_select_context is None 541 else self._teams_button 542 ), 543 ) 544 else: 545 bui.containerwidget( 546 edit=self._root_widget, 547 selected_child=( 548 self._coop_button 549 if self._playlist_select_context is None 550 else self._teams_button 551 ), 552 ) 553 554 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.
556 @override 557 def get_main_window_state(self) -> bui.MainWindowState: 558 # Support recreating our window for back/refresh purposes. 559 cls = type(self) 560 561 # Pull any values out of self here; if we do it in the lambda 562 # we'll keep our window alive inadvertantly. 563 playlist_select_context = self._playlist_select_context 564 return bui.BasicMainWindowState( 565 create_call=lambda transition, origin_widget: cls( 566 transition=transition, 567 origin_widget=origin_widget, 568 playlist_select_context=playlist_select_context, 569 ) 570 )
Return a WindowState to recreate this window, if supported.