bastd.ui.league.rankbutton

Provides a button showing league rank.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides a button showing league rank."""
  4
  5from __future__ import annotations
  6
  7from typing import TYPE_CHECKING
  8
  9import ba
 10import ba.internal
 11
 12if TYPE_CHECKING:
 13    from typing import Any, Callable
 14
 15
 16class LeagueRankButton:
 17    """Button showing league rank."""
 18
 19    def __init__(
 20        self,
 21        parent: ba.Widget,
 22        position: tuple[float, float],
 23        size: tuple[float, float],
 24        scale: float,
 25        on_activate_call: Callable[[], Any] | None = None,
 26        transition_delay: float | None = None,
 27        color: tuple[float, float, float] | None = None,
 28        textcolor: tuple[float, float, float] | None = None,
 29        smooth_update_delay: float | None = None,
 30    ):
 31        if on_activate_call is None:
 32            on_activate_call = ba.WeakCall(self._default_on_activate_call)
 33        self._on_activate_call = on_activate_call
 34        if smooth_update_delay is None:
 35            smooth_update_delay = 1000
 36        self._smooth_update_delay = smooth_update_delay
 37        self._size = size
 38        self._scale = scale
 39        if color is None:
 40            color = (0.5, 0.6, 0.5)
 41        if textcolor is None:
 42            textcolor = (1, 1, 1)
 43        self._color = color
 44        self._textcolor = textcolor
 45        self._header_color = (0.8, 0.8, 2.0)
 46        self._parent = parent
 47        self._position: tuple[float, float] = (0.0, 0.0)
 48
 49        self._button = ba.buttonwidget(
 50            parent=parent,
 51            size=size,
 52            label='',
 53            button_type='square',
 54            scale=scale,
 55            autoselect=True,
 56            on_activate_call=self._on_activate,
 57            transition_delay=transition_delay,
 58            color=color,
 59        )
 60
 61        self._title_text = ba.textwidget(
 62            parent=parent,
 63            size=(0, 0),
 64            draw_controller=self._button,
 65            h_align='center',
 66            v_align='center',
 67            maxwidth=size[0] * scale * 0.85,
 68            text=ba.Lstr(
 69                resource='league.leagueRankText',
 70                fallback_resource='coopSelectWindow.powerRankingText',
 71            ),
 72            color=self._header_color,
 73            flatness=1.0,
 74            shadow=1.0,
 75            scale=scale * 0.5,
 76            transition_delay=transition_delay,
 77        )
 78
 79        self._value_text = ba.textwidget(
 80            parent=parent,
 81            size=(0, 0),
 82            h_align='center',
 83            v_align='center',
 84            maxwidth=size[0] * scale * 0.85,
 85            text='-',
 86            draw_controller=self._button,
 87            big=True,
 88            scale=scale,
 89            transition_delay=transition_delay,
 90            color=textcolor,
 91        )
 92
 93        self._smooth_percent: float | None = None
 94        self._percent: int | None = None
 95        self._smooth_rank: float | None = None
 96        self._rank: int | None = None
 97        self._ticking_node: ba.Node | None = None
 98        self._smooth_increase_speed = 1.0
 99        self._league: str | None = None
100        self._improvement_text: str | None = None
101
102        self._smooth_update_timer: ba.Timer | None = None
103
104        # Take note of our account state; we'll refresh later if this changes.
105        self._account_state_num = ba.internal.get_v1_account_state_num()
106        self._last_power_ranking_query_time: float | None = None
107        self._doing_power_ranking_query = False
108        self.set_position(position)
109        self._bg_flash = False
110        self._update_timer = ba.Timer(
111            1.0,
112            ba.WeakCall(self._update),
113            timetype=ba.TimeType.REAL,
114            repeat=True,
115        )
116        self._update()
117
118        # If we've got cached power-ranking data already, apply it.
119        data = ba.app.accounts_v1.get_cached_league_rank_data()
120        if data is not None:
121            self._update_for_league_rank_data(data)
122
123    def _on_activate(self) -> None:
124        ba.internal.increment_analytics_count('League rank button press')
125        self._on_activate_call()
126
127    def __del__(self) -> None:
128        if self._ticking_node is not None:
129            self._ticking_node.delete()
130
131    def _start_smooth_update(self) -> None:
132        self._smooth_update_timer = ba.Timer(
133            0.05,
134            ba.WeakCall(self._smooth_update),
135            repeat=True,
136            timetype=ba.TimeType.REAL,
137        )
138
139    def _smooth_update(self) -> None:
140        # pylint: disable=too-many-branches
141        # pylint: disable=too-many-statements
142        try:
143            if not self._button:
144                return
145            if self._ticking_node is None:
146                with ba.Context('ui'):
147                    self._ticking_node = ba.newnode(
148                        'sound',
149                        attrs={
150                            'sound': ba.getsound('scoreIncrease'),
151                            'positional': False,
152                        },
153                    )
154            self._bg_flash = not self._bg_flash
155            color_used = (
156                (self._color[0] * 2, self._color[1] * 2, self._color[2] * 2)
157                if self._bg_flash
158                else self._color
159            )
160            textcolor_used = (1, 1, 1) if self._bg_flash else self._textcolor
161            header_color_used = (
162                (1, 1, 1) if self._bg_flash else self._header_color
163            )
164
165            if self._rank is not None:
166                assert self._smooth_rank is not None
167                self._smooth_rank -= 1.0 * self._smooth_increase_speed
168                finished = int(self._smooth_rank) <= self._rank
169            elif self._smooth_percent is not None:
170                self._smooth_percent += 1.0 * self._smooth_increase_speed
171                assert self._percent is not None
172                finished = int(self._smooth_percent) >= self._percent
173            else:
174                finished = True
175            if finished:
176                if self._rank is not None:
177                    self._smooth_rank = float(self._rank)
178                elif self._percent is not None:
179                    self._smooth_percent = float(self._percent)
180                color_used = self._color
181                textcolor_used = self._textcolor
182                self._smooth_update_timer = None
183                if self._ticking_node is not None:
184                    self._ticking_node.delete()
185                    self._ticking_node = None
186                ba.playsound(ba.getsound('cashRegister2'))
187                assert self._improvement_text is not None
188                diff_text = ba.textwidget(
189                    parent=self._parent,
190                    size=(0, 0),
191                    h_align='center',
192                    v_align='center',
193                    text='+' + self._improvement_text + '!',
194                    position=(
195                        self._position[0] + self._size[0] * 0.5 * self._scale,
196                        self._position[1] + self._size[1] * -0.2 * self._scale,
197                    ),
198                    color=(0, 1, 0),
199                    flatness=1.0,
200                    shadow=0.0,
201                    scale=self._scale * 0.7,
202                )
203
204                def safe_delete(widget: ba.Widget) -> None:
205                    if widget:
206                        widget.delete()
207
208                ba.timer(
209                    2.0,
210                    ba.Call(safe_delete, diff_text),
211                    timetype=ba.TimeType.REAL,
212                )
213            status_text: str | ba.Lstr
214            if self._rank is not None:
215                assert self._smooth_rank is not None
216                status_text = ba.Lstr(
217                    resource='numberText',
218                    subs=[('${NUMBER}', str(int(self._smooth_rank)))],
219                )
220            elif self._smooth_percent is not None:
221                status_text = str(int(self._smooth_percent)) + '%'
222            else:
223                status_text = '-'
224            ba.textwidget(
225                edit=self._value_text, text=status_text, color=textcolor_used
226            )
227            ba.textwidget(edit=self._title_text, color=header_color_used)
228            ba.buttonwidget(edit=self._button, color=color_used)
229
230        except Exception:
231            ba.print_exception('Error doing smooth update.')
232            self._smooth_update_timer = None
233
234    def _update_for_league_rank_data(self, data: dict[str, Any] | None) -> None:
235        # pylint: disable=too-many-branches
236        # pylint: disable=too-many-statements
237
238        # If our button has died, ignore.
239        if not self._button:
240            return
241
242        status_text: str | ba.Lstr
243
244        in_top = data is not None and data['rank'] is not None
245        do_percent = False
246        if data is None or ba.internal.get_v1_account_state() != 'signed_in':
247            self._percent = self._rank = None
248            status_text = '-'
249        elif in_top:
250            self._percent = None
251            self._rank = data['rank']
252            prev_league = self._league
253            self._league = data['l']
254
255            # If this is the first set, league has changed, or rank has gotten
256            # worse, snap the smooth value immediately.
257            assert self._rank is not None
258            if (
259                self._smooth_rank is None
260                or prev_league != self._league
261                or self._rank > int(self._smooth_rank)
262            ):
263                self._smooth_rank = float(self._rank)
264            status_text = ba.Lstr(
265                resource='numberText',
266                subs=[('${NUMBER}', str(int(self._smooth_rank)))],
267            )
268        else:
269            try:
270                if not data['scores'] or data['scores'][-1][1] <= 0:
271                    self._percent = self._rank = None
272                    status_text = '-'
273                else:
274                    our_points = ba.app.accounts_v1.get_league_rank_points(data)
275                    progress = float(our_points) / data['scores'][-1][1]
276                    self._percent = int(progress * 100.0)
277                    self._rank = None
278                    do_percent = True
279                    prev_league = self._league
280                    self._league = data['l']
281
282                    # If this is the first set, league has changed, or percent
283                    # has decreased, snap the smooth value immediately.
284                    if (
285                        self._smooth_percent is None
286                        or prev_league != self._league
287                        or self._percent < int(self._smooth_percent)
288                    ):
289                        self._smooth_percent = float(self._percent)
290                    status_text = str(int(self._smooth_percent)) + '%'
291
292            except Exception:
293                ba.print_exception('Error updating power ranking.')
294                self._percent = self._rank = None
295                status_text = '-'
296
297        # If we're doing a smooth update, set a timer.
298        if (
299            self._rank is not None
300            and self._smooth_rank is not None
301            and int(self._smooth_rank) != self._rank
302        ):
303            self._improvement_text = str(
304                -(int(self._rank) - int(self._smooth_rank))
305            )
306            diff = abs(self._rank - self._smooth_rank)
307            if diff > 100:
308                self._smooth_increase_speed = diff / 80.0
309            elif diff > 50:
310                self._smooth_increase_speed = diff / 70.0
311            elif diff > 25:
312                self._smooth_increase_speed = diff / 55.0
313            else:
314                self._smooth_increase_speed = diff / 40.0
315            self._smooth_increase_speed = max(0.4, self._smooth_increase_speed)
316            ba.timer(
317                self._smooth_update_delay,
318                ba.WeakCall(self._start_smooth_update),
319                timetype=ba.TimeType.REAL,
320                timeformat=ba.TimeFormat.MILLISECONDS,
321            )
322
323        if (
324            self._percent is not None
325            and self._smooth_percent is not None
326            and int(self._smooth_percent) != self._percent
327        ):
328            self._improvement_text = str(
329                (int(self._percent) - int(self._smooth_percent))
330            )
331            self._smooth_increase_speed = 0.3
332            ba.timer(
333                self._smooth_update_delay,
334                ba.WeakCall(self._start_smooth_update),
335                timetype=ba.TimeType.REAL,
336                timeformat=ba.TimeFormat.MILLISECONDS,
337            )
338
339        if do_percent:
340            ba.textwidget(
341                edit=self._title_text,
342                text=ba.Lstr(resource='coopSelectWindow.toRankedText'),
343            )
344        else:
345            try:
346                assert data is not None
347                txt = ba.Lstr(
348                    resource='league.leagueFullText',
349                    subs=[
350                        (
351                            '${NAME}',
352                            ba.Lstr(translate=('leagueNames', data['l']['n'])),
353                        ),
354                    ],
355                )
356                t_color = data['l']['c']
357            except Exception:
358                txt = ba.Lstr(
359                    resource='league.leagueRankText',
360                    fallback_resource='coopSelectWindow.powerRankingText',
361                )
362                t_color = ba.app.ui.title_color
363            ba.textwidget(edit=self._title_text, text=txt, color=t_color)
364        ba.textwidget(edit=self._value_text, text=status_text)
365
366    def _on_power_ranking_query_response(
367        self, data: dict[str, Any] | None
368    ) -> None:
369        self._doing_power_ranking_query = False
370        ba.app.accounts_v1.cache_league_rank_data(data)
371        self._update_for_league_rank_data(data)
372
373    def _update(self) -> None:
374        cur_time = ba.time(ba.TimeType.REAL)
375
376        # If our account state has changed, refresh our UI.
377        account_state_num = ba.internal.get_v1_account_state_num()
378        if account_state_num != self._account_state_num:
379            self._account_state_num = account_state_num
380
381            # And power ranking too...
382            if not self._doing_power_ranking_query:
383                self._last_power_ranking_query_time = None
384
385        # Send off a new power-ranking query if its been
386        # long enough or whatnot.
387        if not self._doing_power_ranking_query and (
388            self._last_power_ranking_query_time is None
389            or cur_time - self._last_power_ranking_query_time > 30.0
390        ):
391            self._last_power_ranking_query_time = cur_time
392            self._doing_power_ranking_query = True
393            ba.internal.power_ranking_query(
394                callback=ba.WeakCall(self._on_power_ranking_query_response)
395            )
396
397    def _default_on_activate_call(self) -> None:
398        # pylint: disable=cyclic-import
399        from bastd.ui.league.rankwindow import LeagueRankWindow
400
401        LeagueRankWindow(modal=True, origin_widget=self._button)
402
403    def set_position(self, position: tuple[float, float]) -> None:
404        """Set the button's position."""
405        self._position = position
406        if not self._button:
407            return
408        ba.buttonwidget(edit=self._button, position=self._position)
409        ba.textwidget(
410            edit=self._title_text,
411            position=(
412                self._position[0] + self._size[0] * 0.5 * self._scale,
413                self._position[1] + self._size[1] * 0.82 * self._scale,
414            ),
415        )
416        ba.textwidget(
417            edit=self._value_text,
418            position=(
419                self._position[0] + self._size[0] * 0.5 * self._scale,
420                self._position[1] + self._size[1] * 0.36 * self._scale,
421            ),
422        )
423
424    def get_button(self) -> ba.Widget:
425        """Return the underlying button ba.Widget>"""
426        return self._button
class LeagueRankButton:
 17class LeagueRankButton:
 18    """Button showing league rank."""
 19
 20    def __init__(
 21        self,
 22        parent: ba.Widget,
 23        position: tuple[float, float],
 24        size: tuple[float, float],
 25        scale: float,
 26        on_activate_call: Callable[[], Any] | None = None,
 27        transition_delay: float | None = None,
 28        color: tuple[float, float, float] | None = None,
 29        textcolor: tuple[float, float, float] | None = None,
 30        smooth_update_delay: float | None = None,
 31    ):
 32        if on_activate_call is None:
 33            on_activate_call = ba.WeakCall(self._default_on_activate_call)
 34        self._on_activate_call = on_activate_call
 35        if smooth_update_delay is None:
 36            smooth_update_delay = 1000
 37        self._smooth_update_delay = smooth_update_delay
 38        self._size = size
 39        self._scale = scale
 40        if color is None:
 41            color = (0.5, 0.6, 0.5)
 42        if textcolor is None:
 43            textcolor = (1, 1, 1)
 44        self._color = color
 45        self._textcolor = textcolor
 46        self._header_color = (0.8, 0.8, 2.0)
 47        self._parent = parent
 48        self._position: tuple[float, float] = (0.0, 0.0)
 49
 50        self._button = ba.buttonwidget(
 51            parent=parent,
 52            size=size,
 53            label='',
 54            button_type='square',
 55            scale=scale,
 56            autoselect=True,
 57            on_activate_call=self._on_activate,
 58            transition_delay=transition_delay,
 59            color=color,
 60        )
 61
 62        self._title_text = ba.textwidget(
 63            parent=parent,
 64            size=(0, 0),
 65            draw_controller=self._button,
 66            h_align='center',
 67            v_align='center',
 68            maxwidth=size[0] * scale * 0.85,
 69            text=ba.Lstr(
 70                resource='league.leagueRankText',
 71                fallback_resource='coopSelectWindow.powerRankingText',
 72            ),
 73            color=self._header_color,
 74            flatness=1.0,
 75            shadow=1.0,
 76            scale=scale * 0.5,
 77            transition_delay=transition_delay,
 78        )
 79
 80        self._value_text = ba.textwidget(
 81            parent=parent,
 82            size=(0, 0),
 83            h_align='center',
 84            v_align='center',
 85            maxwidth=size[0] * scale * 0.85,
 86            text='-',
 87            draw_controller=self._button,
 88            big=True,
 89            scale=scale,
 90            transition_delay=transition_delay,
 91            color=textcolor,
 92        )
 93
 94        self._smooth_percent: float | None = None
 95        self._percent: int | None = None
 96        self._smooth_rank: float | None = None
 97        self._rank: int | None = None
 98        self._ticking_node: ba.Node | None = None
 99        self._smooth_increase_speed = 1.0
