Source code for bauiv1lib.league.rankbutton

# Released under the MIT License. See LICENSE for details.
#
"""Provides a button showing league rank."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

import bauiv1 as bui

if TYPE_CHECKING:
    from typing import Any, Callable


[docs] class LeagueRankButton: """Button showing league rank.""" def __init__( self, parent: bui.Widget, position: tuple[float, float], size: tuple[float, float], scale: float, *, on_activate_call: Callable[[], Any] | None = 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, ): if on_activate_call is None: on_activate_call = bui.WeakCall(self._default_on_activate_call) self._on_activate_call = on_activate_call if smooth_update_delay is None: smooth_update_delay = 1.0 self._smooth_update_delay = smooth_update_delay self._size = size self._scale = scale if color is None: color = (0.5, 0.6, 0.5) if textcolor is None: textcolor = (1, 1, 1) self._color = color self._textcolor = textcolor self._header_color = (0.8, 0.8, 2.0) self._parent = parent self._position: tuple[float, float] = (0.0, 0.0) self._button = bui.buttonwidget( parent=parent, size=size, label='', button_type='square', scale=scale, autoselect=True, on_activate_call=self._on_activate, transition_delay=transition_delay, color=color, ) self._title_text = bui.textwidget( parent=parent, size=(0, 0), draw_controller=self._button, h_align='center', v_align='center', maxwidth=size[0] * scale * 0.85, text=bui.Lstr( resource='league.leagueRankText', fallback_resource='coopSelectWindow.powerRankingText', ), color=self._header_color, flatness=1.0, shadow=1.0, scale=scale * 0.5, transition_delay=transition_delay, ) self._value_text = bui.textwidget( parent=parent, size=(0, 0), h_align='center', v_align='center', maxwidth=size[0] * scale * 0.85, text='-', draw_controller=self._button, big=True, scale=scale, transition_delay=transition_delay, color=textcolor, ) plus = bui.app.plus assert plus is not None self._smooth_percent: float | None = None self._percent: int | None = None self._smooth_rank: float | None = None self._rank: int | None = None self._ticking_sound: bui.Sound | None = None self._smooth_increase_speed = 1.0 self._league: str | None = None self._improvement_text: str | None = None self._smooth_update_timer: bui.AppTimer | None = None # Take note of our account state; we'll refresh later if this changes. self._account_state_num = plus.get_v1_account_state_num() self._last_power_ranking_query_time: float | None = None self._doing_power_ranking_query = False self.set_position(position) self._bg_flash = False self._update_timer = bui.AppTimer( 1.0, bui.WeakCall(self._update), repeat=True ) self._update() # If we've got cached power-ranking data already, apply it. assert bui.app.classic is not None data = bui.app.classic.accounts.get_cached_league_rank_data() if data is not None: self._update_for_league_rank_data(data) def _on_activate(self) -> None: bui.increment_analytics_count('League rank button press') self._on_activate_call() def __del__(self) -> None: if self._ticking_sound is not None: self._ticking_sound.stop() self._ticking_sound = None def _start_smooth_update(self) -> None: self._smooth_update_timer = bui.AppTimer( 0.05, bui.WeakCall(self._smooth_update), repeat=True ) def _smooth_update(self) -> None: # pylint: disable=too-many-branches # pylint: disable=too-many-statements try: if not self._button: return if self._ticking_sound is None: self._ticking_sound = bui.getsound('scoreIncrease') self._ticking_sound.play() self._bg_flash = not self._bg_flash color_used = ( (self._color[0] * 2, self._color[1] * 2, self._color[2] * 2) if self._bg_flash else self._color ) textcolor_used = (1, 1, 1) if self._bg_flash else self._textcolor header_color_used = ( (1, 1, 1) if self._bg_flash else self._header_color ) if self._rank is not None: assert self._smooth_rank is not None self._smooth_rank -= 1.0 * self._smooth_increase_speed finished = int(self._smooth_rank) <= self._rank elif self._smooth_percent is not None: self._smooth_percent += 1.0 * self._smooth_increase_speed assert self._percent is not None finished = int(self._smooth_percent) >= self._percent else: finished = True if finished: if self._rank is not None: self._smooth_rank = float(self._rank) elif self._percent is not None: self._smooth_percent = float(self._percent) color_used = self._color textcolor_used = self._textcolor self._smooth_update_timer = None if self._ticking_sound is not None: self._ticking_sound.stop() self._ticking_sound = None bui.getsound('cashRegister2').play() assert self._improvement_text is not None diff_text = bui.textwidget( parent=self._parent, size=(0, 0), h_align='center', v_align='center', text='+' + self._improvement_text + '!', position=( self._position[0] + self._size[0] * 0.5 * self._scale, self._position[1] + self._size[1] * -0.2 * self._scale, ), color=(0, 1, 0), flatness=1.0, shadow=0.0, scale=self._scale * 0.7, ) def safe_delete(widget: bui.Widget) -> None: if widget: widget.delete() bui.apptimer(2.0, bui.Call(safe_delete, diff_text)) status_text: str | bui.Lstr if self._rank is not None: assert self._smooth_rank is not None status_text = bui.Lstr( resource='numberText', subs=[('${NUMBER}', str(int(self._smooth_rank)))], ) elif self._smooth_percent is not None: status_text = str(int(self._smooth_percent)) + '%' else: status_text = '-' bui.textwidget( edit=self._value_text, text=status_text, color=textcolor_used ) bui.textwidget(edit=self._title_text, color=header_color_used) bui.buttonwidget(edit=self._button, color=color_used) except Exception: logging.exception('Error doing smooth update.') self._smooth_update_timer = None def _update_for_league_rank_data(self, data: dict[str, Any] | None) -> None: # pylint: disable=too-many-branches # pylint: disable=too-many-statements plus = bui.app.plus assert plus is not None # If our button has died, ignore. if not self._button: return status_text: str | bui.Lstr in_top = data is not None and data['rank'] is not None do_percent = False if data is None or plus.get_v1_account_state() != 'signed_in': self._percent = self._rank = None status_text = '-' elif in_top: self._percent = None self._rank = data['rank'] prev_league = self._league self._league = data['l'] # If this is the first set, league has changed, or rank has gotten # worse, snap the smooth value immediately. assert self._rank is not None if ( self._smooth_rank is None or prev_league != self._league or self._rank > int(self._smooth_rank) ): self._smooth_rank = float(self._rank) status_text = bui.Lstr( resource='numberText', subs=[('${NUMBER}', str(int(self._smooth_rank)))], ) else: try: if not data['scores'] or data['scores'][-1][1] <= 0: self._percent = self._rank = None status_text = '-' else: assert bui.app.classic is not None our_points = ( bui.app.classic.accounts.get_league_rank_points(data) ) progress = float(our_points) / data['scores'][-1][1] self._percent = int(progress * 100.0) self._rank = None do_percent = True prev_league = self._league self._league = data['l'] # If this is the first set, league has changed, or percent # has decreased, snap the smooth value immediately. if ( self._smooth_percent is None or prev_league != self._league or self._percent < int(self._smooth_percent) ): self._smooth_percent = float(self._percent) status_text = str(int(self._smooth_percent)) + '%' except Exception: logging.exception('Error updating power ranking.') self._percent = self._rank = None status_text = '-' # If we're doing a smooth update, set a timer. if ( self._rank is not None and self._smooth_rank is not None and int(self._smooth_rank) != self._rank ): self._improvement_text = str( -(int(self._rank) - int(self._smooth_rank)) ) diff = abs(self._rank - self._smooth_rank) if diff > 100: self._smooth_increase_speed = diff / 80.0 elif diff > 50: self._smooth_increase_speed = diff / 70.0 elif diff > 25: self._smooth_increase_speed = diff / 55.0 else: self._smooth_increase_speed = diff / 40.0 self._smooth_increase_speed = max(0.4, self._smooth_increase_speed) bui.apptimer( self._smooth_update_delay, bui.WeakCall(self._start_smooth_update), ) if ( self._percent is not None and self._smooth_percent is not None and int(self._smooth_percent) != self._percent ): self._improvement_text = str( (int(self._percent) - int(self._smooth_percent)) ) self._smooth_increase_speed = 0.3 bui.apptimer( self._smooth_update_delay, bui.WeakCall(self._start_smooth_update), ) if do_percent: bui.textwidget( edit=self._title_text, text=bui.Lstr(resource='coopSelectWindow.toRankedText'), ) else: try: assert data is not None txt = bui.Lstr( resource='league.leagueFullText', subs=[ ( '${NAME}', bui.Lstr(translate=('leagueNames', data['l']['n'])), ), ], ) t_color = data['l']['c'] except Exception: txt = bui.Lstr( resource='league.leagueRankText', fallback_resource='coopSelectWindow.powerRankingText', ) assert bui.app.classic is not None t_color = bui.app.ui_v1.title_color bui.textwidget(edit=self._title_text, text=txt, color=t_color) bui.textwidget(edit=self._value_text, text=status_text) def _on_power_ranking_query_response( self, data: dict[str, Any] | None ) -> None: self._doing_power_ranking_query = False assert bui.app.classic is not None bui.app.classic.accounts.cache_league_rank_data(data) self._update_for_league_rank_data(data) def _update(self) -> None: cur_time = bui.apptime() plus = bui.app.plus assert plus is not None # If our account state has changed, refresh our UI. account_state_num = plus.get_v1_account_state_num() if account_state_num != self._account_state_num: self._account_state_num = account_state_num # And power ranking too... if not self._doing_power_ranking_query: self._last_power_ranking_query_time = None # Send off a new power-ranking query if its been # long enough or whatnot. if not self._doing_power_ranking_query and ( self._last_power_ranking_query_time is None or cur_time - self._last_power_ranking_query_time > 30.0 ): self._last_power_ranking_query_time = cur_time self._doing_power_ranking_query = True plus.power_ranking_query( callback=bui.WeakCall(self._on_power_ranking_query_response) ) def _default_on_activate_call(self) -> None: # pylint: disable=cyclic-import # from bauiv1lib.league.rankwindow import LeagueRankWindow raise RuntimeError() # LeagueRankWindow(modal=True, origin_widget=self._button)
[docs] def set_position(self, position: tuple[float, float]) -> None: """Set the button's position.""" self._position = position if not self._button: return bui.buttonwidget(edit=self._button, position=self._position) bui.textwidget( edit=self._title_text, position=( self._position[0] + self._size[0] * 0.5 * self._scale, self._position[1] + self._size[1] * 0.82 * self._scale, ), ) bui.textwidget( edit=self._value_text, position=( self._position[0] + self._size[0] * 0.5 * self._scale, self._position[1] + self._size[1] * 0.36 * self._scale, ), )
[docs] def get_button(self) -> bui.Widget: """Return the underlying button bui.Widget>""" return self._button