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