100        self._league: str | None = None
101        self._improvement_text: str | None = None
102
103        self._smooth_update_timer: ba.Timer | None = None
104
105        # Take note of our account state; we'll refresh later if this changes.
106        self._account_state_num = ba.internal.get_v1_account_state_num()
107        self._last_power_ranking_query_time: float | None = None
108        self._doing_power_ranking_query = False
109        self.set_position(position)
110        self._bg_flash = False
111        self._update_timer = ba.Timer(
112            1.0,
113            ba.WeakCall(self._update),
114            timetype=ba.TimeType.REAL,
115            repeat=True,
116        )
117        self._update()
118
119        # If we've got cached power-ranking data already, apply it.
120        data = ba.app.accounts_v1.get_cached_league_rank_data()
121        if data is not None:
122            self._update_for_league_rank_data(data)
123
124    def _on_activate(self) -> None:
125        ba.internal.increment_analytics_count('League rank button press')
126        self._on_activate_call()
127
128    def __del__(self) -> None:
129        if self._ticking_node is not None:
130            self._ticking_node.delete()
131
132    def _start_smooth_update(self) -> None:
133        self._smooth_update_timer = ba.Timer(
134            0.05,
135            ba.WeakCall(self._smooth_update),
136            repeat=True,
137            timetype=ba.TimeType.REAL,
138        )
139
140    def _smooth_update(self) -> None:
141        # pylint: disable=too-many-branches
142        # pylint: disable=too-many-statements
143        try:
144            if not self._button:
145                return
146            if self._ticking_node is None:
147                with ba.Context('ui'):
148                    self._ticking_node = ba.newnode(
149                        'sound',
150                        attrs={
151                            'sound': ba.getsound('scoreIncrease'),
152                            'positional': False,
153                        },
154                    )
155            self._bg_flash = not self._bg_flash
156            color_used = (
157                (self._color[0] * 2, self._color[1] * 2, self._color[2] * 2)
158                if self._bg_flash
159                else self._color
160            )
161            textcolor_used = (1, 1, 1) if self._bg_flash else self._textcolor
162            header_color_used = (
163                (1, 1, 1) if self._bg_flash else self._header_color
164            )
165
166            if self._rank is not None:
167                assert self._smooth_rank is not None
168                self._smooth_rank -= 1.0 * self._smooth_increase_speed
169                finished = int(self._smooth_rank) <= self._rank
170            elif self._smooth_percent is not None:
171                self._smooth_percent += 1.0 * self._smooth_increase_speed
172                assert self._percent is not None
173                finished = int(self._smooth_percent) >= self._percent
174            else:
175                finished = True
176            if finished:
177                if self._rank is not None:
178                    self._smooth_rank = float(self._rank)
179                elif self._percent is not None:
180                    self._smooth_percent = float(self._percent)
181                color_used = self._color
182                textcolor_used = self._textcolor
183                self._smooth_update_timer = None
184                if self._ticking_node is not None:
185                    self._ticking_node.delete()
186                    self._ticking_node = None
187                ba.playsound(ba.getsound('cashRegister2'))
188                assert self._improvement_text is not None
189                diff_text = ba.textwidget(
190                    parent=self._parent,
191                    size=(0, 0),
192                    h_align='center',
193                    v_align='center',
194                    text='+' + self._improvement_text + '!',
195                    position=(
196                        self._position[0] + self._size[0] * 0.5 * self._scale,
197                        self._position[1] + self._size[1] * -0.2 * self._scale,
198                    ),
199                    color=(0, 1, 0),
200                    flatness=1.0,
201                    shadow=0.0,
202                    scale=self._scale * 0.7,
203                )
204
205                def safe_delete(widget: ba.Widget) -> None:
206                    if widget:
207                        widget.delete()
208
209                ba.timer(
210                    2.0,
211                    ba.Call(safe_delete, diff_text),
212                    timetype=ba.TimeType.REAL,
213                )
214            status_text: str | ba.Lstr
215            if self._rank is not None:
216                assert self._smooth_rank is not None
217                status_text = ba.Lstr(
218                    resource='numberText',
219                    subs=[('${NUMBER}', str(int(self._smooth_rank)))],
220                )
221            elif self._smooth_percent is not None:
222                status_text = str(int(self._smooth_percent)) + '%'
223            else:
224                status_text = '-'
225            ba.textwidget(
226                edit=self._value_text, text=status_text, color=textcolor_used
227            )
228            ba.textwidget(edit=self._title_text, color=header_color_used)
229            ba.buttonwidget(edit=self._button, color=color_used)
230
231        except Exception:
232            ba.print_exception('Error doing smooth update.')
233            self._smooth_update_timer = None
234
235    def _update_for_league_rank_data(self, data: dict[str, Any] | None) -> None:
236        # pylint: disable=too-many-branches
237        # pylint: disable=too-many-statements
238
239        # If our button has died, ignore.
240        if not self._button:
241            return
242
243        status_text: str | ba.Lstr
244
245        in_top = data is not None and data['rank'] is not None
246        do_percent = False
247        if data is None or ba.internal.get_v1_account_state() != 'signed_in':
248            self._percent = self._rank = None
249            status_text = '-'
250        elif in_top:
251            self._percent = None
252            self._rank = data['rank']
253            prev_league = self._league
254            self._league = data['l']
255
256            # If this is the first set, league has changed, or rank has gotten
257            # worse, snap the smooth value immediately.
258            assert self._rank is not None
259            if (
260                self._smooth_rank is None
261                or prev_league != self._league
262                or self._rank > int(self._smooth_rank)
263            ):
264                self._smooth_rank = float(self._rank)
265            status_text = ba.Lstr(
266                resource='numberText',
267                subs=[('${NUMBER}', str(int(self._smooth_rank)))],
268            )
269        else:
270            try:
271                if not data['scores'] or data['scores'][-1][1] <= 0:
272                    self._percent = self._rank = None
273                    status_text = '-'
274                else:
275                    our_points = ba.app.accounts_v1.get_league_rank_points(data)
276                    progress = float(our_points) / data['scores'][-1][1]
277                    self._percent = int(progress * 100.0)
278                    self._rank = None
279                    do_percent = True
280                    prev_league = self._league
281                    self._league = data['l']
282
283                    # If this is the first set, league has changed, or percent
284                    # has decreased, snap the smooth value immediately.
285                    if (
286                        self._smooth_percent is None
287                        or prev_league != self._league
288                        or self._percent < int(self._smooth_percent)
289                    ):
290                        self._smooth_percent = float(self._percent)
291                    status_text = str(int(self._smooth_percent)) + '%'
292
293            except Exception:
294                ba.print_exception('Error updating power ranking.')
295                self._percent = self._rank = None
296                status_text = '-'
297
298        # If we're doing a smooth update, set a timer.
299        if (
300            self._rank is not None
301            and self._smooth_rank is not None
302            and int(self._smooth_rank) != self._rank
303        ):
304            self._improvement_text = str(
305                -(int(self._rank) - int(self._smooth_rank))
306            )
307            diff = abs(self._rank - self._smooth_rank)
308            if diff > 100:
309                self._smooth_increase_speed = diff / 80.0
310            elif diff > 50:
311                self._smooth_increase_speed = diff / 70.0
312            elif diff > 25:
313                self._smooth_increase_speed = diff / 55.0
314            else:
315                self._smooth_increase_speed = diff / 40.0
316            self._smooth_increase_speed = max(0.4, self._smooth_increase_speed)
317            ba.timer(
318                self._smooth_update_delay,
319                ba.WeakCall(self._start_smooth_update),
320                timetype=ba.TimeType.REAL,
321                timeformat=ba.TimeFormat.MILLISECONDS,
322            )
323
324        if (
325            self._percent is not None
326            and self._smooth_percent is not None
327            and int(self._smooth_percent) != self._percent
328        ):
329            self._improvement_text = str(
330                (int(self._percent) - int(self._smooth_percent))
331            )
332            self._smooth_increase_speed = 0.3
333            ba.timer(
334                self._smooth_update_delay,
335                ba.WeakCall(self._start_smooth_update),
336                timetype=ba.TimeType.REAL,
337                timeformat=ba.TimeFormat.MILLISECONDS,
338            )
339
340        if do_percent:
341            ba.textwidget(
342                edit=self._title_text,
343                text=ba.Lstr(resource='coopSelectWindow.toRankedText'),
344            )
345        else:
346            try:
347                assert data is not None
348                txt = ba.Lstr(
349                    resource='league.leagueFullText',
350                    subs=[
351                        (
352                            '${NAME}',
353                            ba.Lstr(translate=('leagueNames', data['l']['n'])),
354                        ),
355                    ],
356                )
357                t_color = data['l']['c']
358            except Exception:
359                txt = ba.Lstr(
360                    resource='league.leagueRankText',
361                    fallback_resource='coopSelectWindow.powerRankingText',
362                )
363                t_color = ba.app.ui.title_color
364            ba.textwidget(edit=self._title_text, text=txt, color=t_color)
365        ba.textwidget(edit=self._value_text, text=status_text)
366
367    def _on_power_ranking_query_response(
368        self, data: dict[str, Any] | None
369    ) -> None:
370        self._doing_power_ranking_query = False
371        ba.app.accounts_v1.cache_league_rank_data(data)
372        self._update_for_league_rank_data(data)
373
374    def _update(self) -> None:
375        cur_time = ba.time(ba.TimeType.REAL)
376
377        # If our account state has changed, refresh our UI.
378        account_state_num = ba.internal.get_v1_account_state_num()
379        if account_state_num != self._account_state_num:
380            self._account_state_num = account_state_num
381
382            # And power ranking too...
383            if not self._doing_power_ranking_query:
384                self._last_power_ranking_query_time = None
385
386        # Send off a new power-ranking query if its been
387        # long enough or whatnot.
388        if not self._doing_power_ranking_query and (
389            self._last_power_ranking_query_time is None
390            or cur_time - self._last_power_ranking_query_time > 30.0
391        ):
392            self._last_power_ranking_query_time = cur_time
393            self._doing_power_ranking_query = True
394            ba.internal.power_ranking_query(
395                callback=ba.WeakCall(self._on_power_ranking_query_response)
396            )
397
398    def _default_on_activate_call(self) -> None:
399        # pylint: disable=cyclic-import
400        from bastd.ui.league.rankwindow import LeagueRankWindow
401
402        LeagueRankWindow(modal=True, origin_widget=self._button)
403
404    def set_position(self, position: tuple[float, float]) -> None:
405        """Set the button's position."""
406        self._position = position
407        if not self._button:
408            return
409        ba.buttonwidget(edit=self._button, position=self._position)
410        ba.textwidget(
411            edit=self._title_text,
412            position=(
413                self._position[0] + self._size[0] * 0.5 * self._scale,
414                self._position[1] + self._size[1] * 0.82 * self._scale,
415            ),
416        )
417        ba.textwidget(
418            edit=self._value_text,
419            position=(
420                self._position[0] + self._size[0] * 0.5 * self._scale,
421                self._position[1] + self._size[1] * 0.36 * self._scale,
422            ),
423        )
424
425    def get_button(self) -> ba.Widget:
426        """Return the underlying button ba.Widget>"""
427        return self._button

