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

Set the button's position.

def get_button(self) -> _ba.Widget:
377    def get_button(self) -> ba.Widget:
378        """Return the underlying button ba.Widget>"""
379        return self._button

Return the underlying button ba.Widget>