bauiv1lib.account.viewer
Provides a popup for displaying info about any account.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides a popup for displaying info about any account.""" 4 5from __future__ import annotations 6 7from typing import TYPE_CHECKING 8import logging 9 10from bauiv1lib.popup import PopupWindow, PopupMenuWindow 11import bauiv1 as bui 12 13if TYPE_CHECKING: 14 from typing import Any 15 16 from bauiv1lib.popup import PopupMenu 17 18 19class AccountViewerWindow(PopupWindow): 20 """Popup window that displays info for an account.""" 21 22 def __init__( 23 self, 24 account_id: str, 25 profile_id: str | None = None, 26 position: tuple[float, float] = (0.0, 0.0), 27 scale: float | None = None, 28 offset: tuple[float, float] = (0.0, 0.0), 29 ): 30 if bui.app.classic is None: 31 raise RuntimeError('This requires classic support.') 32 33 plus = bui.app.plus 34 assert plus is not None 35 36 self._account_id = account_id 37 self._profile_id = profile_id 38 39 uiscale = bui.app.ui_v1.uiscale 40 if scale is None: 41 scale = ( 42 2.6 43 if uiscale is bui.UIScale.SMALL 44 else 1.8 45 if uiscale is bui.UIScale.MEDIUM 46 else 1.4 47 ) 48 self._transitioning_out = False 49 50 self._width = 400 51 self._height = ( 52 300 53 if uiscale is bui.UIScale.SMALL 54 else 400 55 if uiscale is bui.UIScale.MEDIUM 56 else 450 57 ) 58 self._subcontainer: bui.Widget | None = None 59 60 bg_color = (0.5, 0.4, 0.6) 61 62 # Creates our _root_widget. 63 super().__init__( 64 position=position, 65 size=(self._width, self._height), 66 scale=scale, 67 bg_color=bg_color, 68 offset=offset, 69 ) 70 71 self._cancel_button = bui.buttonwidget( 72 parent=self.root_widget, 73 position=(50, self._height - 30), 74 size=(50, 50), 75 scale=0.5, 76 label='', 77 color=bg_color, 78 on_activate_call=self._on_cancel_press, 79 autoselect=True, 80 icon=bui.gettexture('crossOut'), 81 iconscale=1.2, 82 ) 83 84 self._title_text = bui.textwidget( 85 parent=self.root_widget, 86 position=(self._width * 0.5, self._height - 20), 87 size=(0, 0), 88 h_align='center', 89 v_align='center', 90 scale=0.6, 91 text=bui.Lstr(resource='playerInfoText'), 92 maxwidth=200, 93 color=(0.7, 0.7, 0.7, 0.7), 94 ) 95 96 self._scrollwidget = bui.scrollwidget( 97 parent=self.root_widget, 98 size=(self._width - 60, self._height - 70), 99 position=(30, 30), 100 capture_arrows=True, 101 simple_culling_v=10, 102 ) 103 bui.widget(edit=self._scrollwidget, autoselect=True) 104 105 self._loading_text = bui.textwidget( 106 parent=self._scrollwidget, 107 scale=0.5, 108 text=bui.Lstr( 109 value='${A}...', 110 subs=[('${A}', bui.Lstr(resource='loadingText'))], 111 ), 112 size=(self._width - 60, 100), 113 h_align='center', 114 v_align='center', 115 ) 116 117 # In cases where the user most likely has a browser/email, lets 118 # offer a 'report this user' button. 119 if ( 120 bui.is_browser_likely_available() 121 and plus.get_v1_account_misc_read_val( 122 'showAccountExtrasMenu', False 123 ) 124 ): 125 self._extras_menu_button = bui.buttonwidget( 126 parent=self.root_widget, 127 size=(20, 20), 128 position=(self._width - 60, self._height - 30), 129 autoselect=True, 130 label='...', 131 button_type='square', 132 color=(0.64, 0.52, 0.69), 133 textcolor=(0.57, 0.47, 0.57), 134 on_activate_call=self._on_extras_menu_press, 135 ) 136 137 bui.containerwidget( 138 edit=self.root_widget, cancel_button=self._cancel_button 139 ) 140 141 bui.app.classic.master_server_v1_get( 142 'bsAccountInfo', 143 { 144 'buildNumber': bui.app.env.build_number, 145 'accountID': self._account_id, 146 'profileID': self._profile_id, 147 }, 148 callback=bui.WeakCall(self._on_query_response), 149 ) 150 151 def popup_menu_selected_choice( 152 self, window: PopupMenu, choice: str 153 ) -> None: 154 """Called when a menu entry is selected.""" 155 del window # Unused arg. 156 if choice == 'more': 157 self._on_more_press() 158 elif choice == 'report': 159 self._on_report_press() 160 elif choice == 'ban': 161 self._on_ban_press() 162 else: 163 print('ERROR: unknown account info extras menu item:', choice) 164 165 def popup_menu_closing(self, window: PopupMenu) -> None: 166 """Called when the popup menu is closing.""" 167 168 def _on_extras_menu_press(self) -> None: 169 choices = ['more', 'report'] 170 choices_display = [ 171 bui.Lstr(resource='coopSelectWindow.seeMoreText'), 172 bui.Lstr(resource='reportThisPlayerText'), 173 ] 174 is_admin = False 175 if is_admin: 176 bui.screenmessage('TEMP FORCING ADMIN ON') 177 choices.append('ban') 178 choices_display.append(bui.Lstr(resource='banThisPlayerText')) 179 180 assert bui.app.classic is not None 181 uiscale = bui.app.ui_v1.uiscale 182 PopupMenuWindow( 183 position=self._extras_menu_button.get_screen_space_center(), 184 scale=( 185 2.3 186 if uiscale is bui.UIScale.SMALL 187 else 1.65 188 if uiscale is bui.UIScale.MEDIUM 189 else 1.23 190 ), 191 choices=choices, 192 choices_display=choices_display, 193 current_choice='more', 194 delegate=self, 195 ) 196 197 def _on_ban_press(self) -> None: 198 plus = bui.app.plus 199 assert plus is not None 200 201 plus.add_v1_account_transaction( 202 {'type': 'BAN_ACCOUNT', 'account': self._account_id} 203 ) 204 plus.run_v1_account_transactions() 205 206 def _on_report_press(self) -> None: 207 from bauiv1lib import report 208 209 report.ReportPlayerWindow( 210 self._account_id, origin_widget=self._extras_menu_button 211 ) 212 213 def _on_more_press(self) -> None: 214 plus = bui.app.plus 215 assert plus is not None 216 bui.open_url( 217 plus.get_master_server_address() 218 + '/highscores?profile=' 219 + self._account_id 220 ) 221 222 def _on_query_response(self, data: dict[str, Any] | None) -> None: 223 # FIXME: Tidy this up. 224 # pylint: disable=too-many-locals 225 # pylint: disable=too-many-branches 226 # pylint: disable=too-many-statements 227 # pylint: disable=too-many-nested-blocks 228 assert bui.app.classic is not None 229 if data is None: 230 bui.textwidget( 231 edit=self._loading_text, 232 text=bui.Lstr(resource='internal.unavailableNoConnectionText'), 233 ) 234 else: 235 try: 236 self._loading_text.delete() 237 trophystr = '' 238 try: 239 trophystr = data['trophies'] 240 num = 10 241 chunks = [ 242 trophystr[i : i + num] 243 for i in range(0, len(trophystr), num) 244 ] 245 trophystr = '\n\n'.join(chunks) 246 if trophystr == '': 247 trophystr = '-' 248 except Exception: 249 logging.exception('Error displaying trophies.') 250 account_name_spacing = 15 251 tscale = 0.65 252 ts_height = bui.get_string_height( 253 trophystr, suppress_warning=True 254 ) 255 sub_width = self._width - 80 256 sub_height = ( 257 200 258 + ts_height * tscale 259 + account_name_spacing * len(data['accountDisplayStrings']) 260 ) 261 self._subcontainer = bui.containerwidget( 262 parent=self._scrollwidget, 263 size=(sub_width, sub_height), 264 background=False, 265 ) 266 v = sub_height - 20 267 268 title_scale = 0.37 269 center = 0.3 270 maxwidth_scale = 0.45 271 showing_character = False 272 if data['profileDisplayString'] is not None: 273 tint_color = (1, 1, 1) 274 try: 275 if data['profile'] is not None: 276 profile = data['profile'] 277 assert bui.app.classic is not None 278 character = bui.app.classic.spaz_appearances.get( 279 profile['character'], None 280 ) 281 if character is not None: 282 tint_color = ( 283 profile['color'] 284 if 'color' in profile 285 else (1, 1, 1) 286 ) 287 tint2_color = ( 288 profile['highlight'] 289 if 'highlight' in profile 290 else (1, 1, 1) 291 ) 292 icon_tex = character.icon_texture 293 tint_tex = character.icon_mask_texture 294 mask_texture = bui.gettexture( 295 'characterIconMask' 296 ) 297 bui.imagewidget( 298 parent=self._subcontainer, 299 position=(sub_width * center - 40, v - 80), 300 size=(80, 80), 301 color=(1, 1, 1), 302 mask_texture=mask_texture, 303 texture=bui.gettexture(icon_tex), 304 tint_texture=bui.gettexture(tint_tex), 305 tint_color=tint_color, 306 tint2_color=tint2_color, 307 ) 308 v -= 95 309 except Exception: 310 logging.exception('Error displaying character.') 311 bui.textwidget( 312 parent=self._subcontainer, 313 size=(0, 0), 314 position=(sub_width * center, v), 315 h_align='center', 316 v_align='center', 317 scale=0.9, 318 color=bui.safecolor(tint_color, 0.7), 319 shadow=1.0, 320 text=bui.Lstr(value=data['profileDisplayString']), 321 maxwidth=sub_width * maxwidth_scale * 0.75, 322 ) 323 showing_character = True 324 v -= 33 325 326 center = 0.75 if showing_character else 0.5 327 maxwidth_scale = 0.45 if showing_character else 0.9 328 329 v = sub_height - 20 330 if len(data['accountDisplayStrings']) <= 1: 331 account_title = bui.Lstr( 332 resource='settingsWindow.accountText' 333 ) 334 else: 335 account_title = bui.Lstr( 336 resource='accountSettingsWindow.accountsText', 337 fallback_resource='settingsWindow.accountText', 338 ) 339 bui.textwidget( 340 parent=self._subcontainer, 341 size=(0, 0), 342 position=(sub_width * center, v), 343 flatness=1.0, 344 h_align='center', 345 v_align='center', 346 scale=title_scale, 347 color=bui.app.ui_v1.infotextcolor, 348 text=account_title, 349 maxwidth=sub_width * maxwidth_scale, 350 ) 351 draw_small = ( 352 showing_character or len(data['accountDisplayStrings']) > 1 353 ) 354 v -= 14 if draw_small else 20 355 for account_string in data['accountDisplayStrings']: 356 bui.textwidget( 357 parent=self._subcontainer, 358 size=(0, 0), 359 position=(sub_width * center, v), 360 h_align='center', 361 v_align='center', 362 scale=0.55 if draw_small else 0.8, 363 text=account_string, 364 maxwidth=sub_width * maxwidth_scale, 365 ) 366 v -= account_name_spacing 367 368 v += account_name_spacing 369 v -= 25 if showing_character else 29 370 371 bui.textwidget( 372 parent=self._subcontainer, 373 size=(0, 0), 374 position=(sub_width * center, v), 375 flatness=1.0, 376 h_align='center', 377 v_align='center', 378 scale=title_scale, 379 color=bui.app.ui_v1.infotextcolor, 380 text=bui.Lstr(resource='rankText'), 381 maxwidth=sub_width * maxwidth_scale, 382 ) 383 v -= 14 384 if data['rank'] is None: 385 rank_str = '-' 386 suffix_offset = None 387 else: 388 str_raw = bui.Lstr( 389 resource='league.rankInLeagueText' 390 ).evaluate() 391 # FIXME: Would be nice to not have to eval this. 392 rank_str = bui.Lstr( 393 resource='league.rankInLeagueText', 394 subs=[ 395 ('${RANK}', str(data['rank'][2])), 396 ( 397 '${NAME}', 398 bui.Lstr( 399 translate=('leagueNames', data['rank'][0]) 400 ), 401 ), 402 ('${SUFFIX}', ''), 403 ], 404 ).evaluate() 405 rank_str_width = min( 406 sub_width * maxwidth_scale, 407 bui.get_string_width(rank_str, suppress_warning=True) 408 * 0.55, 409 ) 410 411 # Only tack our suffix on if its at the end and only for 412 # non-diamond leagues. 413 if ( 414 str_raw.endswith('${SUFFIX}') 415 and data['rank'][0] != 'Diamond' 416 ): 417 suffix_offset = rank_str_width * 0.5 + 2 418 else: 419 suffix_offset = None 420 421 bui.textwidget( 422 parent=self._subcontainer, 423 size=(0, 0), 424 position=(sub_width * center, v), 425 h_align='center', 426 v_align='center', 427 scale=0.55, 428 text=rank_str, 429 maxwidth=sub_width * maxwidth_scale, 430 ) 431 if suffix_offset is not None: 432 assert data['rank'] is not None 433 bui.textwidget( 434 parent=self._subcontainer, 435 size=(0, 0), 436 position=(sub_width * center + suffix_offset, v + 3), 437 h_align='left', 438 v_align='center', 439 scale=0.29, 440 flatness=1.0, 441 text='[' + str(data['rank'][1]) + ']', 442 ) 443 v -= 14 444 445 str_raw = bui.Lstr( 446 resource='league.rankInLeagueText' 447 ).evaluate() 448 old_offs = -50 449 prev_ranks_shown = 0 450 for prev_rank in data['prevRanks']: 451 rank_str = bui.Lstr( 452 value='${S}: ${I}', 453 subs=[ 454 ( 455 '${S}', 456 bui.Lstr( 457 resource='league.seasonText', 458 subs=[('${NUMBER}', str(prev_rank[0]))], 459 ), 460 ), 461 ( 462 '${I}', 463 bui.Lstr( 464 resource='league.rankInLeagueText', 465 subs=[ 466 ('${RANK}', str(prev_rank[3])), 467 ( 468 '${NAME}', 469 bui.Lstr( 470 translate=( 471 'leagueNames', 472 prev_rank[1], 473 ) 474 ), 475 ), 476 ('${SUFFIX}', ''), 477 ], 478 ), 479 ), 480 ], 481 ).evaluate() 482 rank_str_width = min( 483 sub_width * maxwidth_scale, 484 bui.get_string_width(rank_str, suppress_warning=True) 485 * 0.3, 486 ) 487 488 # Only tack our suffix on if its at the end and only for 489 # non-diamond leagues. 490 if ( 491 str_raw.endswith('${SUFFIX}') 492 and prev_rank[1] != 'Diamond' 493 ): 494 suffix_offset = rank_str_width + 2 495 else: 496 suffix_offset = None 497 bui.textwidget( 498 parent=self._subcontainer, 499 size=(0, 0), 500 position=(sub_width * center + old_offs, v), 501 h_align='left', 502 v_align='center', 503 scale=0.3, 504 text=rank_str, 505 flatness=1.0, 506 maxwidth=sub_width * maxwidth_scale, 507 ) 508 if suffix_offset is not None: 509 bui.textwidget( 510 parent=self._subcontainer, 511 size=(0, 0), 512 position=( 513 sub_width * center + old_offs + suffix_offset, 514 v + 1, 515 ), 516 h_align='left', 517 v_align='center', 518 scale=0.20, 519 flatness=1.0, 520 text='[' + str(prev_rank[2]) + ']', 521 ) 522 prev_ranks_shown += 1 523 v -= 10 524 525 v -= 13 526 527 bui.textwidget( 528 parent=self._subcontainer, 529 size=(0, 0), 530 position=(sub_width * center, v), 531 flatness=1.0, 532 h_align='center', 533 v_align='center', 534 scale=title_scale, 535 color=bui.app.ui_v1.infotextcolor, 536 text=bui.Lstr(resource='achievementsText'), 537 maxwidth=sub_width * maxwidth_scale, 538 ) 539 v -= 14 540 bui.textwidget( 541 parent=self._subcontainer, 542 size=(0, 0), 543 position=(sub_width * center, v), 544 h_align='center', 545 v_align='center', 546 scale=0.55, 547 text=str(data['achievementsCompleted']) 548 + ' / ' 549 + str(len(bui.app.classic.ach.achievements)), 550 maxwidth=sub_width * maxwidth_scale, 551 ) 552 v -= 25 553 554 if prev_ranks_shown == 0 and showing_character: 555 v -= 20 556 elif prev_ranks_shown == 1 and showing_character: 557 v -= 10 558 559 center = 0.5 560 maxwidth_scale = 0.9 561 562 bui.textwidget( 563 parent=self._subcontainer, 564 size=(0, 0), 565 position=(sub_width * center, v), 566 h_align='center', 567 v_align='center', 568 scale=title_scale, 569 color=bui.app.ui_v1.infotextcolor, 570 flatness=1.0, 571 text=bui.Lstr( 572 resource='trophiesThisSeasonText', 573 fallback_resource='trophiesText', 574 ), 575 maxwidth=sub_width * maxwidth_scale, 576 ) 577 v -= 19 578 bui.textwidget( 579 parent=self._subcontainer, 580 size=(0, ts_height), 581 position=(sub_width * 0.5, v - ts_height * tscale), 582 h_align='center', 583 v_align='top', 584 corner_scale=tscale, 585 text=trophystr, 586 ) 587 588 except Exception: 589 logging.exception('Error displaying account info.') 590 591 def _on_cancel_press(self) -> None: 592 self._transition_out() 593 594 def _transition_out(self) -> None: 595 if not self._transitioning_out: 596 self._transitioning_out = True 597 bui.containerwidget(edit=self.root_widget, transition='out_scale') 598 599 def on_popup_cancel(self) -> None: 600 bui.getsound('swish').play() 601 self._transition_out()
20class AccountViewerWindow(PopupWindow): 21 """Popup window that displays info for an account.""" 22 23 def __init__( 24 self, 25 account_id: str, 26 profile_id: str | None = None, 27 position: tuple[float, float] = (0.0, 0.0), 28 scale: float | None = None, 29 offset: tuple[float, float] = (0.0, 0.0), 30 ): 31 if bui.app.classic is None: 32 raise RuntimeError('This requires classic support.') 33 34 plus = bui.app.plus 35 assert plus is not None 36 37 self._account_id = account_id 38 self._profile_id = profile_id 39 40 uiscale = bui.app.ui_v1.uiscale 41 if scale is None: 42 scale = ( 43 2.6 44 if uiscale is bui.UIScale.SMALL 45 else 1.8 46 if uiscale is bui.UIScale.MEDIUM 47 else 1.4 48 ) 49 self._transitioning_out = False 50 51 self._width = 400 52 self._height = ( 53 300 54 if uiscale is bui.UIScale.SMALL 55 else 400 56 if uiscale is bui.UIScale.MEDIUM 57 else 450 58 ) 59 self._subcontainer: bui.Widget | None = None 60 61 bg_color = (0.5, 0.4, 0.6) 62 63 # Creates our _root_widget. 64 super().__init__( 65 position=position, 66 size=(self._width, self._height), 67 scale=scale, 68 bg_color=bg_color, 69 offset=offset, 70 ) 71 72 self._cancel_button = bui.buttonwidget( 73 parent=self.root_widget, 74 position=(50, self._height - 30), 75 size=(50, 50), 76 scale=0.5, 77 label='', 78 color=bg_color, 79 on_activate_call=self._on_cancel_press, 80 autoselect=True, 81 icon=bui.gettexture('crossOut'), 82 iconscale=1.2, 83 ) 84 85 self._title_text = bui.textwidget( 86 parent=self.root_widget, 87 position=(self._width * 0.5, self._height - 20), 88 size=(0, 0), 89 h_align='center', 90 v_align='center', 91 scale=0.6, 92 text=bui.Lstr(resource='playerInfoText'), 93 maxwidth=200, 94 color=(0.7, 0.7, 0.7, 0.7), 95 ) 96 97 self._scrollwidget = bui.scrollwidget( 98 parent=self.root_widget, 99 size=(self._width - 60, self._height - 70), 100 position=(30, 30), 101 capture_arrows=True, 102 simple_culling_v=10, 103 ) 104 bui.widget(edit=self._scrollwidget, autoselect=True) 105 106 self._loading_text = bui.textwidget( 107 parent=self._scrollwidget, 108 scale=0.5, 109 text=bui.Lstr( 110 value='${A}...', 111 subs=[('${A}', bui.Lstr(resource='loadingText'))], 112 ), 113 size=(self._width - 60, 100), 114 h_align='center', 115 v_align='center', 116 ) 117 118 # In cases where the user most likely has a browser/email, lets 119 # offer a 'report this user' button. 120 if ( 121 bui.is_browser_likely_available() 122 and plus.get_v1_account_misc_read_val( 123 'showAccountExtrasMenu', False 124 ) 125 ): 126 self._extras_menu_button = bui.buttonwidget( 127 parent=self.root_widget, 128 size=(20, 20), 129 position=(self._width - 60, self._height - 30), 130 autoselect=True, 131 label='...', 132 button_type='square', 133 color=(0.64, 0.52, 0.69), 134 textcolor=(0.57, 0.47, 0.57), 135 on_activate_call=self._on_extras_menu_press, 136 ) 137 138 bui.containerwidget( 139 edit=self.root_widget, cancel_button=self._cancel_button 140 ) 141 142 bui.app.classic.master_server_v1_get( 143 'bsAccountInfo', 144 { 145 'buildNumber': bui.app.env.build_number, 146 'accountID': self._account_id, 147 'profileID': self._profile_id, 148 }, 149 callback=bui.WeakCall(self._on_query_response), 150 ) 151 152 def popup_menu_selected_choice( 153 self, window: PopupMenu, choice: str 154 ) -> None: 155 """Called when a menu entry is selected.""" 156 del window # Unused arg. 157 if choice == 'more': 158 self._on_more_press() 159 elif choice == 'report': 160 self._on_report_press() 161 elif choice == 'ban': 162 self._on_ban_press() 163 else: 164 print('ERROR: unknown account info extras menu item:', choice) 165 166 def popup_menu_closing(self, window: PopupMenu) -> None: 167 """Called when the popup menu is closing.""" 168 169 def _on_extras_menu_press(self) -> None: 170 choices = ['more', 'report'] 171 choices_display = [ 172 bui.Lstr(resource='coopSelectWindow.seeMoreText'), 173 bui.Lstr(resource='reportThisPlayerText'), 174 ] 175 is_admin = False 176 if is_admin: 177 bui.screenmessage('TEMP FORCING ADMIN ON') 178 choices.append('ban') 179 choices_display.append(bui.Lstr(resource='banThisPlayerText')) 180 181 assert bui.app.classic is not None 182 uiscale = bui.app.ui_v1.uiscale 183 PopupMenuWindow( 184 position=self._extras_menu_button.get_screen_space_center(), 185 scale=( 186 2.3 187 if uiscale is bui.UIScale.SMALL 188 else 1.65 189 if uiscale is bui.UIScale.MEDIUM 190 else 1.23 191 ), 192 choices=choices, 193 choices_display=choices_display, 194 current_choice='more', 195 delegate=self, 196 ) 197 198 def _on_ban_press(self) -> None: 199 plus = bui.app.plus 200 assert plus is not None 201 202 plus.add_v1_account_transaction( 203 {'type': 'BAN_ACCOUNT', 'account': self._account_id} 204 ) 205 plus.run_v1_account_transactions() 206 207 def _on_report_press(self) -> None: 208 from bauiv1lib import report 209 210 report.ReportPlayerWindow( 211 self._account_id, origin_widget=self._extras_menu_button 212 ) 213 214 def _on_more_press(self) -> None: 215 plus = bui.app.plus 216 assert plus is not None 217 bui.open_url( 218 plus.get_master_server_address() 219 + '/highscores?profile=' 220 + self._account_id 221 ) 222 223 def _on_query_response(self, data: dict[str, Any] | None) -> None: 224 # FIXME: Tidy this up. 225 # pylint: disable=too-many-locals 226 # pylint: disable=too-many-branches 227 # pylint: disable=too-many-statements 228 # pylint: disable=too-many-nested-blocks 229 assert bui.app.classic is not None 230 if data is None: 231 bui.textwidget( 232 edit=self._loading_text, 233 text=bui.Lstr(resource='internal.unavailableNoConnectionText'), 234 ) 235 else: 236 try: 237 self._loading_text.delete() 238 trophystr = '' 239 try: 240 trophystr = data['trophies'] 241 num = 10 242 chunks = [ 243 trophystr[i : i + num] 244 for i in range(0, len(trophystr), num) 245 ] 246 trophystr = '\n\n'.join(chunks) 247 if trophystr == '': 248 trophystr = '-' 249 except Exception: 250 logging.exception('Error displaying trophies.') 251 account_name_spacing = 15 252 tscale = 0.65 253 ts_height = bui.get_string_height( 254 trophystr, suppress_warning=True 255 ) 256 sub_width = self._width - 80 257 sub_height = ( 258 200 259 + ts_height * tscale 260 + account_name_spacing * len(data['accountDisplayStrings']) 261 ) 262 self._subcontainer = bui.containerwidget( 263 parent=self._scrollwidget, 264 size=(sub_width, sub_height), 265 background=False, 266 ) 267 v = sub_height - 20 268 269 title_scale = 0.37 270 center = 0.3 271 maxwidth_scale = 0.45 272 showing_character = False 273 if data['profileDisplayString'] is not None: 274 tint_color = (1, 1, 1) 275 try: 276 if data['profile'] is not None: 277 profile = data['profile'] 278 assert bui.app.classic is not None 279 character = bui.app.classic.spaz_appearances.get( 280 profile['character'], None 281 ) 282 if character is not None: 283 tint_color = ( 284 profile['color'] 285 if 'color' in profile 286 else (1, 1, 1) 287 ) 288 tint2_color = ( 289 profile['highlight'] 290 if 'highlight' in profile 291 else (1, 1, 1) 292 ) 293 icon_tex = character.icon_texture 294 tint_tex = character.icon_mask_texture 295 mask_texture = bui.gettexture( 296 'characterIconMask' 297 ) 298 bui.imagewidget( 299 parent=self._subcontainer, 300 position=(sub_width * center - 40, v - 80), 301 size=(80, 80), 302 color=(1, 1, 1), 303 mask_texture=mask_texture, 304 texture=bui.gettexture(icon_tex), 305 tint_texture=bui.gettexture(tint_tex), 306 tint_color=tint_color, 307 tint2_color=tint2_color, 308 ) 309 v -= 95 310 except Exception: 311 logging.exception('Error displaying character.') 312 bui.textwidget( 313 parent=self._subcontainer, 314 size=(0, 0), 315 position=(sub_width * center, v), 316 h_align='center', 317 v_align='center', 318 scale=0.9, 319 color=bui.safecolor(tint_color, 0.7), 320 shadow=1.0, 321 text=bui.Lstr(value=data['profileDisplayString']), 322 maxwidth=sub_width * maxwidth_scale * 0.75, 323 ) 324 showing_character = True 325 v -= 33 326 327 center = 0.75 if showing_character else 0.5 328 maxwidth_scale = 0.45 if showing_character else 0.9 329 330 v = sub_height - 20 331 if len(data['accountDisplayStrings']) <= 1: 332 account_title = bui.Lstr( 333 resource='settingsWindow.accountText' 334 ) 335 else: 336 account_title = bui.Lstr( 337 resource='accountSettingsWindow.accountsText', 338 fallback_resource='settingsWindow.accountText', 339 ) 340 bui.textwidget( 341 parent=self._subcontainer, 342 size=(0, 0), 343 position=(sub_width * center, v), 344 flatness=1.0, 345 h_align='center', 346 v_align='center', 347 scale=title_scale, 348 color=bui.app.ui_v1.infotextcolor, 349 text=account_title, 350 maxwidth=sub_width * maxwidth_scale, 351 ) 352 draw_small = ( 353 showing_character or len(data['accountDisplayStrings']) > 1 354 ) 355 v -= 14 if draw_small else 20 356 for account_string in data['accountDisplayStrings']: 357 bui.textwidget( 358 parent=self._subcontainer, 359 size=(0, 0), 360 position=(sub_width * center, v), 361 h_align='center', 362 v_align='center', 363 scale=0.55 if draw_small else 0.8, 364 text=account_string, 365 maxwidth=sub_width * maxwidth_scale, 366 ) 367 v -= account_name_spacing 368 369 v += account_name_spacing 370 v -= 25 if showing_character else 29 371 372 bui.textwidget( 373 parent=self._subcontainer, 374 size=(0, 0), 375 position=(sub_width * center, v), 376 flatness=1.0, 377 h_align='center', 378 v_align='center', 379 scale=title_scale, 380 color=bui.app.ui_v1.infotextcolor, 381 text=bui.Lstr(resource='rankText'), 382 maxwidth=sub_width * maxwidth_scale, 383 ) 384 v -= 14 385 if data['rank'] is None: 386 rank_str = '-' 387 suffix_offset = None 388 else: 389 str_raw = bui.Lstr( 390 resource='league.rankInLeagueText' 391 ).evaluate() 392 # FIXME: Would be nice to not have to eval this. 393 rank_str = bui.Lstr( 394 resource='league.rankInLeagueText', 395 subs=[ 396 ('${RANK}', str(data['rank'][2])), 397 ( 398 '${NAME}', 399 bui.Lstr( 400 translate=('leagueNames', data['rank'][0]) 401 ), 402 ), 403 ('${SUFFIX}', ''), 404 ], 405 ).evaluate() 406 rank_str_width = min( 407 sub_width * maxwidth_scale, 408 bui.get_string_width(rank_str, suppress_warning=True) 409 * 0.55, 410 ) 411 412 # Only tack our suffix on if its at the end and only for 413 # non-diamond leagues. 414 if ( 415 str_raw.endswith('${SUFFIX}') 416 and data['rank'][0] != 'Diamond' 417 ): 418 suffix_offset = rank_str_width * 0.5 + 2 419 else: 420 suffix_offset = None 421 422 bui.textwidget( 423 parent=self._subcontainer, 424 size=(0, 0), 425 position=(sub_width * center, v), 426 h_align='center', 427 v_align='center', 428 scale=0.55, 429 text=rank_str, 430 maxwidth=sub_width * maxwidth_scale, 431 ) 432 if suffix_offset is not None: 433 assert data['rank'] is not None 434 bui.textwidget( 435 parent=self._subcontainer, 436 size=(0, 0), 437 position=(sub_width * center + suffix_offset, v + 3), 438 h_align='left', 439 v_align='center', 440 scale=0.29, 441 flatness=1.0, 442 text='[' + str(data['rank'][1]) + ']', 443 ) 444 v -= 14 445 446 str_raw = bui.Lstr( 447 resource='league.rankInLeagueText' 448 ).evaluate() 449 old_offs = -50 450 prev_ranks_shown = 0 451 for prev_rank in data['prevRanks']: 452 rank_str = bui.Lstr( 453 value='${S}: ${I}', 454 subs=[ 455 ( 456 '${S}', 457 bui.Lstr( 458 resource='league.seasonText', 459 subs=[('${NUMBER}', str(prev_rank[0]))], 460 ), 461 ), 462 ( 463 '${I}', 464 bui.Lstr( 465 resource='league.rankInLeagueText', 466 subs=[ 467 ('${RANK}', str(prev_rank[3])), 468 ( 469 '${NAME}', 470 bui.Lstr( 471 translate=( 472 'leagueNames', 473 prev_rank[1], 474 ) 475 ), 476 ), 477 ('${SUFFIX}', ''), 478 ], 479 ), 480 ), 481 ], 482 ).evaluate() 483 rank_str_width = min( 484 sub_width * maxwidth_scale, 485 bui.get_string_width(rank_str, suppress_warning=True) 486 * 0.3, 487 ) 488 489 # Only tack our suffix on if its at the end and only for 490 # non-diamond leagues. 491 if ( 492 str_raw.endswith('${SUFFIX}') 493 and prev_rank[1] != 'Diamond' 494 ): 495 suffix_offset = rank_str_width + 2 496 else: 497 suffix_offset = None 498 bui.textwidget( 499 parent=self._subcontainer, 500 size=(0, 0), 501 position=(sub_width * center + old_offs, v), 502 h_align='left', 503 v_align='center', 504 scale=0.3, 505 text=rank_str, 506 flatness=1.0, 507 maxwidth=sub_width * maxwidth_scale, 508 ) 509 if suffix_offset is not None: 510 bui.textwidget( 511 parent=self._subcontainer, 512 size=(0, 0), 513 position=( 514 sub_width * center + old_offs + suffix_offset, 515 v + 1, 516 ), 517 h_align='left', 518 v_align='center', 519 scale=0.20, 520 flatness=1.0, 521 text='[' + str(prev_rank[2]) + ']', 522 ) 523 prev_ranks_shown += 1 524 v -= 10 525 526 v -= 13 527 528 bui.textwidget( 529 parent=self._subcontainer, 530 size=(0, 0), 531 position=(sub_width * center, v), 532 flatness=1.0, 533 h_align='center', 534 v_align='center', 535 scale=title_scale, 536 color=bui.app.ui_v1.infotextcolor, 537 text=bui.Lstr(resource='achievementsText'), 538 maxwidth=sub_width * maxwidth_scale, 539 ) 540 v -= 14 541 bui.textwidget( 542 parent=self._subcontainer, 543 size=(0, 0), 544 position=(sub_width * center, v), 545 h_align='center', 546 v_align='center', 547 scale=0.55, 548 text=str(data['achievementsCompleted']) 549 + ' / ' 550 + str(len(bui.app.classic.ach.achievements)), 551 maxwidth=sub_width * maxwidth_scale, 552 ) 553 v -= 25 554 555 if prev_ranks_shown == 0 and showing_character: 556 v -= 20 557 elif prev_ranks_shown == 1 and showing_character: 558 v -= 10 559 560 center = 0.5 561 maxwidth_scale = 0.9 562 563 bui.textwidget( 564 parent=self._subcontainer, 565 size=(0, 0), 566 position=(sub_width * center, v), 567 h_align='center', 568 v_align='center', 569 scale=title_scale, 570 color=bui.app.ui_v1.infotextcolor, 571 flatness=1.0, 572 text=bui.Lstr( 573 resource='trophiesThisSeasonText', 574 fallback_resource='trophiesText', 575 ), 576 maxwidth=sub_width * maxwidth_scale, 577 ) 578 v -= 19 579 bui.textwidget( 580 parent=self._subcontainer, 581 size=(0, ts_height), 582 position=(sub_width * 0.5, v - ts_height * tscale), 583 h_align='center', 584 v_align='top', 585 corner_scale=tscale, 586 text=trophystr, 587 ) 588 589 except Exception: 590 logging.exception('Error displaying account info.') 591 592 def _on_cancel_press(self) -> None: 593 self._transition_out() 594 595 def _transition_out(self) -> None: 596 if not self._transitioning_out: 597 self._transitioning_out = True 598 bui.containerwidget(edit=self.root_widget, transition='out_scale') 599 600 def on_popup_cancel(self) -> None: 601 bui.getsound('swish').play() 602 self._transition_out()
Popup window that displays info for an account.
AccountViewerWindow( account_id: str, profile_id: str | None = None, position: tuple[float, float] = (0.0, 0.0), scale: float | None = None, offset: tuple[float, float] = (0.0, 0.0))
23 def __init__( 24 self, 25 account_id: str, 26 profile_id: str | None = None, 27 position: tuple[float, float] = (0.0, 0.0), 28 scale: float | None = None, 29 offset: tuple[float, float] = (0.0, 0.0), 30 ): 31 if bui.app.classic is None: 32 raise RuntimeError('This requires classic support.') 33 34 plus = bui.app.plus 35 assert plus is not None 36 37 self._account_id = account_id 38 self._profile_id = profile_id 39 40 uiscale = bui.app.ui_v1.uiscale 41 if scale is None: 42 scale = ( 43 2.6 44 if uiscale is bui.UIScale.SMALL 45 else 1.8 46 if uiscale is bui.UIScale.MEDIUM 47 else 1.4 48 ) 49 self._transitioning_out = False 50 51 self._width = 400 52 self._height = ( 53 300 54 if uiscale is bui.UIScale.SMALL 55 else 400 56 if uiscale is bui.UIScale.MEDIUM 57 else 450 58 ) 59 self._subcontainer: bui.Widget | None = None 60 61 bg_color = (0.5, 0.4, 0.6) 62 63 # Creates our _root_widget. 64 super().__init__( 65 position=position, 66 size=(self._width, self._height), 67 scale=scale, 68 bg_color=bg_color, 69 offset=offset, 70 ) 71 72 self._cancel_button = bui.buttonwidget( 73 parent=self.root_widget, 74 position=(50, self._height - 30), 75 size=(50, 50), 76 scale=0.5, 77 label='', 78 color=bg_color, 79 on_activate_call=self._on_cancel_press, 80 autoselect=True, 81 icon=bui.gettexture('crossOut'), 82 iconscale=1.2, 83 ) 84 85 self._title_text = bui.textwidget( 86 parent=self.root_widget, 87 position=(self._width * 0.5, self._height - 20), 88 size=(0, 0), 89 h_align='center', 90 v_align='center', 91 scale=0.6, 92 text=bui.Lstr(resource='playerInfoText'), 93 maxwidth=200, 94 color=(0.7, 0.7, 0.7, 0.7), 95 ) 96 97 self._scrollwidget = bui.scrollwidget( 98 parent=self.root_widget, 99 size=(self._width - 60, self._height - 70), 100 position=(30, 30), 101 capture_arrows=True, 102 simple_culling_v=10, 103 ) 104 bui.widget(edit=self._scrollwidget, autoselect=True) 105 106 self._loading_text = bui.textwidget( 107 parent=self._scrollwidget, 108 scale=0.5, 109 text=bui.Lstr( 110 value='${A}...', 111 subs=[('${A}', bui.Lstr(resource='loadingText'))], 112 ), 113 size=(self._width - 60, 100), 114 h_align='center', 115 v_align='center', 116 ) 117 118 # In cases where the user most likely has a browser/email, lets 119 # offer a 'report this user' button. 120 if ( 121 bui.is_browser_likely_available() 122 and plus.get_v1_account_misc_read_val( 123 'showAccountExtrasMenu', False 124 ) 125 ): 126 self._extras_menu_button = bui.buttonwidget( 127 parent=self.root_widget, 128 size=(20, 20), 129 position=(self._width - 60, self._height - 30), 130 autoselect=True, 131 label='...', 132 button_type='square', 133 color=(0.64, 0.52, 0.69), 134 textcolor=(0.57, 0.47, 0.57), 135 on_activate_call=self._on_extras_menu_press, 136 ) 137 138 bui.containerwidget( 139 edit=self.root_widget, cancel_button=self._cancel_button 140 ) 141 142 bui.app.classic.master_server_v1_get( 143 'bsAccountInfo', 144 { 145 'buildNumber': bui.app.env.build_number, 146 'accountID': self._account_id, 147 'profileID': self._profile_id, 148 }, 149 callback=bui.WeakCall(self._on_query_response), 150 )
def
on_popup_cancel(self) -> None:
Called when the popup is canceled.
Cancels can occur due to clicking outside the window, hitting escape, etc.