Button showing league rank.

LeagueRankButton( parent: _ba.Widget, position: tuple[float, float], size: tuple[float, float], scale: float, on_activate_call: Optional[Callable[[], Any]] = None, transition_delay: float | None = None, color: tuple[float, float, float] | None = None, textcolor: tuple[float, float, float] | None = None, smooth_update_delay: float | None = None)
 20    def __init__(
 21        self,
 22        parent: ba.Widget,
 23        position: tuple[float, float],
 24        size: tuple[float, float],
 25        scale: float,
 26        on_activate_call: Callable[[], Any] | None = None,
 27        transition_delay: float | None = None,
 28        color: tuple[float, float, float] | None = None,
 29        textcolor: tuple[float, float, float] | None = None,
 30        smooth_update_delay: float | None = None,
 31    ):
 32        if on_activate_call is None:
 33            on_activate_call = ba.WeakCall(self._default_on_activate_call)
 34        self._on_activate_call = on_activate_call
 35        if smooth_update_delay is None:
 36            smooth_update_delay = 1000
 37        self._smooth_update_delay = smooth_update_delay
 38        self._size = size
 39        self._scale = scale
 40        if color is None:
 41            color = (0.5, 0.6, 0.5)
 42        if textcolor is None:
 43            textcolor = (1, 1, 1)
 44        self._color = color
 45        self._textcolor = textcolor
 46        self._header_color = (0.8, 0.8, 2.0)
 47        self._parent = parent
 48        self._position: tuple[float, float] = (0.0, 0.0)
 49
 50        self._button = ba.buttonwidget(
 51            parent=parent,
 52            size=size,
 53            label='',
 54            button_type='square',
 55            scale=scale,
 56            autoselect=True,
 57            on_activate_call=self._on_activate,
 58            transition_delay=transition_delay,
 59            color=color,
 60        )
 61
 62        self._title_text = ba.textwidget(
 63            parent=parent,
 64            size=(0, 0),
 65            draw_controller=self._button,
 66            h_align='center',
 67            v_align='center',
 68            maxwidth=size[0] * scale * 0.85,
 69            text=ba.Lstr(
 70                resource='league.leagueRankText',
 71                fallback_resource='coopSelectWindow.powerRankingText',
 72            ),
 73            color=self._header_color,
 74            flatness=1.0,
 75            shadow=1.0,
 76            scale=scale * 0.5,
 77            transition_delay=transition_delay,
 78        )
 79
 80        self._value_text = ba.textwidget(
 81            parent=parent,
 82            size=(0, 0),
 83            h_align='center',
 84            v_align='center',
 85            maxwidth=size[0] * scale * 0.85,
 86            text='-',
 87            draw_controller=self._button,
 88            big=True,
 89            scale=scale,
 90            transition_delay=transition_delay,
 91            color=textcolor,
 92        )
 93
 94        self._smooth_percent: float | None = None
 95        self._percent: int | None = None
 96        self._smooth_rank: float | None = None
 97        self._rank: int | None = None
 98        self._ticking_node: ba.Node | None = None
 99        self._smooth_increase_speed = 1.0
