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

Return a WindowState to recreate this window, if supported.

@override
def on_main_window_close(self) -> None:
189    @override
190    def on_main_window_close(self) -> None:
191        self._save_state()

Called before transitioning out a main window.

A good opportunity to save window state/etc.