bauiv1lib.league.rankwindow

UI related to league rank.

   1# Released under the MIT License. See LICENSE for details.
   2#
   3"""UI related to league rank."""
   4# pylint: disable=too-many-lines
   5
   6from __future__ import annotations
   7
   8import copy
   9import logging
  10from typing import TYPE_CHECKING, override
  11
  12from bauiv1lib.popup import PopupMenu
  13import bauiv1 as bui
  14
  15if TYPE_CHECKING:
  16    from typing import Any
  17
  18
  19class LeagueRankWindow(bui.MainWindow):
  20    """Window for showing league rank."""
  21
  22    def __init__(
  23        self,
  24        transition: str | None = 'in_right',
  25        origin_widget: bui.Widget | None = None,
  26    ):
  27        # pylint: disable=too-many-statements
  28        plus = bui.app.plus
  29        assert plus is not None
  30
  31        bui.set_analytics_screen('League Rank Window')
  32
  33        self._league_rank_data: dict[str, Any] | None = None
  34
  35        self._power_ranking_achievements_button: bui.Widget | None = None
  36        self._pro_mult_button: bui.Widget | None = None
  37        self._power_ranking_trophies_button: bui.Widget | None = None
  38        self._league_title_text: bui.Widget | None = None
  39        self._league_text: bui.Widget | None = None
  40        self._league_number_text: bui.Widget | None = None
  41        self._your_power_ranking_text: bui.Widget | None = None
  42        self._loading_spinner: bui.Widget | None = None
  43        self._season_ends_text: bui.Widget | None = None
  44        self._power_ranking_rank_text: bui.Widget | None = None
  45        self._to_ranked_text: bui.Widget | None = None
  46        self._trophy_counts_reset_text: bui.Widget | None = None
  47
  48        assert bui.app.classic is not None
  49        uiscale = bui.app.ui_v1.uiscale
  50        self._width = 1490 if uiscale is bui.UIScale.SMALL else 1120
  51        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
  52        self._height = (
  53            660
  54            if uiscale is bui.UIScale.SMALL
  55            else 710 if uiscale is bui.UIScale.MEDIUM else 800
  56        )
  57        self._r = 'coopSelectWindow'
  58        self._rdict = bui.app.lang.get_resource(self._r)
  59        top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
  60
  61        self._xoffs = 80.0 if uiscale is bui.UIScale.SMALL else 0
  62
  63        self._league_url_arg = ''
  64
  65        self._is_current_season = False
  66        self._can_do_more_button = True
  67
  68        super().__init__(
  69            root_widget=bui.containerwidget(
  70                size=(self._width, self._height + top_extra),
  71                stack_offset=(
  72                    (0, 0)
  73                    if uiscale is bui.UIScale.SMALL
  74                    else (0, 10) if uiscale is bui.UIScale.MEDIUM else (0, 0)
  75                ),
  76                scale=(
  77                    1.08
  78                    if uiscale is bui.UIScale.SMALL
  79                    else 0.93 if uiscale is bui.UIScale.MEDIUM else 0.8
  80                ),
  81                toolbar_visibility=(
  82                    'menu_minimal'
  83                    if uiscale is bui.UIScale.SMALL
  84                    else 'menu_full'
  85                ),
  86            ),
  87            transition=transition,
  88            origin_widget=origin_widget,
  89        )
  90
  91        if uiscale is bui.UIScale.SMALL:
  92            self._back_button = bui.get_special_widget('back_button')
  93            bui.containerwidget(
  94                edit=self._root_widget, on_cancel_call=self.main_window_back
  95            )
  96        else:
  97            self._back_button = btn = bui.buttonwidget(
  98                parent=self._root_widget,
  99                position=(75 + x_inset, self._height - 87),
 100                size=(120, 60),
 101                scale=1.2,
 102                autoselect=True,
 103                label=bui.Lstr(resource='backText'),
 104                button_type='back',
 105                on_activate_call=self.main_window_back,
 106            )
 107            bui.buttonwidget(
 108                edit=btn,
 109                button_type='backSmall',
 110                position=(75 + x_inset, self._height - 87),
 111                size=(60, 55),
 112                label=bui.charstr(bui.SpecialChar.BACK),
 113            )
 114            bui.containerwidget(
 115                edit=self._root_widget,
 116                cancel_button=self._back_button,
 117                selected_child=self._back_button,
 118            )
 119
 120        self._title_text = bui.textwidget(
 121            parent=self._root_widget,
 122            position=(
 123                self._width * 0.5,
 124                self._height - (66 if uiscale is bui.UIScale.SMALL else 56),
 125            ),
 126            size=(0, 0),
 127            text=bui.Lstr(
 128                resource='league.leagueRankText',
 129                fallback_resource='coopSelectWindow.powerRankingText',
 130            ),
 131            h_align='center',
 132            color=bui.app.ui_v1.title_color,
 133            scale=1.4,
 134            maxwidth=600,
 135            v_align='center',
 136        )
 137
 138        self._scroll_width = self._width - (130 + 2 * x_inset)
 139        self._scroll_height = self._height - 160
 140        self._scrollwidget = bui.scrollwidget(
 141            parent=self._root_widget,
 142            highlight=False,
 143            position=(65 + x_inset, 70),
 144            size=(self._scroll_width, self._scroll_height),
 145            center_small_content=True,
 146            border_opacity=0.4,
 147        )
 148        bui.widget(edit=self._scrollwidget, autoselect=True)
 149        bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
 150
 151        self._last_power_ranking_query_time: float | None = None
 152        self._doing_power_ranking_query = False
 153
 154        self._subcontainer: bui.Widget | None = None
 155        self._subcontainerwidth = max(800, self._scroll_width)
 156        self._subcontainerheight = 483
 157        self._power_ranking_score_widgets: list[bui.Widget] = []
 158
 159        self._season_popup_menu: PopupMenu | None = None
 160        self._requested_season: str | None = None
 161        self._season: str | None = None
 162
 163        # take note of our account state; we'll refresh later if this changes
 164        self._account_state = plus.get_v1_account_state()
 165
 166        self._refresh()
 167        self._restore_state()
 168
 169        # if we've got cached power-ranking data already, display it
 170        assert bui.app.classic is not None
 171        info = bui.app.classic.accounts.get_cached_league_rank_data()
 172        if info is not None:
 173            self._update_for_league_rank_data(info)
 174
 175        self._update_timer = bui.AppTimer(
 176            1.0, bui.WeakCall(self._update), repeat=True
 177        )
 178        self._update(show=info is None)
 179
 180    @override
 181    def get_main_window_state(self) -> bui.MainWindowState:
 182        # Support recreating our window for back/refresh purposes.
 183        cls = type(self)
 184        return bui.BasicMainWindowState(
 185            create_call=lambda transition, origin_widget: cls(
 186                transition=transition, origin_widget=origin_widget
 187            )
 188        )
 189
 190    @override
 191    def on_main_window_close(self) -> None:
 192        self._save_state()
 193
 194    def _on_achievements_press(self) -> None:
 195        from bauiv1lib.achievements import AchievementsWindow
 196
 197        # only allow this for all-time or the current season
 198        # (we currently don't keep specific achievement data for old seasons)
 199        if self._season == 'a' or self._is_current_season:
 200            prab = self._power_ranking_achievements_button
 201            assert prab is not None
 202            self.main_window_replace(AchievementsWindow(origin_widget=prab))
 203        else:
 204            bui.screenmessage(
 205                bui.Lstr(
 206                    resource='achievementsUnavailableForOldSeasonsText',
 207                    fallback_resource='unavailableText',
 208                ),
 209                color=(1, 0, 0),
 210            )
 211            bui.getsound('error').play()
 212
 213    def _on_activity_mult_press(self) -> None:
 214        from bauiv1lib import confirm
 215
 216        plus = bui.app.plus
 217        assert plus is not None
 218
 219        txt = bui.Lstr(
 220            resource=(
 221                'coopSelectWindow.activenessAllTimeInfoText'
 222                if self._season == 'a'
 223                else 'coopSelectWindow.activenessInfoText'
 224            ),
 225            subs=[
 226                (
 227                    '${MAX}',
 228                    str(
 229                        plus.get_v1_account_misc_read_val('activenessMax', 1.0)
 230                    ),
 231                )
 232            ],
 233        )
 234        confirm.ConfirmWindow(
 235            txt,
 236            cancel_button=False,
 237            width=460,
 238            height=150,
 239            origin_widget=self._activity_mult_button,
 240        )
 241
 242    def _on_pro_mult_press(self) -> None:
 243        from bauiv1lib import confirm
 244
 245        plus = bui.app.plus
 246        assert plus is not None
 247
 248        txt = bui.Lstr(
 249            resource='coopSelectWindow.proMultInfoText',
 250            subs=[
 251                (
 252                    '${PERCENT}',
 253                    str(
 254                        plus.get_v1_account_misc_read_val(
 255                            'proPowerRankingBoost', 10
 256                        )
 257                    ),
 258                ),
 259                (
 260                    '${PRO}',
 261                    bui.Lstr(
 262                        resource='store.bombSquadProNameText',
 263                        subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
 264                    ),
 265                ),
 266            ],
 267        )
 268        confirm.ConfirmWindow(
 269            txt,
 270            cancel_button=False,
 271            width=460,
 272            height=130,
 273            origin_widget=self._pro_mult_button,
 274        )
 275
 276    def _on_trophies_press(self) -> None:
 277        from bauiv1lib.trophies import TrophiesWindow
 278
 279        info = self._league_rank_data
 280        if info is not None:
 281            prtb = self._power_ranking_trophies_button
 282            assert prtb is not None
 283            TrophiesWindow(
 284                position=prtb.get_screen_space_center(),
 285                data=info,
 286            )
 287        else:
 288            bui.getsound('error').play()
 289
 290    def _on_power_ranking_query_response(
 291        self, data: dict[str, Any] | None
 292    ) -> None:
 293        self._doing_power_ranking_query = False
 294
 295        # Important: *only* cache this if we requested the current season.
 296        if data is not None and data.get('s', None) is None:
 297            assert bui.app.classic is not None
 298            bui.app.classic.accounts.cache_league_rank_data(data)
 299
 300        # Always store a copy locally though (even for other seasons).
 301        self._league_rank_data = copy.deepcopy(data)
 302        self._update_for_league_rank_data(data)
 303
 304    def _restore_state(self) -> None:
 305        pass
 306
 307    def _update(self, show: bool = False) -> None:
 308        plus = bui.app.plus
 309        assert plus is not None
 310
 311        cur_time = bui.apptime()
 312
 313        # If our account state has changed, refresh our UI.
 314        account_state = plus.get_v1_account_state()
 315        if account_state != self._account_state:
 316            self._account_state = account_state
 317            self._save_state()
 318            self._refresh()
 319
 320            # And power ranking too.
 321            if not self._doing_power_ranking_query:
 322                self._last_power_ranking_query_time = None
 323
 324        # Send off a new power-ranking query if its been long enough or our
 325        # requested season has changed or whatnot.
 326        if not self._doing_power_ranking_query and (
 327            self._last_power_ranking_query_time is None
 328            or cur_time - self._last_power_ranking_query_time > 30.0
 329        ):
 330            try:
 331                if show:
 332                    bui.textwidget(edit=self._league_title_text, text='')
 333                    bui.textwidget(edit=self._league_text, text='')
 334                    bui.textwidget(edit=self._league_number_text, text='')
 335                    bui.textwidget(edit=self._your_power_ranking_text, text='')
 336                    bui.spinnerwidget(edit=self._loading_spinner, visible=True)
 337                    bui.textwidget(edit=self._to_ranked_text, text='')
 338                    bui.textwidget(edit=self._power_ranking_rank_text, text='')
 339                    bui.textwidget(edit=self._season_ends_text, text='')
 340                    bui.textwidget(edit=self._trophy_counts_reset_text, text='')
 341            except Exception:
 342                logging.exception('Error showing updated rank info.')
 343
 344            self._last_power_ranking_query_time = cur_time
 345            self._doing_power_ranking_query = True
 346            plus.power_ranking_query(
 347                season=self._requested_season,
 348                callback=bui.WeakCall(self._on_power_ranking_query_response),
 349            )
 350
 351    def _refresh(self) -> None:
 352        # pylint: disable=too-many-statements
 353
 354        plus = bui.app.plus
 355        assert plus is not None
 356
 357        # (re)create the sub-container if need be..
 358        if self._subcontainer is not None:
 359            self._subcontainer.delete()
 360        self._subcontainer = bui.containerwidget(
 361            parent=self._scrollwidget,
 362            size=(self._subcontainerwidth, self._subcontainerheight),
 363            background=False,
 364        )
 365
 366        w_parent = self._subcontainer
 367        v = self._subcontainerheight - 20
 368
 369        v -= 0
 370
 371        h2 = 80
 372        v2 = v - 60
 373        worth_color = (0.6, 0.6, 0.65)
 374        tally_color = (0.5, 0.6, 0.8)
 375        spc = 43
 376
 377        h_offs_tally = 150
 378        tally_maxwidth = 120
 379        v2 -= 70
 380
 381        bui.textwidget(
 382            parent=w_parent,
 383            position=(self._xoffs + h2 - 60, v2 + 106),
 384            size=(0, 0),
 385            flatness=1.0,
 386            shadow=0.0,
 387            text=bui.Lstr(resource='coopSelectWindow.pointsText'),
 388            h_align='left',
 389            v_align='center',
 390            scale=0.8,
 391            color=(1, 1, 1, 0.3),
 392            maxwidth=200,
 393        )
 394
 395        self._power_ranking_achievements_button = bui.buttonwidget(
 396            parent=w_parent,
 397            position=(self._xoffs + h2 - 60, v2 + 10),
 398            size=(200, 80),
 399            icon=bui.gettexture('achievementsIcon'),
 400            autoselect=True,
 401            on_activate_call=bui.WeakCall(self._on_achievements_press),
 402            up_widget=self._back_button,
 403            left_widget=self._back_button,
 404            color=(0.5, 0.5, 0.6),
 405            textcolor=(0.7, 0.7, 0.8),
 406            label='',
 407        )
 408
 409        self._power_ranking_achievement_total_text = bui.textwidget(
 410            parent=w_parent,
 411            position=(self._xoffs + h2 + h_offs_tally, v2 + 45),
 412            size=(0, 0),
 413            flatness=1.0,
 414            shadow=0.0,
 415            text='-',
 416            h_align='left',
 417            v_align='center',
 418            scale=0.8,
 419            color=tally_color,
 420            maxwidth=tally_maxwidth,
 421        )
 422
 423        v2 -= 80
 424
 425        self._power_ranking_trophies_button = bui.buttonwidget(
 426            parent=w_parent,
 427            position=(self._xoffs + h2 - 60, v2 + 10),
 428            size=(200, 80),
 429            icon=bui.gettexture('medalSilver'),
 430            autoselect=True,
 431            on_activate_call=bui.WeakCall(self._on_trophies_press),
 432            left_widget=self._back_button,
 433            color=(0.5, 0.5, 0.6),
 434            textcolor=(0.7, 0.7, 0.8),
 435            label='',
 436        )
 437        self._power_ranking_trophies_total_text = bui.textwidget(
 438            parent=w_parent,
 439            position=(self._xoffs + h2 + h_offs_tally, v2 + 45),
 440            size=(0, 0),
 441            flatness=1.0,
 442            shadow=0.0,
 443            text='-',
 444            h_align='left',
 445            v_align='center',
 446            scale=0.8,
 447            color=tally_color,
 448            maxwidth=tally_maxwidth,
 449        )
 450
 451        v2 -= 100
 452
 453        bui.textwidget(
 454            parent=w_parent,
 455            position=(self._xoffs + h2 - 60, v2 + 86),
 456            size=(0, 0),
 457            flatness=1.0,
 458            shadow=0.0,
 459            text=bui.Lstr(resource='coopSelectWindow.multipliersText'),
 460            h_align='left',
 461            v_align='center',
 462            scale=0.8,
 463            color=(1, 1, 1, 0.3),
 464            maxwidth=200,
 465        )
 466
 467        self._activity_mult_button: bui.Widget | None
 468        if plus.get_v1_account_misc_read_val('act', False):
 469            self._activity_mult_button = bui.buttonwidget(
 470                parent=w_parent,
 471                position=(self._xoffs + h2 - 60, v2 + 10),
 472                size=(200, 60),
 473                icon=bui.gettexture('heart'),
 474                icon_color=(0.5, 0, 0.5),
 475                label=bui.Lstr(resource='coopSelectWindow.activityText'),
 476                autoselect=True,
 477                on_activate_call=bui.WeakCall(self._on_activity_mult_press),
 478                left_widget=self._back_button,
 479                color=(0.5, 0.5, 0.6),
 480                textcolor=(0.7, 0.7, 0.8),
 481            )
 482
 483            self._activity_mult_text = bui.textwidget(
 484                parent=w_parent,
 485                position=(self._xoffs + h2 + h_offs_tally, v2 + 40),
 486                size=(0, 0),
 487                flatness=1.0,
 488                shadow=0.0,
 489                text='-',
 490                h_align='left',
 491                v_align='center',
 492                scale=0.8,
 493                color=tally_color,
 494                maxwidth=tally_maxwidth,
 495            )
 496            v2 -= 65
 497        else:
 498            self._activity_mult_button = None
 499
 500        self._pro_mult_button = bui.buttonwidget(
 501            parent=w_parent,
 502            position=(self._xoffs + h2 - 60, v2 + 10),
 503            size=(200, 60),
 504            icon=bui.gettexture('logo'),
 505            icon_color=(0.3, 0, 0.3),
 506            label=bui.Lstr(
 507                resource='store.bombSquadProNameText',
 508                subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
 509            ),
 510            autoselect=True,
 511            on_activate_call=bui.WeakCall(self._on_pro_mult_press),
 512            left_widget=self._back_button,
 513            color=(0.5, 0.5, 0.6),
 514            textcolor=(0.7, 0.7, 0.8),
 515        )
 516
 517        self._pro_mult_text = bui.textwidget(
 518            parent=w_parent,
 519            position=(self._xoffs + h2 + h_offs_tally, v2 + 40),
 520            size=(0, 0),
 521            flatness=1.0,
 522            shadow=0.0,
 523            text='-',
 524            h_align='left',
 525            v_align='center',
 526            scale=0.8,
 527            color=tally_color,
 528            maxwidth=tally_maxwidth,
 529        )
 530        v2 -= 30
 531
 532        v2 -= spc
 533        bui.textwidget(
 534            parent=w_parent,
 535            position=(self._xoffs + h2 + h_offs_tally - 10 - 40, v2 + 35),
 536            size=(0, 0),
 537            flatness=1.0,
 538            shadow=0.0,
 539            text=bui.Lstr(resource='finalScoreText'),
 540            h_align='right',
 541            v_align='center',
 542            scale=0.9,
 543            color=worth_color,
 544            maxwidth=150,
 545        )
 546        self._power_ranking_total_text = bui.textwidget(
 547            parent=w_parent,
 548            position=(self._xoffs + h2 + h_offs_tally - 40, v2 + 35),
 549            size=(0, 0),
 550            flatness=1.0,
 551            shadow=0.0,
 552            text='-',
 553            h_align='left',
 554            v_align='center',
 555            scale=0.9,
 556            color=tally_color,
 557            maxwidth=tally_maxwidth,
 558        )
 559
 560        self._season_show_text = bui.textwidget(
 561            parent=w_parent,
 562            position=(self._xoffs + 390 - 15, v - 20),
 563            size=(0, 0),
 564            color=(0.6, 0.6, 0.7),
 565            maxwidth=200,
 566            text=bui.Lstr(resource='showText'),
 567            h_align='right',
 568            v_align='center',
 569            scale=0.8,
 570            shadow=0,
 571            flatness=1.0,
 572        )
 573
 574        self._league_title_text = bui.textwidget(
 575            parent=w_parent,
 576            position=(self._xoffs + 470, v - 97),
 577            size=(0, 0),
 578            color=(0.6, 0.6, 0.7),
 579            maxwidth=230,
 580            text='',
 581            h_align='center',
 582            v_align='center',
 583            scale=0.9,
 584            shadow=0,
 585            flatness=1.0,
 586        )
 587
 588        self._league_text_scale = 1.8
 589        self._league_text_maxwidth = 210
 590        self._league_text = bui.textwidget(
 591            parent=w_parent,
 592            position=(self._xoffs + 470, v - 140),
 593            size=(0, 0),
 594            color=(1, 1, 1),
 595            maxwidth=self._league_text_maxwidth,
 596            text='-',
 597            h_align='center',
 598            v_align='center',
 599            scale=self._league_text_scale,
 600            shadow=1.0,
 601            flatness=1.0,
 602        )
 603        self._league_number_base_pos = (470, v - 140)
 604        self._league_number_text = bui.textwidget(
 605            parent=w_parent,
 606            position=(self._xoffs + 470, v - 140),
 607            size=(0, 0),
 608            color=(1, 1, 1),
 609            maxwidth=100,
 610            text='',
 611            h_align='left',
 612            v_align='center',
 613            scale=0.8,
 614            shadow=1.0,
 615            flatness=1.0,
 616        )
 617
 618        self._loading_spinner = bui.spinnerwidget(
 619            parent=w_parent,
 620            position=(
 621                self._subcontainerwidth * 0.5,
 622                self._subcontainerheight * 0.5,
 623            ),
 624            size=64,
 625        )
 626        self._your_power_ranking_text = bui.textwidget(
 627            parent=w_parent,
 628            position=(self._xoffs + 470, v - 142 - 70),
 629            size=(0, 0),
 630            color=(0.6, 0.6, 0.7),
 631            maxwidth=230,
 632            text='',
 633            h_align='center',
 634            v_align='center',
 635            scale=0.9,
 636            shadow=0,
 637            flatness=1.0,
 638        )
 639
 640        self._to_ranked_text = bui.textwidget(
 641            parent=w_parent,
 642            position=(self._xoffs + 470, v - 250 - 70),
 643            size=(0, 0),
 644            color=(0.6, 0.6, 0.7),
 645            maxwidth=230,
 646            text='',
 647            h_align='center',
 648            v_align='center',
 649            scale=0.8,
 650            shadow=0,
 651            flatness=1.0,
 652        )
 653
 654        self._power_ranking_rank_text = bui.textwidget(
 655            parent=w_parent,
 656            position=(self._xoffs + 473, v - 210 - 70),
 657            size=(0, 0),
 658            big=False,
 659            text='-',
 660            h_align='center',
 661            v_align='center',
 662            scale=1.0,
 663        )
 664
 665        self._season_ends_text = bui.textwidget(
 666            parent=w_parent,
 667            position=(self._xoffs + 470, v - 380),
 668            size=(0, 0),
 669            color=(0.6, 0.6, 0.6),
 670            maxwidth=230,
 671            text='',
 672            h_align='center',
 673            v_align='center',
 674            scale=0.9,
 675            shadow=0,
 676            flatness=1.0,
 677        )
 678        self._trophy_counts_reset_text = bui.textwidget(
 679            parent=w_parent,
 680            position=(self._xoffs + 470, v - 410),
 681            size=(0, 0),
 682            color=(0.5, 0.5, 0.5),
 683            maxwidth=230,
 684            text='Trophy counts will reset next season.',
 685            h_align='center',
 686            v_align='center',
 687            scale=0.8,
 688            shadow=0,
 689            flatness=1.0,
 690        )
 691
 692        self._power_ranking_score_widgets = []
 693
 694        self._power_ranking_score_v = v - 56
 695
 696        h = 707
 697        v -= 451
 698
 699        self._see_more_button = bui.buttonwidget(
 700            parent=w_parent,
 701            label=self._rdict.seeMoreText,
 702            position=(self._xoffs + h, v),
 703            color=(0.5, 0.5, 0.6),
 704            textcolor=(0.7, 0.7, 0.8),
 705            size=(230, 60),
 706            autoselect=True,
 707            on_activate_call=bui.WeakCall(self._on_more_press),
 708        )
 709
 710    def _on_more_press(self) -> None:
 711        plus = bui.app.plus
 712        assert plus is not None
 713
 714        our_login_id = plus.get_v1_account_public_login_id()
 715        # our_login_id = _bs.get_account_misc_read_val_2(
 716        #     'resolvedAccountID', None)
 717        if not self._can_do_more_button or our_login_id is None:
 718            bui.getsound('error').play()
 719            bui.screenmessage(
 720                bui.Lstr(resource='unavailableText'), color=(1, 0, 0)
 721            )
 722            return
 723        if self._season is None:
 724            season_str = ''
 725        else:
 726            season_str = '&season=' + (
 727                'all_time' if self._season == 'a' else self._season
 728            )
 729        if self._league_url_arg != '':
 730            league_str = '&league=' + self._league_url_arg
 731        else:
 732            league_str = ''
 733        bui.open_url(
 734            plus.get_master_server_address()
 735            + '/highscores?list=powerRankings&v=2'
 736            + league_str
 737            + season_str
 738            + '&player='
 739            + our_login_id
 740        )
 741
 742    def _update_for_league_rank_data(self, data: dict[str, Any] | None) -> None:
 743        # pylint: disable=too-many-statements
 744        # pylint: disable=too-many-branches
 745        # pylint: disable=too-many-locals
 746        if not self._root_widget:
 747            return
 748        plus = bui.app.plus
 749        uiscale = bui.app.ui_v1.uiscale
 750        assert plus is not None
 751        assert bui.app.classic is not None
 752        accounts = bui.app.classic.accounts
 753        in_top = data is not None and data['rank'] is not None
 754        eq_text = self._rdict.powerRankingPointsEqualsText
 755        pts_txt = self._rdict.powerRankingPointsText
 756        num_text = bui.Lstr(resource='numberText').evaluate()
 757        do_percent = False
 758        finished_season_unranked = False
 759        self._can_do_more_button = True
 760        extra_text = ''
 761        if plus.get_v1_account_state() != 'signed_in':
 762            status_text = (
 763                '(' + bui.Lstr(resource='notSignedInText').evaluate() + ')'
 764            )
 765        elif in_top:
 766            assert data is not None
 767            status_text = num_text.replace('${NUMBER}', str(data['rank']))
 768        elif data is not None:
 769            try:
 770                # handle old seasons where we didn't wind up ranked
 771                # at the end..
 772                if not data['scores']:
 773                    status_text = (
 774                        self._rdict.powerRankingFinishedSeasonUnrankedText
 775                    )
 776                    extra_text = ''
 777                    finished_season_unranked = True
 778                    self._can_do_more_button = False
 779                else:
 780                    our_points = accounts.get_league_rank_points(data)
 781                    progress = float(our_points) / max(1, data['scores'][-1][1])
 782                    status_text = str(int(progress * 100.0)) + '%'
 783                    extra_text = (
 784                        '\n'
 785                        + self._rdict.powerRankingPointsToRankedText.replace(
 786                            '${CURRENT}', str(our_points)
 787                        ).replace('${REMAINING}', str(data['scores'][-1][1]))
 788                    )
 789                    do_percent = True
 790            except Exception:
 791                logging.exception('Error updating power ranking.')
 792                status_text = self._rdict.powerRankingNotInTopText.replace(
 793                    '${NUMBER}', str(data['listSize'])
 794                )
 795                extra_text = ''
 796        else:
 797            status_text = '-'
 798
 799        self._season = data['s'] if data is not None else None
 800
 801        v = self._subcontainerheight - 20
 802        popup_was_selected = False
 803        if self._season_popup_menu is not None:
 804            btn = self._season_popup_menu.get_button()
 805            assert self._subcontainer
 806            if self._subcontainer.get_selected_child() == btn:
 807                popup_was_selected = True
 808            btn.delete()
 809        season_choices = []
 810        season_choices_display = []
 811        did_first = False
 812        self._is_current_season = False
 813        if data is not None:
 814            # build our list of seasons we have available
 815            for ssn in data['sl']:
 816                season_choices.append(ssn)
 817                if ssn != 'a' and not did_first:
 818                    season_choices_display.append(
 819                        bui.Lstr(
 820                            resource='league.currentSeasonText',
 821                            subs=[('${NUMBER}', ssn)],
 822                        )
 823                    )
 824                    did_first = True
 825                    # if we either did not specify a season or specified the
 826                    # first, we're looking at the current..
 827                    if self._season in [ssn, None]:
 828                        self._is_current_season = True
 829                elif ssn == 'a':
 830                    season_choices_display.append(
 831                        bui.Lstr(resource='league.allTimeText')
 832                    )
 833                else:
 834                    season_choices_display.append(
 835                        bui.Lstr(
 836                            resource='league.seasonText',
 837                            subs=[('${NUMBER}', ssn)],
 838                        )
 839                    )
 840            assert self._subcontainer
 841            self._season_popup_menu = PopupMenu(
 842                parent=self._subcontainer,
 843                position=(self._xoffs + 390, v - 45),
 844                width=150,
 845                button_size=(200, 50),
 846                choices=season_choices,
 847                on_value_change_call=bui.WeakCall(self._on_season_change),
 848                choices_display=season_choices_display,
 849                current_choice=self._season,
 850            )
 851            if popup_was_selected:
 852                bui.containerwidget(
 853                    edit=self._subcontainer,
 854                    selected_child=self._season_popup_menu.get_button(),
 855                )
 856            bui.widget(edit=self._see_more_button, show_buffer_bottom=100)
 857            bui.widget(
 858                edit=self._season_popup_menu.get_button(),
 859                up_widget=self._back_button,
 860            )
 861            if uiscale is not bui.UIScale.SMALL:
 862                bui.widget(
 863                    edit=self._back_button,
 864                    down_widget=self._power_ranking_achievements_button,
 865                    right_widget=self._season_popup_menu.get_button(),
 866                )
 867
 868        bui.textwidget(
 869            edit=self._league_title_text,
 870            text=(
 871                ''
 872                if self._season == 'a'
 873                else bui.Lstr(resource='league.leagueText')
 874            ),
 875        )
 876
 877        if data is None:
 878            lname = ''
 879            lnum = ''
 880            lcolor = (1, 1, 1)
 881            self._league_url_arg = ''
 882        elif self._season == 'a':
 883            lname = bui.Lstr(resource='league.allTimeText').evaluate()
 884            lnum = ''
 885            lcolor = (1, 1, 1)
 886            self._league_url_arg = ''
 887        else:
 888            lnum = ('[' + str(data['l']['i']) + ']') if data['l']['i2'] else ''
 889            lname = bui.Lstr(
 890                translate=('leagueNames', data['l']['n'])
 891            ).evaluate()
 892            lcolor = data['l']['c']
 893            self._league_url_arg = (
 894                data['l']['n'] + '_' + str(data['l']['i'])
 895            ).lower()
 896
 897        to_end_string: bui.Lstr | str
 898        if data is None or self._season == 'a' or data['se'] is None:
 899            to_end_string = ''
 900            show_season_end = False
 901        else:
 902            show_season_end = True
 903            days_to_end = data['se'][0]
 904            minutes_to_end = data['se'][1]
 905            if days_to_end > 0:
 906                to_end_string = bui.Lstr(
 907                    resource='league.seasonEndsDaysText',
 908                    subs=[('${NUMBER}', str(days_to_end))],
 909                )
 910            elif days_to_end == 0 and minutes_to_end >= 60:
 911                to_end_string = bui.Lstr(
 912                    resource='league.seasonEndsHoursText',
 913                    subs=[('${NUMBER}', str(minutes_to_end // 60))],
 914                )
 915            elif days_to_end == 0 and minutes_to_end >= 0:
 916                to_end_string = bui.Lstr(
 917                    resource='league.seasonEndsMinutesText',
 918                    subs=[('${NUMBER}', str(minutes_to_end))],
 919                )
 920            else:
 921                to_end_string = bui.Lstr(
 922                    resource='league.seasonEndedDaysAgoText',
 923                    subs=[('${NUMBER}', str(-(days_to_end + 1)))],
 924                )
 925
 926        bui.textwidget(edit=self._season_ends_text, text=to_end_string)
 927        bui.textwidget(
 928            edit=self._trophy_counts_reset_text,
 929            text=(
 930                bui.Lstr(resource='league.trophyCountsResetText')
 931                if self._is_current_season and show_season_end
 932                else ''
 933            ),
 934        )
 935
 936        bui.textwidget(edit=self._league_text, text=lname, color=lcolor)
 937        l_text_width = min(
 938            self._league_text_maxwidth,
 939            bui.get_string_width(lname, suppress_warning=True)
 940            * self._league_text_scale,
 941        )
 942        bui.textwidget(
 943            edit=self._league_number_text,
 944            text=lnum,
 945            color=lcolor,
 946            position=(
 947                self._xoffs
 948                + self._league_number_base_pos[0]
 949                + l_text_width * 0.5
 950                + 8,
 951                self._league_number_base_pos[1] + 10,
 952            ),
 953        )
 954        bui.textwidget(
 955            edit=self._to_ranked_text,
 956            text=(
 957                bui.Lstr(resource='coopSelectWindow.toRankedText').evaluate()
 958                + ''
 959                + extra_text
 960                if do_percent
 961                else ''
 962            ),
 963        )
 964
 965        bui.textwidget(
 966            edit=self._your_power_ranking_text,
 967            text=(
 968                bui.Lstr(
 969                    resource='rankText',
 970                    fallback_resource='coopSelectWindow.yourPowerRankingText',
 971                )
 972                if (not do_percent)
 973                else ''
 974            ),
 975        )
 976        bui.spinnerwidget(edit=self._loading_spinner, visible=False)
 977
 978        bui.textwidget(
 979            edit=self._power_ranking_rank_text,
 980            position=(self._xoffs + 473, v - 70 - (170 if do_percent else 220)),
 981            text=status_text,
 982            big=(in_top or do_percent),
 983            scale=(
 984                3.0
 985                if (in_top or do_percent)
 986                else 0.7 if finished_season_unranked else 1.0
 987            ),
 988        )
 989
 990        if self._activity_mult_button is not None:
 991            if data is None or data['act'] is None:
 992                bui.buttonwidget(
 993                    edit=self._activity_mult_button,
 994                    textcolor=(0.7, 0.7, 0.8, 0.5),
 995                    icon_color=(0.5, 0, 0.5, 0.3),
 996                )
 997                bui.textwidget(edit=self._activity_mult_text, text='     -')
 998            else:
 999                bui.buttonwidget(
1000                    edit=self._activity_mult_button,
1001                    textcolor=(0.7, 0.7, 0.8, 1.0),
1002                    icon_color=(0.5, 0, 0.5, 1.0),
1003                )
1004                # pylint: disable=consider-using-f-string
1005                bui.textwidget(
1006                    edit=self._activity_mult_text,
1007                    text='x ' + ('%.2f' % data['act']),
1008                )
1009
1010        have_pro = False if data is None else data['p']
1011        pro_mult = (
1012            1.0
1013            + float(
1014                plus.get_v1_account_misc_read_val('proPowerRankingBoost', 0.0)
1015            )
1016            * 0.01
1017        )
1018        # pylint: disable=consider-using-f-string
1019        bui.textwidget(
1020            edit=self._pro_mult_text,
1021            text=(
1022                '     -'
1023                if (data is None or not have_pro)
1024                else 'x ' + ('%.2f' % pro_mult)
1025            ),
1026        )
1027        bui.buttonwidget(
1028            edit=self._pro_mult_button,
1029            textcolor=(0.7, 0.7, 0.8, (1.0 if have_pro else 0.5)),
1030            icon_color=(0.5, 0, 0.5) if have_pro else (0.5, 0, 0.5, 0.2),
1031        )
1032        bui.buttonwidget(
1033            edit=self._power_ranking_achievements_button,
1034            label=('' if data is None else str(data['a']) + ' ')
1035            + bui.Lstr(resource='achievementsText').evaluate(),
1036        )
1037
1038        # for the achievement value, use the number they gave us for
1039        # non-current seasons; otherwise calc our own
1040        total_ach_value = 0
1041        for ach in bui.app.classic.ach.achievements:
1042            if ach.complete:
1043                total_ach_value += ach.power_ranking_value
1044        if self._season != 'a' and not self._is_current_season:
1045            if data is not None and 'at' in data:
1046                total_ach_value = data['at']
1047
1048        bui.textwidget(
1049            edit=self._power_ranking_achievement_total_text,
1050            text=(
1051                '-'
1052                if data is None
1053                else ('+ ' + pts_txt.replace('${NUMBER}', str(total_ach_value)))
1054            ),
1055        )
1056
1057        total_trophies_count = accounts.get_league_rank_points(
1058            data, 'trophyCount'
1059        )
1060        total_trophies_value = accounts.get_league_rank_points(data, 'trophies')
1061        bui.buttonwidget(
1062            edit=self._power_ranking_trophies_button,
1063            label=('' if data is None else str(total_trophies_count) + ' ')
1064            + bui.Lstr(resource='trophiesText').evaluate(),
1065        )
1066        bui.textwidget(
1067            edit=self._power_ranking_trophies_total_text,
1068            text=(
1069                '-'
1070                if data is None
1071                else (
1072                    '+ '
1073                    + pts_txt.replace('${NUMBER}', str(total_trophies_value))
1074                )
1075            ),
1076        )
1077
1078        bui.textwidget(
1079            edit=self._power_ranking_total_text,
1080            text=(
1081                '-'
1082                if data is None
1083                else eq_text.replace(
1084                    '${NUMBER}', str(accounts.get_league_rank_points(data))
1085                )
1086            ),
1087        )
1088        for widget in self._power_ranking_score_widgets:
1089            widget.delete()
1090        self._power_ranking_score_widgets = []
1091
1092        scores = data['scores'] if data is not None else []
1093        tally_color = (0.5, 0.6, 0.8)
1094        w_parent = self._subcontainer
1095        v2 = self._power_ranking_score_v
1096
1097        for score in scores:
1098            h2 = 680
1099            is_us = score[3]
1100            self._power_ranking_score_widgets.append(
1101                bui.textwidget(
1102                    parent=w_parent,
1103                    position=(self._xoffs + h2 - 20, v2),
1104                    size=(0, 0),
1105                    color=(1, 1, 1) if is_us else (0.6, 0.6, 0.7),
1106                    maxwidth=40,
1107                    flatness=1.0,
1108                    shadow=0.0,
1109                    text=num_text.replace('${NUMBER}', str(score[0])),
1110                    h_align='right',
1111                    v_align='center',
1112                    scale=0.5,
1113                )
1114            )
1115            self._power_ranking_score_widgets.append(
1116                bui.textwidget(
1117                    parent=w_parent,
1118                    position=(self._xoffs + h2 + 20, v2),
1119                    size=(0, 0),
1120                    color=(1, 1, 1) if is_us else tally_color,
1121                    maxwidth=60,
1122                    text=str(score[1]),
1123                    flatness=1.0,
1124                    shadow=0.0,
1125                    h_align='center',
1126                    v_align='center',
1127                    scale=0.7,
1128                )
1129            )
1130            txt = bui.textwidget(
1131                parent=w_parent,
1132                position=(self._xoffs + h2 + 60, v2 - (28 * 0.5) / 0.9),
1133                size=(210 / 0.9, 28),
1134                color=(1, 1, 1) if is_us else (0.6, 0.6, 0.6),
1135                maxwidth=210,
1136                flatness=1.0,
1137                shadow=0.0,
1138                autoselect=True,
1139                selectable=True,
1140                click_activate=True,
1141                text=score[2],
1142                h_align='left',
1143                v_align='center',
1144                scale=0.9,
1145            )
1146            self._power_ranking_score_widgets.append(txt)
1147            bui.textwidget(
1148                edit=txt,
1149                on_activate_call=bui.Call(
1150                    self._show_account_info, score[4], txt
1151                ),
1152            )
1153            assert self._season_popup_menu is not None
1154            bui.widget(
1155                edit=txt, left_widget=self._season_popup_menu.get_button()
1156            )
1157            v2 -= 28
1158
1159    def _show_account_info(
1160        self, account_id: str, textwidget: bui.Widget
1161    ) -> None:
1162        from bauiv1lib.account.viewer import AccountViewerWindow
1163
1164        bui.getsound('swish').play()
1165        AccountViewerWindow(
1166            account_id=account_id, position=textwidget.get_screen_space_center()
1167        )
1168
1169    def _on_season_change(self, value: str) -> None:
1170        self._requested_season = value
1171        self._last_power_ranking_query_time = None  # make sure we update asap
1172        self._update(show=True)
1173
1174    def _save_state(self) -> None:
1175        pass
class LeagueRankWindow(bauiv1._uitypes.MainWindow):
  20class LeagueRankWindow(bui.MainWindow):
  21    """Window for showing league rank."""
  22
  23    def __init__(
  24        self,
  25        transition: str | None = 'in_right',
  26        origin_widget: bui.Widget | None = None,
  27    ):
  28        # pylint: disable=too-many-statements
  29        plus = bui.app.plus
  30        assert plus is not None
  31
  32        bui.set_analytics_screen('League Rank Window')
  33
  34        self._league_rank_data: dict[str, Any] | None = None
  35
  36        self._power_ranking_achievements_button: bui.Widget | None = None
  37        self._pro_mult_button: bui.Widget | None = None
  38        self._power_ranking_trophies_button: bui.Widget | None = None
  39        self._league_title_text: bui.Widget | None = None
  40        self._league_text: bui.Widget | None = None
  41        self._league_number_text: bui.Widget | None = None
  42        self._your_power_ranking_text: bui.Widget | None = None
  43        self._loading_spinner: bui.Widget | None = None
  44        self._season_ends_text: bui.Widget | None = None
  45        self._power_ranking_rank_text: bui.Widget | None = None
  46        self._to_ranked_text: bui.Widget | None = None
  47        self._trophy_counts_reset_text: bui.Widget | None = None
  48
  49        assert bui.app.classic is not None
  50        uiscale = bui.app.ui_v1.uiscale
  51        self._width = 1490 if uiscale is bui.UIScale.SMALL else 1120
  52        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
  53        self._height = (
  54            660
  55            if uiscale is bui.UIScale.SMALL
  56            else 710 if uiscale is bui.UIScale.MEDIUM else 800
  57        )
  58        self._r = 'coopSelectWindow'
  59        self._rdict = bui.app.lang.get_resource(self._r)
  60        top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
  61
  62        self._xoffs = 80.0 if uiscale is bui.UIScale.SMALL else 0
  63
  64        self._league_url_arg = ''
  65
  66        self._is_current_season = False
  67        self._can_do_more_button = True
  68
  69        super().__init__(
  70            root_widget=bui.containerwidget(
  71                size=(self._width, self._height + top_extra),
  72                stack_offset=(
  73                    (0, 0)
  74                    if uiscale is bui.UIScale.SMALL
  75                    else (0, 10) if uiscale is bui.UIScale.MEDIUM else (0, 0)
  76                ),
  77                scale=(
  78                    1.08
  79                    if uiscale is bui.UIScale.SMALL
  80                    else 0.93 if uiscale is bui.UIScale.MEDIUM else 0.8
  81                ),
  82                toolbar_visibility=(
  83                    'menu_minimal'
  84                    if uiscale is bui.UIScale.SMALL
  85                    else 'menu_full'
  86                ),
  87            ),
  88            transition=transition,
  89            origin_widget=origin_widget,
  90        )
  91
  92        if uiscale is bui.UIScale.SMALL:
  93            self._back_button = bui.get_special_widget('back_button')
  94            bui.containerwidget(
  95                edit=self._root_widget, on_cancel_call=self.main_window_back
  96            )
  97        else:
  98            self._back_button = btn = bui.buttonwidget(
  99                parent=self._root_widget,
 100                position=(75 + x_inset, self._height - 87),
 101                size=(120, 60),
 102                scale=1.2,
 103                autoselect=True,
 104                label=bui.Lstr(resource='backText'),
 105                button_type='back',
 106                on_activate_call=self.main_window_back,
 107            )
 108            bui.buttonwidget(
 109                edit=btn,
 110                button_type='backSmall',
 111                position=(75 + x_inset, self._height - 87),
 112                size=(60, 55),
 113                label=bui.charstr(bui.SpecialChar.BACK),
 114            )
 115            bui.containerwidget(
 116                edit=self._root_widget,
 117                cancel_button=self._back_button,
 118                selected_child=self._back_button,
 119            )
 120
 121        self._title_text = bui.textwidget(
 122            parent=self._root_widget,
 123            position=(
 124                self._width * 0.5,
 125                self._height - (66 if uiscale is bui.UIScale.SMALL else 56),
 126            ),
 127            size=(0, 0),
 128            text=bui.Lstr(
 129                resource='league.leagueRankText',
 130                fallback_resource='coopSelectWindow.powerRankingText',
 131            ),
 132            h_align='center',
 133            color=bui.app.ui_v1.title_color,
 134            scale=1.4,
 135            maxwidth=600,
 136            v_align='center',
 137        )
 138
 139        self._scroll_width = self._width - (130 + 2 * x_inset)
 140        self._scroll_height = self._height - 160
 141        self._scrollwidget = bui.scrollwidget(
 142            parent=self._root_widget,
 143            highlight=False,
 144            position=(65 + x_inset, 70),
 145            size=(self._scroll_width, self._scroll_height),
 146            center_small_content=True,
 147            border_opacity=0.4,
 148        )
 149        bui.widget(edit=self._scrollwidget, autoselect=True)
 150        bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
 151
 152        self._last_power_ranking_query_time: float | None = None
 153        self._doing_power_ranking_query = False
 154
 155        self._subcontainer: bui.Widget | None = None
 156        self._subcontainerwidth = max(800, self._scroll_width)
 157        self._subcontainerheight = 483
 158        self._power_ranking_score_widgets: list[bui.Widget] = []
 159
 160        self._season_popup_menu: PopupMenu | None = None
 161        self._requested_season: str | None = None
 162        self._season: str | None = None
 163
 164        # take note of our account state; we'll refresh later if this changes
 165        self._account_state = plus.get_v1_account_state()
 166
 167        self._refresh()
 168        self._restore_state()
 169
 170        # if we've got cached power-ranking data already, display it
 171        assert bui.app.classic is not None
 172        info = bui.app.classic.accounts.get_cached_league_rank_data()
 173        if info is not None:
 174            self._update_for_league_rank_data(info)
 175
 176        self._update_timer = bui.AppTimer(
 177            1.0, bui.WeakCall(self._update), repeat=True
 178        )
 179        self._update(show=info is None)
 180
 181    @override
 182    def get_main_window_state(self) -> bui.MainWindowState:
 183        # Support recreating our window for back/refresh purposes.
 184        cls = type(self)
 185        return bui.BasicMainWindowState(
 186            create_call=lambda transition, origin_widget: cls(
 187                transition=transition, origin_widget=origin_widget
 188            )
 189        )
 190
 191    @override
 192    def on_main_window_close(self) -> None:
 193        self._save_state()
 194
 195    def _on_achievements_press(self) -> None:
 196        from bauiv1lib.achievements import AchievementsWindow
 197
 198        # only allow this for all-time or the current season
 199        # (we currently don't keep specific achievement data for old seasons)
 200        if self._season == 'a' or self._is_current_season:
 201            prab = self._power_ranking_achievements_button
 202            assert prab is not None
 203            self.main_window_replace(AchievementsWindow(origin_widget=prab))
 204        else:
 205            bui.screenmessage(
 206                bui.Lstr(
 207                    resource='achievementsUnavailableForOldSeasonsText',
 208                    fallback_resource='unavailableText',
 209                ),
 210                color=(1, 0, 0),
 211            )
 212            bui.getsound('error').play()
 213
 214    def _on_activity_mult_press(self) -> None:
 215        from bauiv1lib import confirm
 216
 217        plus = bui.app.plus
 218        assert plus is not None
 219
 220        txt = bui.Lstr(
 221            resource=(
 222                'coopSelectWindow.activenessAllTimeInfoText'
 223                if self._season == 'a'
 224                else 'coopSelectWindow.activenessInfoText'
 225            ),
 226            subs=[
 227                (
 228                    '${MAX}',
 229                    str(
 230                        plus.get_v1_account_misc_read_val('activenessMax', 1.0)
 231                    ),
 232                )
 233            ],
 234        )
 235        confirm.ConfirmWindow(
 236            txt,
 237            cancel_button=False,
 238            width=460,
 239            height=150,
 240            origin_widget=self._activity_mult_button,
 241        )
 242
 243    def _on_pro_mult_press(self) -> None:
 244        from bauiv1lib import confirm
 245
 246        plus = bui.app.plus
 247        assert plus is not None
 248
 249        txt = bui.Lstr(
 250            resource='coopSelectWindow.proMultInfoText',
 251            subs=[
 252                (
 253                    '${PERCENT}',
 254                    str(
 255                        plus.get_v1_account_misc_read_val(
 256                            'proPowerRankingBoost', 10
 257                        )
 258                    ),
 259                ),
 260                (
 261                    '${PRO}',
 262                    bui.Lstr(
 263                        resource='store.bombSquadProNameText',
 264                        subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
 265                    ),
 266                ),
 267            ],
 268        )
 269        confirm.ConfirmWindow(
 270            txt,
 271            cancel_button=False,
 272            width=460,
 273            height=130,
 274            origin_widget=self._pro_mult_button,
 275        )
 276
 277    def _on_trophies_press(self) -> None:
 278        from bauiv1lib.trophies import TrophiesWindow
 279
 280        info = self._league_rank_data
 281        if info is not None:
 282            prtb = self._power_ranking_trophies_button
 283            assert prtb is not None
 284            TrophiesWindow(
 285                position=prtb.get_screen_space_center(),
 286                data=info,
 287            )
 288        else:
 289            bui.getsound('error').play()
 290
 291    def _on_power_ranking_query_response(
 292        self, data: dict[str, Any] | None
 293    ) -> None:
 294        self._doing_power_ranking_query = False
 295
 296        # Important: *only* cache this if we requested the current season.
 297        if data is not None and data.get('s', None) is None:
 298            assert bui.app.classic is not None
 299            bui.app.classic.accounts.cache_league_rank_data(data)
 300
 301        # Always store a copy locally though (even for other seasons).
 302        self._league_rank_data = copy.deepcopy(data)
 303        self._update_for_league_rank_data(data)
 304
 305    def _restore_state(self) -> None:
 306        pass
 307
 308    def _update(self, show: bool = False) -> None:
 309        plus = bui.app.plus
 310        assert plus is not None
 311
 312        cur_time = bui.apptime()
 313
 314        # If our account state has changed, refresh our UI.
 315        account_state = plus.get_v1_account_state()
 316        if account_state != self._account_state:
 317            self._account_state = account_state
 318            self._save_state()
 319            self._refresh()
 320
 321            # And power ranking too.
 322            if not self._doing_power_ranking_query:
 323                self._last_power_ranking_query_time = None
 324
 325        # Send off a new power-ranking query if its been long enough or our
 326        # requested season has changed or whatnot.
 327        if not self._doing_power_ranking_query and (
 328            self._last_power_ranking_query_time is None
 329            or cur_time - self._last_power_ranking_query_time > 30.0
 330        ):
 331            try:
 332                if show:
 333                    bui.textwidget(edit=self._league_title_text, text='')
 334                    bui.textwidget(edit=self._league_text, text='')
 335                    bui.textwidget(edit=self._league_number_text, text='')
 336                    bui.textwidget(edit=self._your_power_ranking_text, text='')
 337                    bui.spinnerwidget(edit=self._loading_spinner, visible=True)
 338                    bui.textwidget(edit=self._to_ranked_text, text='')
 339                    bui.textwidget(edit=self._power_ranking_rank_text, text='')
 340                    bui.textwidget(edit=self._season_ends_text, text='')
 341                    bui.textwidget(edit=self._trophy_counts_reset_text, text='')
 342            except Exception:
 343                logging.exception('Error showing updated rank info.')
 344
 345            self._last_power_ranking_query_time = cur_time
 346            self._doing_power_ranking_query = True
 347            plus.power_ranking_query(
 348                season=self._requested_season,
 349                callback=bui.WeakCall(self._on_power_ranking_query_response),
 350            )
 351
 352    def _refresh(self) -> None:
 353        # pylint: disable=too-many-statements
 354
 355        plus = bui.app.plus
 356        assert plus is not None
 357
 358        # (re)create the sub-container if need be..
 359        if self._subcontainer is not None:
 360            self._subcontainer.delete()
 361        self._subcontainer = bui.containerwidget(
 362            parent=self._scrollwidget,
 363            size=(self._subcontainerwidth, self._subcontainerheight),
 364            background=False,
 365        )
 366
 367        w_parent = self._subcontainer
 368        v = self._subcontainerheight - 20
 369
 370        v -= 0
 371
 372        h2 = 80
 373        v2 = v - 60
 374        worth_color = (0.6, 0.6, 0.65)
 375        tally_color = (0.5, 0.6, 0.8)
 376        spc = 43
 377
 378        h_offs_tally = 150
 379        tally_maxwidth = 120
 380        v2 -= 70
 381
 382        bui.textwidget(
 383            parent=w_parent,
 384            position=(self._xoffs + h2 - 60, v2 + 106),
 385            size=(0, 0),
 386            flatness=1.0,
 387            shadow=0.0,
 388            text=bui.Lstr(resource='coopSelectWindow.pointsText'),
 389            h_align='left',
 390            v_align='center',
 391            scale=0.8,
 392            color=(1, 1, 1, 0.3),
 393            maxwidth=200,
 394        )
 395
 396        self._power_ranking_achievements_button = bui.buttonwidget(
 397            parent=w_parent,
 398            position=(self._xoffs + h2 - 60, v2 + 10),
 399            size=(200, 80),
 400            icon=bui.gettexture('achievementsIcon'),
 401            autoselect=True,
 402            on_activate_call=bui.WeakCall(self._on_achievements_press),
 403            up_widget=self._back_button,
 404            left_widget=self._back_button,
 405            color=(0.5, 0.5, 0.6),
 406            textcolor=(0.7, 0.7, 0.8),
 407            label='',
 408        )
 409
 410        self._power_ranking_achievement_total_text = bui.textwidget(
 411            parent=w_parent,
 412            position=(self._xoffs + h2 + h_offs_tally, v2 + 45),
 413            size=(0, 0),
 414            flatness=1.0,
 415            shadow=0.0,
 416            text='-',
 417            h_align='left',
 418            v_align='center',
 419            scale=0.8,
 420            color=tally_color,
 421            maxwidth=tally_maxwidth,
 422        )
 423
 424        v2 -= 80
 425
 426        self._power_ranking_trophies_button = bui.buttonwidget(
 427            parent=w_parent,
 428            position=(self._xoffs + h2 - 60, v2 + 10),
 429            size=(200, 80),
 430            icon=bui.gettexture('medalSilver'),
 431            autoselect=True,
 432            on_activate_call=bui.WeakCall(self._on_trophies_press),
 433            left_widget=self._back_button,
 434            color=(0.5, 0.5, 0.6),
 435            textcolor=(0.7, 0.7, 0.8),
 436            label='',
 437        )
 438        self._power_ranking_trophies_total_text = bui.textwidget(
 439            parent=w_parent,
 440            position=(self._xoffs + h2 + h_offs_tally, v2 + 45),
 441            size=(0, 0),
 442            flatness=1.0,
 443            shadow=0.0,
 444            text='-',
 445            h_align='left',
 446            v_align='center',
 447            scale=0.8,
 448            color=tally_color,
 449            maxwidth=tally_maxwidth,
 450        )
 451
 452        v2 -= 100
 453
 454        bui.textwidget(
 455            parent=w_parent,
 456            position=(self._xoffs + h2 - 60, v2 + 86),
 457            size=(0, 0),
 458            flatness=1.0,
 459            shadow=0.0,
 460            text=bui.Lstr(resource='coopSelectWindow.multipliersText'),
 461            h_align='left',
 462            v_align='center',
 463            scale=0.8,
 464            color=(1, 1, 1, 0.3),
 465            maxwidth=200,
 466        )
 467
 468        self._activity_mult_button: bui.Widget | None
 469        if plus.get_v1_account_misc_read_val('act', False):
 470            self._activity_mult_button = bui.buttonwidget(
 471                parent=w_parent,
 472                position=(self._xoffs + h2 - 60, v2 + 10),
 473                size=(200, 60),
 474                icon=bui.gettexture('heart'),
 475                icon_color=(0.5, 0, 0.5),
 476                label=bui.Lstr(resource='coopSelectWindow.activityText'),
 477                autoselect=True,
 478                on_activate_call=bui.WeakCall(self._on_activity_mult_press),
 479                left_widget=self._back_button,
 480                color=(0.5, 0.5, 0.6),
 481                textcolor=(0.7, 0.7, 0.8),
 482            )
 483
 484            self._activity_mult_text = bui.textwidget(
 485                parent=w_parent,
 486                position=(self._xoffs + h2 + h_offs_tally, v2 + 40),
 487                size=(0, 0),
 488                flatness=1.0,
 489                shadow=0.0,
 490                text='-',
 491                h_align='left',
 492                v_align='center',
 493                scale=0.8,
 494                color=tally_color,
 495                maxwidth=tally_maxwidth,
 496            )
 497            v2 -= 65
 498        else:
 499            self._activity_mult_button = None
 500
 501        self._pro_mult_button = bui.buttonwidget(
 502            parent=w_parent,
 503            position=(self._xoffs + h2 - 60, v2 + 10),
 504            size=(200, 60),
 505            icon=bui.gettexture('logo'),
 506            icon_color=(0.3, 0, 0.3),
 507            label=bui.Lstr(
 508                resource='store.bombSquadProNameText',
 509                subs=[('${APP_NAME}', bui.Lstr(resource='titleText'))],
 510            ),
 511            autoselect=True,
 512            on_activate_call=bui.WeakCall(self._on_pro_mult_press),
 513            left_widget=self._back_button,
 514            color=(0.5, 0.5, 0.6),
 515            textcolor=(0.7, 0.7, 0.8),
 516        )
 517
 518        self._pro_mult_text = bui.textwidget(
 519            parent=w_parent,
 520            position=(self._xoffs + h2 + h_offs_tally, v2 + 40),
 521            size=(0, 0),
 522            flatness=1.0,
 523            shadow=0.0,
 524            text='-',
 525            h_align='left',
 526            v_align='center',
 527            scale=0.8,
 528            color=tally_color,
 529            maxwidth=tally_maxwidth,
 530        )
 531        v2 -= 30
 532
 533        v2 -= spc
 534        bui.textwidget(
 535            parent=w_parent,
 536            position=(self._xoffs + h2 + h_offs_tally - 10 - 40, v2 + 35),
 537            size=(0, 0),
 538            flatness=1.0,
 539            shadow=0.0,
 540            text=bui.Lstr(resource='finalScoreText'),
 541            h_align='right',
 542            v_align='center',
 543            scale=0.9,
 544            color=worth_color,
 545            maxwidth=150,
 546        )
 547        self._power_ranking_total_text = bui.textwidget(
 548            parent=w_parent,
 549            position=(self._xoffs + h2 + h_offs_tally - 40, v2 + 35),
 550            size=(0, 0),
 551            flatness=1.0,
 552            shadow=0.0,
 553            text='-',
 554            h_align='left',
 555            v_align='center',
 556            scale=0.9,
 557            color=tally_color,
 558            maxwidth=tally_maxwidth,
 559        )
 560
 561        self._season_show_text = bui.textwidget(
 562            parent=w_parent,
 563            position=(self._xoffs + 390 - 15, v - 20),
 564            size=(0, 0),
 565            color=(0.6, 0.6, 0.7),
 566            maxwidth=200,
 567            text=bui.Lstr(resource='showText'),
 568            h_align='right',
 569            v_align='center',
 570            scale=0.8,
 571            shadow=0,
 572            flatness=1.0,
 573        )
 574
 575        self._league_title_text = bui.textwidget(
 576            parent=w_parent,
 577            position=(self._xoffs + 470, v - 97),
 578            size=(0, 0),
 579            color=(0.6, 0.6, 0.7),
 580            maxwidth=230,
 581            text='',
 582            h_align='center',
 583            v_align='center',
 584            scale=0.9,
 585            shadow=0,
 586            flatness=1.0,
 587        )
 588
 589        self._league_text_scale = 1.8
 590        self._league_text_maxwidth = 210
 591        self._league_text = bui.textwidget(
 592            parent=w_parent,
 593            position=(self._xoffs + 470, v - 140),
 594            size=(0, 0),
 595            color=(1, 1, 1),
 596            maxwidth=self._league_text_maxwidth,
 597            text='-',
 598            h_align='center',
 599            v_align='center',
 600            scale=self._league_text_scale,
 601            shadow=1.0,
 602            flatness=1.0,
 603        )
 604        self._league_number_base_pos = (470, v - 140)
 605        self._league_number_text = bui.textwidget(
 606            parent=w_parent,
 607            position=(self._xoffs + 470, v - 140),
 608            size=(0, 0),
 609            color=(1, 1, 1),
 610            maxwidth=100,
 611            text='',
 612            h_align='left',
 613            v_align='center',
 614            scale=0.8,
 615            shadow=1.0,
 616            flatness=1.0,
 617        )
 618
 619        self._loading_spinner = bui.spinnerwidget(
 620            parent=w_parent,
 621            position=(
 622                self._subcontainerwidth * 0.5,
 623                self._subcontainerheight * 0.5,
 624            ),
 625            size=64,
 626        )
 627        self._your_power_ranking_text = bui.textwidget(
 628            parent=w_parent,
 629            position=(self._xoffs + 470, v - 142 - 70),
 630            size=(0, 0),
 631            color=(0.6, 0.6, 0.7),
 632            maxwidth=230,
 633            text='',
 634            h_align='center',
 635            v_align='center',
 636            scale=0.9,
 637            shadow=0,
 638            flatness=1.0,
 639        )
 640
 641        self._to_ranked_text = bui.textwidget(
 642            parent=w_parent,
 643            position=(self._xoffs + 470, v - 250 - 70),
 644            size=(0, 0),
 645            color=(0.6, 0.6, 0.7),
 646            maxwidth=230,
 647            text='',
 648            h_align='center',
 649            v_align='center',
 650            scale=0.8,
 651            shadow=0,
 652            flatness=1.0,
 653        )
 654
 655        self._power_ranking_rank_text = bui.textwidget(
 656            parent=w_parent,
 657            position=(self._xoffs + 473, v - 210 - 70),
 658            size=(0, 0),
 659            big=False,
 660            text='-',
 661            h_align='center',
 662            v_align='center',
 663            scale=1.0,
 664        )
 665
 666        self._season_ends_text = bui.textwidget(
 667            parent=w_parent,
 668            position=(self._xoffs + 470, v - 380),
 669            size=(0, 0),
 670            color=(0.6, 0.6, 0.6),
 671            maxwidth=230,
 672            text='',
 673            h_align='center',
 674            v_align='center',
 675            scale=0.9,
 676            shadow=0,
 677            flatness=1.0,
 678        )
 679        self._trophy_counts_reset_text = bui.textwidget(
 680            parent=w_parent,
 681            position=(self._xoffs + 470, v - 410),
 682            size=(0, 0),
 683            color=(0.5, 0.5, 0.5),
 684            maxwidth=230,
 685            text='Trophy counts will reset next season.',
 686            h_align='center',
 687            v_align='center',
 688            scale=0.8,
 689            shadow=0,
 690            flatness=1.0,
 691        )
 692
 693        self._power_ranking_score_widgets = []
 694
 695        self._power_ranking_score_v = v - 56
 696
 697        h = 707
 698        v -= 451
 699
 700        self._see_more_button = bui.buttonwidget(
 701            parent=w_parent,
 702            label=self._rdict.seeMoreText,
 703            position=(self._xoffs + h, v),
 704            color=(0.5, 0.5, 0.6),
 705            textcolor=(0.7, 0.7, 0.8),
 706            size=(230, 60),
 707            autoselect=True,
 708            on_activate_call=bui.WeakCall(self._on_more_press),
 709        )
 710
 711    def _on_more_press(self) -> None:
 712        plus = bui.app.plus
 713        assert plus is not None
 714
 715        our_login_id = plus.get_v1_account_public_login_id()
 716        # our_login_id = _bs.get_account_misc_read_val_2(
 717        #     'resolvedAccountID', None)
 718        if not self._can_do_more_button or our_login_id is None:
 719            bui.getsound('error').play()
 720            bui.screenmessage(
 721                bui.Lstr(resource='unavailableText'), color=(1, 0, 0)
 722            )
 723            return
 724        if self._season is None:
 725            season_str = ''
 726        else:
 727            season_str = '&season=' + (
 728                'all_time' if self._season == 'a' else self._season
 729            )
 730        if self._league_url_arg != '':
 731            league_str = '&league=' + self._league_url_arg
 732        else:
 733            league_str = ''
 734        bui.open_url(
 735            plus.get_master_server_address()
 736            + '/highscores?list=powerRankings&v=2'
 737            + league_str
 738            + season_str
 739            + '&player='
 740            + our_login_id
 741        )
 742
 743    def _update_for_league_rank_data(self, data: dict[str, Any] | None) -> None:
 744        # pylint: disable=too-many-statements
 745        # pylint: disable=too-many-branches
 746        # pylint: disable=too-many-locals
 747        if not self._root_widget:
 748            return
 749        plus = bui.app.plus
 750        uiscale = bui.app.ui_v1.uiscale
 751        assert plus is not None
 752        assert bui.app.classic is not None
 753        accounts = bui.app.classic.accounts
 754        in_top = data is not None and data['rank'] is not None
 755        eq_text = self._rdict.powerRankingPointsEqualsText
 756        pts_txt = self._rdict.powerRankingPointsText
 757        num_text = bui.Lstr(resource='numberText').evaluate()
 758        do_percent = False
 759        finished_season_unranked = False
 760        self._can_do_more_button = True
 761        extra_text = ''
 762        if plus.get_v1_account_state() != 'signed_in':
 763            status_text = (
 764                '(' + bui.Lstr(resource='notSignedInText').evaluate() + ')'
 765            )
 766        elif in_top:
 767            assert data is not None
 768            status_text = num_text.replace('${NUMBER}', str(data['rank']))
 769        elif data is not None:
 770            try:
 771                # handle old seasons where we didn't wind up ranked
 772                # at the end..
 773                if not data['scores']:
 774                    status_text = (
 775                        self._rdict.powerRankingFinishedSeasonUnrankedText
 776                    )
 777                    extra_text = ''
 778                    finished_season_unranked = True
 779                    self._can_do_more_button = False
 780                else:
 781                    our_points = accounts.get_league_rank_points(data)
 782                    progress = float(our_points) / max(1, data['scores'][-1][1])
 783                    status_text = str(int(progress * 100.0)) + '%'
 784                    extra_text = (
 785                        '\n'
 786                        + self._rdict.powerRankingPointsToRankedText.replace(
 787                            '${CURRENT}', str(our_points)
 788                        ).replace('${REMAINING}', str(data['scores'][-1][1]))
 789                    )
 790                    do_percent = True
 791            except Exception:
 792                logging.exception('Error updating power ranking.')
 793                status_text = self._rdict.powerRankingNotInTopText.replace(
 794                    '${NUMBER}', str(data['listSize'])
 795                )
 796                extra_text = ''
 797        else:
 798            status_text = '-'
 799
 800        self._season = data['s'] if data is not None else None
 801
 802        v = self._subcontainerheight - 20
 803        popup_was_selected = False
 804        if self._season_popup_menu is not None:
 805            btn = self._season_popup_menu.get_button()
 806            assert self._subcontainer
 807            if self._subcontainer.get_selected_child() == btn:
 808                popup_was_selected = True
 809            btn.delete()
 810        season_choices = []
 811        season_choices_display = []
 812        did_first = False
 813        self._is_current_season = False
 814        if data is not None:
 815            # build our list of seasons we have available
 816            for ssn in data['sl']:
 817                season_choices.append(ssn)
 818                if ssn != 'a' and not did_first:
 819                    season_choices_display.append(
 820                        bui.Lstr(
 821                            resource='league.currentSeasonText',
 822                            subs=[('${NUMBER}', ssn)],
 823                        )
 824                    )
 825                    did_first = True
 826                    # if we either did not specify a season or specified the
 827                    # first, we're looking at the current..
 828                    if self._season in [ssn, None]:
 829                        self._is_current_season = True
 830                elif ssn == 'a':
 831                    season_choices_display.append(
 832                        bui.Lstr(resource='league.allTimeText')
 833                    )
 834                else:
 835                    season_choices_display.append(
 836                        bui.Lstr(
 837                            resource='league.seasonText',
 838                            subs=[('${NUMBER}', ssn)],
 839                        )
 840                    )
 841            assert self._subcontainer
 842            self._season_popup_menu = PopupMenu(
 843                parent=self._subcontainer,
 844                position=(self._xoffs + 390, v - 45),
 845                width=150,
 846                button_size=(200, 50),
 847                choices=season_choices,
 848                on_value_change_call=bui.WeakCall(self._on_season_change),
 849                choices_display=season_choices_display,
 850                current_choice=self._season,
 851            )
 852            if popup_was_selected:
 853                bui.containerwidget(
 854                    edit=self._subcontainer,
 855                    selected_child=self._season_popup_menu.get_button(),
 856                )
 857            bui.widget(edit=self._see_more_button, show_buffer_bottom=100)
 858            bui.widget(
 859                edit=self._season_popup_menu.get_button(),
 860                up_widget=self._back_button,
 861            )
 862            if uiscale is not bui.UIScale.SMALL:
 863                bui.widget(
 864                    edit=self._back_button,
 865                    down_widget=self._power_ranking_achievements_button,
 866                    right_widget=self._season_popup_menu.get_button(),
 867                )
 868
 869        bui.textwidget(
 870            edit=self._league_title_text,
 871            text=(
 872                ''
 873                if self._season == 'a'
 874                else bui.Lstr(resource='league.leagueText')
 875            ),
 876        )
 877
 878        if data is None:
 879            lname = ''
 880            lnum = ''
 881            lcolor = (1, 1, 1)
 882            self._league_url_arg = ''
 883        elif self._season == 'a':
 884            lname = bui.Lstr(resource='league.allTimeText').evaluate()
 885            lnum = ''
 886            lcolor = (1, 1, 1)
 887            self._league_url_arg = ''
 888        else:
 889            lnum = ('[' + str(data['l']['i']) + ']') if data['l']['i2'] else ''
 890            lname = bui.Lstr(
 891                translate=('leagueNames', data['l']['n'])
 892            ).evaluate()
 893            lcolor = data['l']['c']
 894            self._league_url_arg = (
 895                data['l']['n'] + '_' + str(data['l']['i'])
 896            ).lower()
 897
 898        to_end_string: bui.Lstr | str
 899        if data is None or self._season == 'a' or data['se'] is None:
 900            to_end_string = ''
 901            show_season_end = False
 902        else:
 903            show_season_end = True
 904            days_to_end = data['se'][0]
 905            minutes_to_end = data['se'][1]
 906            if days_to_end > 0:
 907                to_end_string = bui.Lstr(
 908                    resource='league.seasonEndsDaysText',
 909                    subs=[('${NUMBER}', str(days_to_end))],
 910                )
 911            elif days_to_end == 0 and minutes_to_end >= 60:
 912                to_end_string = bui.Lstr(
 913                    resource='league.seasonEndsHoursText',
 914                    subs=[('${NUMBER}', str(minutes_to_end // 60))],
 915                )
 916            elif days_to_end == 0 and minutes_to_end >= 0:
 917                to_end_string = bui.Lstr(
 918                    resource='league.seasonEndsMinutesText',
 919                    subs=[('${NUMBER}', str(minutes_to_end))],
 920                )
 921            else:
 922                to_end_string = bui.Lstr(
 923                    resource='league.seasonEndedDaysAgoText',
 924                    subs=[('${NUMBER}', str(-(days_to_end + 1)))],
 925                )
 926
 927        bui.textwidget(edit=self._season_ends_text, text=to_end_string)
 928        bui.textwidget(
 929            edit=self._trophy_counts_reset_text,
 930            text=(
 931                bui.Lstr(resource='league.trophyCountsResetText')
 932                if self._is_current_season and show_season_end
 933                else ''
 934            ),
 935        )
 936
 937        bui.textwidget(edit=self._league_text, text=lname, color=lcolor)
 938        l_text_width = min(
 939            self._league_text_maxwidth,
 940            bui.get_string_width(lname, suppress_warning=True)
 941            * self._league_text_scale,
 942        )
 943        bui.textwidget(
 944            edit=self._league_number_text,
 945            text=lnum,
 946            color=lcolor,
 947            position=(
 948                self._xoffs
 949                + self._league_number_base_pos[0]
 950                + l_text_width * 0.5
 951                + 8,
 952                self._league_number_base_pos[1] + 10,
 953            ),
 954        )
 955        bui.textwidget(
 956            edit=self._to_ranked_text,
 957            text=(
 958                bui.Lstr(resource='coopSelectWindow.toRankedText').evaluate()
 959                + ''
 960                + extra_text
 961                if do_percent
 962                else ''
 963            ),
 964        )
 965
 966        bui.textwidget(
 967            edit=self._your_power_ranking_text,
 968            text=(
 969                bui.Lstr(
 970                    resource='rankText',
 971                    fallback_resource='coopSelectWindow.yourPowerRankingText',
 972                )
 973                if (not do_percent)
 974                else ''
 975            ),
 976        )
 977        bui.spinnerwidget(edit=self._loading_spinner, visible=False)
 978
 979        bui.textwidget(
 980            edit=self._power_ranking_rank_text,
 981            position=(self._xoffs + 473, v - 70 - (170 if do_percent else 220)),
 982            text=status_text,
 983            big=(in_top or do_percent),
 984            scale=(
 985                3.0
 986                if (in_top or do_percent)
 987                else 0.7 if finished_season_unranked else 1.0
 988            ),
 989        )
 990
 991        if self._activity_mult_button is not None:
 992            if data is None or data['act'] is None:
 993                bui.buttonwidget(
 994                    edit=self._activity_mult_button,
 995                    textcolor=(0.7, 0.7, 0.8, 0.5),
 996                    icon_color=(0.5, 0, 0.5, 0.3),
 997                )
 998                bui.textwidget(edit=self._activity_mult_text, text='     -')
 999            else:
1000                bui.buttonwidget(
1001                    edit=self._activity_mult_button,
1002                    textcolor=(0.7, 0.7, 0.8, 1.0),
1003                    icon_color=(0.5, 0, 0.5, 1.0),
1004                )
1005                # pylint: disable=consider-using-f-string
1006                bui.textwidget(
1007                    edit=self._activity_mult_text,
1008                    text='x ' + ('%.2f' % data['act']),
1009                )
1010
1011        have_pro = False if data is None else data['p']
1012        pro_mult = (
1013            1.0
1014            + float(
1015                plus.get_v1_account_misc_read_val('proPowerRankingBoost', 0.0)
1016            )
1017            * 0.01
1018        )
1019        # pylint: disable=consider-using-f-string
1020        bui.textwidget(
1021            edit=self._pro_mult_text,
1022            text=(
1023                '     -'
1024                if (data is None or not have_pro)
1025                else 'x ' + ('%.2f' % pro_mult)
1026            ),
1027        )
1028        bui.buttonwidget(
1029            edit=self._pro_mult_button,
1030            textcolor=(0.7, 0.7, 0.8, (1.0 if have_pro else 0.5)),
1031            icon_color=(0.5, 0, 0.5) if have_pro else (0.5, 0, 0.5, 0.2),
1032        )
1033        bui.buttonwidget(
1034            edit=self._power_ranking_achievements_button,
1035            label=('' if data is None else str(data['a']) + ' ')
1036            + bui.Lstr(resource='achievementsText').evaluate(),
1037        )
1038
1039        # for the achievement value, use the number they gave us for
1040        # non-current seasons; otherwise calc our own
1041        total_ach_value = 0
1042        for ach in bui.app.classic.ach.achievements:
1043            if ach.complete:
1044                total_ach_value += ach.power_ranking_value
1045        if self._season != 'a' and not self._is_current_season:
1046            if data is not None and 'at' in data:
1047                total_ach_value = data['at']
1048
1049        bui.textwidget(
1050            edit=self._power_ranking_achievement_total_text,
1051            text=(
1052                '-'
1053                if data is None
1054                else ('+ ' + pts_txt.replace('${NUMBER}', str(total_ach_value)))
1055            ),
1056        )
1057
1058        total_trophies_count = accounts.get_league_rank_points(
1059            data, 'trophyCount'
1060        )
1061        total_trophies_value = accounts.get_league_rank_points(data, 'trophies')
1062        bui.buttonwidget(
1063            edit=self._power_ranking_trophies_button,
1064            label=('' if data is None else str(total_trophies_count) + ' ')
1065            + bui.Lstr(resource='trophiesText').evaluate(),
1066        )
1067        bui.textwidget(
1068            edit=self._power_ranking_trophies_total_text,
1069            text=(
1070                '-'
1071                if data is None
1072                else (
1073                    '+ '
1074                    + pts_txt.replace('${NUMBER}', str(total_trophies_value))
1075                )
1076            ),
1077        )
1078
1079        bui.textwidget(
1080            edit=self._power_ranking_total_text,
1081            text=(
1082                '-'
1083                if data is None
1084                else eq_text.replace(
1085                    '${NUMBER}', str(accounts.get_league_rank_points(data))
1086                )
1087            ),
1088        )
1089        for widget in self._power_ranking_score_widgets:
1090            widget.delete()
1091        self._power_ranking_score_widgets = []
1092
1093        scores = data['scores'] if data is not None else []
1094        tally_color = (0.5, 0.6, 0.8)
1095        w_parent = self._subcontainer
1096        v2 = self._power_ranking_score_v
1097
1098        for score in scores:
1099            h2 = 680
1100            is_us = score[3]
1101            self._power_ranking_score_widgets.append(
1102                bui.textwidget(
1103                    parent=w_parent,
1104                    position=(self._xoffs + h2 - 20, v2),
1105                    size=(0, 0),
1106                    color=(1, 1, 1) if is_us else (0.6, 0.6, 0.7),
1107                    maxwidth=40,
1108                    flatness=1.0,
1109                    shadow=0.0,
1110                    text=num_text.replace('${NUMBER}', str(score[0])),
1111                    h_align='right',
1112                    v_align='center',
1113                    scale=0.5,
1114                )
1115            )
1116            self._power_ranking_score_widgets.append(
1117                bui.textwidget(
1118                    parent=w_parent,
1119                    position=(self._xoffs + h2 + 20, v2),
1120                    size=(0, 0),
1121                    color=(1, 1, 1) if is_us else tally_color,
1122                    maxwidth=60,
1123                    text=str(score[1]),
1124                    flatness=1.0,
1125                    shadow=0.0,
1126                    h_align='center',
1127                    v_align='center',
1128                    scale=0.7,
1129                )
1130            )
1131            txt = bui.textwidget(
1132                parent=w_parent,
1133                position=(self._xoffs + h2 + 60, v2 - (28 * 0.5) / 0.9),
1134                size=(210 / 0.9, 28),
1135                color=(1, 1, 1) if is_us else (0.6, 0.6, 0.6),
1136                maxwidth=210,
1137                flatness=1.0,
1138                shadow=0.0,
1139                autoselect=True,
1140                selectable=True,
1141                click_activate=True,
1142                text=score[2],
1143                h_align='left',
1144                v_align='center',
1145                scale=0.9,
1146            )
1147            self._power_ranking_score_widgets.append(txt)
1148            bui.textwidget(
1149                edit=txt,
1150                on_activate_call=bui.Call(
1151                    self._show_account_info, score[4], txt
1152                ),
1153            )
1154            assert self._season_popup_menu is not None
1155            bui.widget(
1156                edit=txt, left_widget=self._season_popup_menu.get_button()
1157            )
1158            v2 -= 28
1159
1160    def _show_account_info(
1161        self, account_id: str, textwidget: bui.Widget
1162    ) -> None:
1163        from bauiv1lib.account.viewer import AccountViewerWindow
1164
1165        bui.getsound('swish').play()
1166        AccountViewerWindow(
1167            account_id=account_id, position=textwidget.get_screen_space_center()
1168        )
1169
1170    def _on_season_change(self, value: str) -> None:
1171        self._requested_season = value
1172        self._last_power_ranking_query_time = None  # make sure we update asap
1173        self._update(show=True)
1174
1175    def _save_state(self) -> None:
1176        pass

Window for showing league rank.

LeagueRankWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
 23    def __init__(
 24        self,
 25        transition: str | None = 'in_right',
 26        origin_widget: bui.Widget | None = None,
 27    ):
 28        # pylint: disable=too-many-statements
 29        plus = bui.app.plus
 30        assert plus is not None
 31
 32        bui.set_analytics_screen('League Rank Window')
 33
 34        self._league_rank_data: dict[str, Any] | None = None
 35
 36        self._power_ranking_achievements_button: bui.Widget | None = None
 37        self._pro_mult_button: bui.Widget | None = None
 38        self._power_ranking_trophies_button: bui.Widget | None = None
 39        self._league_title_text: bui.Widget | None = None
 40        self._league_text: bui.Widget | None = None
 41        self._league_number_text: bui.Widget | None = None
 42        self._your_power_ranking_text: bui.Widget | None = None
 43        self._loading_spinner: bui.Widget | None = None
 44        self._season_ends_text: bui.Widget | None = None
 45        self._power_ranking_rank_text: bui.Widget | None = None
 46        self._to_ranked_text: bui.Widget | None = None
 47        self._trophy_counts_reset_text: bui.Widget | None = None
 48
 49        assert bui.app.classic is not None
 50        uiscale = bui.app.ui_v1.uiscale
 51        self._width = 1490 if uiscale is bui.UIScale.SMALL else 1120
 52        x_inset = 100 if uiscale is bui.UIScale.SMALL else 0
 53        self._height = (
 54            660
 55            if uiscale is bui.UIScale.SMALL
 56            else 710 if uiscale is bui.UIScale.MEDIUM else 800
 57        )
 58        self._r = 'coopSelectWindow'
 59        self._rdict = bui.app.lang.get_resource(self._r)
 60        top_extra = 20 if uiscale is bui.UIScale.SMALL else 0
 61
 62        self._xoffs = 80.0 if uiscale is bui.UIScale.SMALL else 0
 63
 64        self._league_url_arg = ''
 65
 66        self._is_current_season = False
 67        self._can_do_more_button = True
 68
 69        super().__init__(
 70            root_widget=bui.containerwidget(
 71                size=(self._width, self._height + top_extra),
 72                stack_offset=(
 73                    (0, 0)
 74                    if uiscale is bui.UIScale.SMALL
 75                    else (0, 10) if uiscale is bui.UIScale.MEDIUM else (0, 0)
 76                ),
 77                scale=(
 78                    1.08
 79                    if uiscale is bui.UIScale.SMALL
 80                    else 0.93 if uiscale is bui.UIScale.MEDIUM else 0.8
 81                ),
 82                toolbar_visibility=(
 83                    'menu_minimal'
 84                    if uiscale is bui.UIScale.SMALL
 85                    else 'menu_full'
 86                ),
 87            ),
 88            transition=transition,
 89            origin_widget=origin_widget,
 90        )
 91
 92        if uiscale is bui.UIScale.SMALL:
 93            self._back_button = bui.get_special_widget('back_button')
 94            bui.containerwidget(
 95                edit=self._root_widget, on_cancel_call=self.main_window_back
 96            )
 97        else:
 98            self._back_button = btn = bui.buttonwidget(
 99                parent=self._root_widget,
100                position=(75 + x_inset, self._height - 87),
101                size=(120, 60),
102                scale=1.2,
103                autoselect=True,
104                label=bui.Lstr(resource='backText'),
105                button_type='back',
106                on_activate_call=self.main_window_back,
107            )
108            bui.buttonwidget(
109                edit=btn,
110                button_type='backSmall',
111                position=(75 + x_inset, self._height - 87),
112                size=(60, 55),
113                label=bui.charstr(bui.SpecialChar.BACK),
114            )
115            bui.containerwidget(
116                edit=self._root_widget,
117                cancel_button=self._back_button,
118                selected_child=self._back_button,
119            )
120
121        self._title_text = bui.textwidget(
122            parent=self._root_widget,
123            position=(
124                self._width * 0.5,
125                self._height - (66 if uiscale is bui.UIScale.SMALL else 56),
126            ),
127            size=(0, 0),
128            text=bui.Lstr(
129                resource='league.leagueRankText',
130                fallback_resource='coopSelectWindow.powerRankingText',
131            ),
132            h_align='center',
133            color=bui.app.ui_v1.title_color,
134            scale=1.4,
135            maxwidth=600,
136            v_align='center',
137        )
138
139        self._scroll_width = self._width - (130 + 2 * x_inset)
140        self._scroll_height = self._height - 160
141        self._scrollwidget = bui.scrollwidget(
142            parent=self._root_widget,
143            highlight=False,
144            position=(65 + x_inset, 70),
145            size=(self._scroll_width, self._scroll_height),
146            center_small_content=True,
147            border_opacity=0.4,
148        )
149        bui.widget(edit=self._scrollwidget, autoselect=True)
150        bui.containerwidget(edit=self._scrollwidget, claims_left_right=True)
151
152        self._last_power_ranking_query_time: float | None = None
153        self._doing_power_ranking_query = False
154
155        self._subcontainer: bui.Widget | None = None
156        self._subcontainerwidth = max(800, self._scroll_width)
157        self._subcontainerheight = 483
158        self._power_ranking_score_widgets: list[bui.Widget] = []
159
160        self._season_popup_menu: PopupMenu | None = None
161        self._requested_season: str | None = None
162        self._season: str | None = None
163
164        # take note of our account state; we'll refresh later if this changes
165        self._account_state = plus.get_v1_account_state()
166
167        self._refresh()
168        self._restore_state()
169
170        # if we've got cached power-ranking data already, display it
171        assert bui.app.classic is not None
172        info = bui.app.classic.accounts.get_cached_league_rank_data()
173        if info is not None:
174            self._update_for_league_rank_data(info)
175
176        self._update_timer = bui.AppTimer(
177            1.0, bui.WeakCall(self._update), repeat=True
178        )
179        self._update(show=info is None)

Create a MainWindow given a root widget and transition info.

Automatically handles in and out transitions on the provided widget, so there is no need to set transitions when creating it.

@override
def get_main_window_state(self) -> bauiv1.MainWindowState:
181    @override
182    def get_main_window_state(self) -> bui.MainWindowState:
183        # Support recreating our window for back/refresh purposes.
184        cls = type(self)
185        return bui.BasicMainWindowState(
186            create_call=lambda transition, origin_widget: cls(
187                transition=transition, origin_widget=origin_widget
188            )
189        )

Return a WindowState to recreate this window, if supported.

@override
def on_main_window_close(self) -> None:
191    @override
192    def on_main_window_close(self) -> None:
193        self._save_state()

Called before transitioning out a main window.

A good opportunity to save window state/etc.