100        self._league: str | None = None
101        self._improvement_text: str | None = None
102
103        self._smooth_update_timer: ba.Timer | None = None
104
105        # Take note of our account state; we'll refresh later if this changes.
106        self._account_state_num = ba.internal.get_v1_account_state_num()
107        self._last_power_ranking_query_time: float | None = None
108        self._doing_power_ranking_query = False
109        self.set_position(position)
110        self._bg_flash = False
111        self._update_timer = ba.Timer(
112            1.0,
113            ba.WeakCall(self._update),
114            timetype=ba.TimeType.REAL,
115            repeat=True,
116        )
117        self._update()
118
119        # If we've got cached power-ranking data already, apply it.
120        data = ba.app.accounts_v1.get_cached_league_rank_data()
121        if data is not None:
122            self._update_for_league_rank_data(data)
def set_position(self, position: tuple[float, float]) -> None:
404    def set_position(self, position: tuple[float, float]) -> None:
405        """Set the button's position."""
406        self._position = position
407        if not self._button:
408            return
409        ba.buttonwidget(edit=self._button, position=self._position)
410        ba.textwidget(
411            edit=self._title_text,
412            position=(
413                self._position[0] + self._size[0] * 0.5 * self._scale,
414                self._position[1] + self._size[1] * 0.82 * self._scale,
415            ),
416        )
417        ba.textwidget(
418            edit=self._value_text,
419            position=(
420                self._position[0] + self._size[0] * 0.5 * self._scale,
421                self._position[1] + self._size[1] * 0.36 * self._scale,
422            ),
423        )

Set the button's position.

def get_button(self) -> _ba.Widget:
425    def get_button(self) -> ba.Widget:
426        """Return the underlying button ba.Widget>"""
427        return self._button

Return the underlying button ba.Widget>