bastd.ui.tournamentscores

Provides a popup for viewing tournament scores.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides a popup for viewing tournament scores."""
  4
  5from __future__ import annotations
  6
  7from typing import TYPE_CHECKING
  8
  9import ba
 10import ba.internal
 11from bastd.ui import popup as popup_ui
 12
 13if TYPE_CHECKING:
 14    from typing import Any, Sequence, Callable
 15
 16
 17class TournamentScoresWindow(popup_ui.PopupWindow):
 18    """Window for viewing tournament scores."""
 19
 20    def __init__(
 21        self,
 22        tournament_id: str,
 23        tournament_activity: ba.GameActivity | None = None,
 24        position: tuple[float, float] = (0.0, 0.0),
 25        scale: float | None = None,
 26        offset: tuple[float, float] = (0.0, 0.0),
 27        tint_color: Sequence[float] = (1.0, 1.0, 1.0),
 28        tint2_color: Sequence[float] = (1.0, 1.0, 1.0),
 29        selected_character: str | None = None,
 30        on_close_call: Callable[[], Any] | None = None,
 31    ):
 32
 33        del tournament_activity  # unused arg
 34        del tint_color  # unused arg
 35        del tint2_color  # unused arg
 36        del selected_character  # unused arg
 37        self._tournament_id = tournament_id
 38        self._subcontainer: ba.Widget | None = None
 39        self._on_close_call = on_close_call
 40        uiscale = ba.app.ui.uiscale
 41        if scale is None:
 42            scale = (
 43                2.3
 44                if uiscale is ba.UIScale.SMALL
 45                else 1.65
 46                if uiscale is ba.UIScale.MEDIUM
 47                else 1.23
 48            )
 49        self._transitioning_out = False
 50
 51        self._width = 400
 52        self._height = (
 53            300
 54            if uiscale is ba.UIScale.SMALL
 55            else 370
 56            if uiscale is ba.UIScale.MEDIUM
 57            else 450
 58        )
 59
 60        bg_color = (0.5, 0.4, 0.6)
 61
 62        # creates our _root_widget
 63        super().__init__(
 64            position=position,
 65            size=(self._width, self._height),
 66            scale=scale,
 67            bg_color=bg_color,
 68            offset=offset,
 69        )
 70
 71        # app = ba.app
 72
 73        self._cancel_button = ba.buttonwidget(
 74            parent=self.root_widget,
 75            position=(50, self._height - 30),
 76            size=(50, 50),
 77            scale=0.5,
 78            label='',
 79            color=bg_color,
 80            on_activate_call=self._on_cancel_press,
 81            autoselect=True,
 82            icon=ba.gettexture('crossOut'),
 83            iconscale=1.2,
 84        )
 85
 86        self._title_text = ba.textwidget(
 87            parent=self.root_widget,
 88            position=(self._width * 0.5, self._height - 20),
 89            size=(0, 0),
 90            h_align='center',
 91            v_align='center',
 92            scale=0.6,
 93            text=ba.Lstr(resource='tournamentStandingsText'),
 94            maxwidth=200,
 95            color=(1, 1, 1, 0.4),
 96        )
 97
 98        self._scrollwidget = ba.scrollwidget(
 99            parent=self.root_widget,
100            size=(self._width - 60, self._height - 70),
101            position=(30, 30),
102            highlight=False,
103            simple_culling_v=10,
104        )
105        ba.widget(edit=self._scrollwidget, autoselect=True)
106
107        self._loading_text = ba.textwidget(
108            parent=self._scrollwidget,
109            scale=0.5,
110            text=ba.Lstr(
111                value='${A}...',
112                subs=[('${A}', ba.Lstr(resource='loadingText'))],
113            ),
114            size=(self._width - 60, 100),
115            h_align='center',
116            v_align='center',
117        )
118
119        ba.containerwidget(
120            edit=self.root_widget, cancel_button=self._cancel_button
121        )
122
123        ba.internal.tournament_query(
124            args={
125                'tournamentIDs': [tournament_id],
126                'numScores': 50,
127                'source': 'scores window',
128            },
129            callback=ba.WeakCall(self._on_tournament_query_response),
130        )
131
132    def _on_tournament_query_response(
133        self, data: dict[str, Any] | None
134    ) -> None:
135        if data is not None:
136            # this used to be the whole payload
137            data_t: list[dict[str, Any]] = data['t']
138            # kill our loading text if we've got scores.. otherwise just
139            # replace it with 'no scores yet'
140            if data_t[0]['scores']:
141                self._loading_text.delete()
142            else:
143                ba.textwidget(
144                    edit=self._loading_text,
145                    text=ba.Lstr(resource='noScoresYetText'),
146                )
147            incr = 30
148            sub_width = self._width - 90
149            sub_height = 30 + len(data_t[0]['scores']) * incr
150            self._subcontainer = ba.containerwidget(
151                parent=self._scrollwidget,
152                size=(sub_width, sub_height),
153                background=False,
154            )
155            for i, entry in enumerate(data_t[0]['scores']):
156
157                ba.textwidget(
158                    parent=self._subcontainer,
159                    position=(sub_width * 0.1 - 5, sub_height - 20 - incr * i),
160                    maxwidth=20,
161                    scale=0.5,
162                    color=(0.6, 0.6, 0.7),
163                    flatness=1.0,
164                    shadow=0.0,
165                    text=str(i + 1),
166                    size=(0, 0),
167                    h_align='right',
168                    v_align='center',
169                )
170
171                ba.textwidget(
172                    parent=self._subcontainer,
173                    position=(sub_width * 0.25 - 2, sub_height - 20 - incr * i),
174                    maxwidth=sub_width * 0.24,
175                    color=(0.9, 1.0, 0.9),
176                    flatness=1.0,
177                    shadow=0.0,
178                    scale=0.6,
179                    text=(
180                        ba.timestring(
181                            entry[0] * 10,
182                            centi=True,
183                            timeformat=ba.TimeFormat.MILLISECONDS,
184                        )
185                        if data_t[0]['scoreType'] == 'time'
186                        else str(entry[0])
187                    ),
188                    size=(0, 0),
189                    h_align='center',
190                    v_align='center',
191                )
192
193                txt = ba.textwidget(
194                    parent=self._subcontainer,
195                    position=(
196                        sub_width * 0.25,
197                        sub_height - 20 - incr * i - (0.5 / 0.7) * incr,
198                    ),
199                    maxwidth=sub_width * 0.6,
200                    scale=0.7,
201                    flatness=1.0,
202                    shadow=0.0,
203                    text=ba.Lstr(value=entry[1]),
204                    selectable=True,
205                    click_activate=True,
206                    autoselect=True,
207                    extra_touch_border_scale=0.0,
208                    size=((sub_width * 0.6) / 0.7, incr / 0.7),
209                    h_align='left',
210                    v_align='center',
211                )
212
213                ba.textwidget(
214                    edit=txt,
215                    on_activate_call=ba.Call(
216                        self._show_player_info, entry, txt
217                    ),
218                )
219                if i == 0:
220                    ba.widget(edit=txt, up_widget=self._cancel_button)
221
222    def _show_player_info(self, entry: Any, textwidget: ba.Widget) -> None:
223        from bastd.ui.account.viewer import AccountViewerWindow
224
225        # for the moment we only work if a single player-info is present..
226        if len(entry[2]) != 1:
227            ba.playsound(ba.getsound('error'))
228            return
229        ba.playsound(ba.getsound('swish'))
230        AccountViewerWindow(
231            account_id=entry[2][0].get('a', None),
232            profile_id=entry[2][0].get('p', None),
233            position=textwidget.get_screen_space_center(),
234        )
235        self._transition_out()
236
237    def _on_cancel_press(self) -> None:
238        self._transition_out()
239
240    def _transition_out(self) -> None:
241        if not self._transitioning_out:
242            self._transitioning_out = True
243            ba.containerwidget(edit=self.root_widget, transition='out_scale')
244            if self._on_close_call is not None:
245                self._on_close_call()
246
247    def on_popup_cancel(self) -> None:
248        ba.playsound(ba.getsound('swish'))
249        self._transition_out()
class TournamentScoresWindow(bastd.ui.popup.PopupWindow):
 18class TournamentScoresWindow(popup_ui.PopupWindow):
 19    """Window for viewing tournament scores."""
 20
 21    def __init__(
 22        self,
 23        tournament_id: str,
 24        tournament_activity: ba.GameActivity | None = None,
 25        position: tuple[float, float] = (0.0, 0.0),
 26        scale: float | None = None,
 27        offset: tuple[float, float] = (0.0, 0.0),
 28        tint_color: Sequence[float] = (1.0, 1.0, 1.0),
 29        tint2_color: Sequence[float] = (1.0, 1.0, 1.0),
 30        selected_character: str | None = None,
 31        on_close_call: Callable[[], Any] | None = None,
 32    ):
 33
 34        del tournament_activity  # unused arg
 35        del tint_color  # unused arg
 36        del tint2_color  # unused arg
 37        del selected_character  # unused arg
 38        self._tournament_id = tournament_id
 39        self._subcontainer: ba.Widget | None = None
 40        self._on_close_call = on_close_call
 41        uiscale = ba.app.ui.uiscale
 42        if scale is None:
 43            scale = (
 44                2.3
 45                if uiscale is ba.UIScale.SMALL
 46                else 1.65
 47                if uiscale is ba.UIScale.MEDIUM
 48                else 1.23
 49            )
 50        self._transitioning_out = False
 51
 52        self._width = 400
 53        self._height = (
 54            300
 55            if uiscale is ba.UIScale.SMALL
 56            else 370
 57            if uiscale is ba.UIScale.MEDIUM
 58            else 450
 59        )
 60
 61        bg_color = (0.5, 0.4, 0.6)
 62
 63        # creates our _root_widget
 64        super().__init__(
 65            position=position,
 66            size=(self._width, self._height),
 67            scale=scale,
 68            bg_color=bg_color,
 69            offset=offset,
 70        )
 71
 72        # app = ba.app
 73
 74        self._cancel_button = ba.buttonwidget(
 75            parent=self.root_widget,
 76            position=(50, self._height - 30),
 77            size=(50, 50),
 78            scale=0.5,
 79            label='',
 80            color=bg_color,
 81            on_activate_call=self._on_cancel_press,
 82            autoselect=True,
 83            icon=ba.gettexture('crossOut'),
 84            iconscale=1.2,
 85        )
 86
 87        self._title_text = ba.textwidget(
 88            parent=self.root_widget,
 89            position=(self._width * 0.5, self._height - 20),
 90            size=(0, 0),
 91            h_align='center',
 92            v_align='center',
 93            scale=0.6,
 94            text=ba.Lstr(resource='tournamentStandingsText'),
 95            maxwidth=200,
 96            color=(1, 1, 1, 0.4),
 97        )
 98
 99        self._scrollwidget = ba.scrollwidget(
100            parent=self.root_widget,
101            size=(self._width - 60, self._height - 70),
102            position=(30, 30),
103            highlight=False,
104            simple_culling_v=10,
105        )
106        ba.widget(edit=self._scrollwidget, autoselect=True)
107
108        self._loading_text = ba.textwidget(
109            parent=self._scrollwidget,
110            scale=0.5,
111            text=ba.Lstr(
112                value='${A}...',
113                subs=[('${A}', ba.Lstr(resource='loadingText'))],
114            ),
115            size=(self._width - 60, 100),
116            h_align='center',
117            v_align='center',
118        )
119
120        ba.containerwidget(
121            edit=self.root_widget, cancel_button=self._cancel_button
122        )
123
124        ba.internal.tournament_query(
125            args={
126                'tournamentIDs': [tournament_id],
127                'numScores': 50,
128                'source': 'scores window',
129            },
130            callback=ba.WeakCall(self._on_tournament_query_response),
131        )
132
133    def _on_tournament_query_response(
134        self, data: dict[str, Any] | None
135    ) -> None:
136        if data is not None:
137            # this used to be the whole payload
138            data_t: list[dict[str, Any]] = data['t']
139            # kill our loading text if we've got scores.. otherwise just
140            # replace it with 'no scores yet'
141            if data_t[0]['scores']:
142                self._loading_text.delete()
143            else:
144                ba.textwidget(
145                    edit=self._loading_text,
146                    text=ba.Lstr(resource='noScoresYetText'),
147                )
148            incr = 30
149            sub_width = self._width - 90
150            sub_height = 30 + len(data_t[0]['scores']) * incr
151            self._subcontainer = ba.containerwidget(
152                parent=self._scrollwidget,
153                size=(sub_width, sub_height),
154                background=False,
155            )
156            for i, entry in enumerate(data_t[0]['scores']):
157
158                ba.textwidget(
159                    parent=self._subcontainer,
160                    position=(sub_width * 0.1 - 5, sub_height - 20 - incr * i),
161                    maxwidth=20,
162                    scale=0.5,
163                    color=(0.6, 0.6, 0.7),
164                    flatness=1.0,
165                    shadow=0.0,
166                    text=str(i + 1),
167                    size=(0, 0),
168                    h_align='right',
169                    v_align='center',
170                )
171
172                ba.textwidget(
173                    parent=self._subcontainer,
174                    position=(sub_width * 0.25 - 2, sub_height - 20 - incr * i),
175                    maxwidth=sub_width * 0.24,
176                    color=(0.9, 1.0, 0.9),
177                    flatness=1.0,
178                    shadow=0.0,
179                    scale=0.6,
180                    text=(
181                        ba.timestring(
182                            entry[0] * 10,
183                            centi=True,
184                            timeformat=ba.TimeFormat.MILLISECONDS,
185                        )
186                        if data_t[0]['scoreType'] == 'time'
187                        else str(entry[0])
188                    ),
189                    size=(0, 0),
190                    h_align='center',
191                    v_align='center',
192                )
193
194                txt = ba.textwidget(
195                    parent=self._subcontainer,
196                    position=(
197                        sub_width * 0.25,
198                        sub_height - 20 - incr * i - (0.5 / 0.7) * incr,
199                    ),
200                    maxwidth=sub_width * 0.6,
201                    scale=0.7,
202                    flatness=1.0,
203                    shadow=0.0,
204                    text=ba.Lstr(value=entry[1]),
205                    selectable=True,
206                    click_activate=True,
207                    autoselect=True,
208                    extra_touch_border_scale=0.0,
209                    size=((sub_width * 0.6) / 0.7, incr / 0.7),
210                    h_align='left',
211                    v_align='center',
212                )
213
214                ba.textwidget(
215                    edit=txt,
216                    on_activate_call=ba.Call(
217                        self._show_player_info, entry, txt
218                    ),
219                )
220                if i == 0:
221                    ba.widget(edit=txt, up_widget=self._cancel_button)
222
223    def _show_player_info(self, entry: Any, textwidget: ba.Widget) -> None:
224        from bastd.ui.account.viewer import AccountViewerWindow
225
226        # for the moment we only work if a single player-info is present..
227        if len(entry[2]) != 1:
228            ba.playsound(ba.getsound('error'))
229            return
230        ba.playsound(ba.getsound('swish'))
231        AccountViewerWindow(
232            account_id=entry[2][0].get('a', None),
233            profile_id=entry[2][0].get('p', None),
234            position=textwidget.get_screen_space_center(),
235        )
236        self._transition_out()
237
238    def _on_cancel_press(self) -> None:
239        self._transition_out()
240
241    def _transition_out(self) -> None:
242        if not self._transitioning_out:
243            self._transitioning_out = True
244            ba.containerwidget(edit=self.root_widget, transition='out_scale')
245            if self._on_close_call is not None:
246                self._on_close_call()
247
248    def on_popup_cancel(self) -> None:
249        ba.playsound(ba.getsound('swish'))
250        self._transition_out()

Window for viewing tournament scores.

TournamentScoresWindow( tournament_id: str, tournament_activity: ba._gameactivity.GameActivity | None = None, position: tuple[float, float] = (0.0, 0.0), scale: float | None = None, offset: tuple[float, float] = (0.0, 0.0), tint_color: Sequence[float] = (1.0, 1.0, 1.0), tint2_color: Sequence[float] = (1.0, 1.0, 1.0), selected_character: str | None = None, on_close_call: Optional[Callable[[], Any]] = None)
 21    def __init__(
 22        self,
 23        tournament_id: str,
 24        tournament_activity: ba.GameActivity | None = None,
 25        position: tuple[float, float] = (0.0, 0.0),
 26        scale: float | None = None,
 27        offset: tuple[float, float] = (0.0, 0.0),
 28        tint_color: Sequence[float] = (1.0, 1.0, 1.0),
 29        tint2_color: Sequence[float] = (1.0, 1.0, 1.0),
 30        selected_character: str | None = None,
 31        on_close_call: Callable[[], Any] | None = None,
 32    ):
 33
 34        del tournament_activity  # unused arg
 35        del tint_color  # unused arg
 36        del tint2_color  # unused arg
 37        del selected_character  # unused arg
 38        self._tournament_id = tournament_id
 39        self._subcontainer: ba.Widget | None = None
 40        self._on_close_call = on_close_call
 41        uiscale = ba.app.ui.uiscale
 42        if scale is None:
 43            scale = (
 44                2.3
 45                if uiscale is ba.UIScale.SMALL
 46                else 1.65
 47                if uiscale is ba.UIScale.MEDIUM
 48                else 1.23
 49            )
 50        self._transitioning_out = False
 51
 52        self._width = 400
 53        self._height = (
 54            300
 55            if uiscale is ba.UIScale.SMALL
 56            else 370
 57            if uiscale is ba.UIScale.MEDIUM
 58            else 450
 59        )
 60
 61        bg_color = (0.5, 0.4, 0.6)
 62
 63        # creates our _root_widget
 64        super().__init__(
 65            position=position,
 66            size=(self._width, self._height),
 67            scale=scale,
 68            bg_color=bg_color,
 69            offset=offset,
 70        )
 71
 72        # app = ba.app
 73
 74        self._cancel_button = ba.buttonwidget(
 75            parent=self.root_widget,
 76            position=(50, self._height - 30),
 77            size=(50, 50),
 78            scale=0.5,
 79            label='',
 80            color=bg_color,
 81            on_activate_call=self._on_cancel_press,
 82            autoselect=True,
 83            icon=ba.gettexture('crossOut'),
 84            iconscale=1.2,
 85        )
 86
 87        self._title_text = ba.textwidget(
 88            parent=self.root_widget,
 89            position=(self._width * 0.5, self._height - 20),
 90            size=(0, 0),
 91            h_align='center',
 92            v_align='center',
 93            scale=0.6,
 94            text=ba.Lstr(resource='tournamentStandingsText'),
 95            maxwidth=200,
 96            color=(1, 1, 1, 0.4),
 97        )
 98
 99        self._scrollwidget = ba.scrollwidget(
100            parent=self.root_widget,
101            size=(self._width - 60, self._height - 70),
102            position=(30, 30),
103            highlight=False,
104            simple_culling_v=10,
105        )
106        ba.widget(edit=self._scrollwidget, autoselect=True)
107
108        self._loading_text = ba.textwidget(
109            parent=self._scrollwidget,
110            scale=0.5,
111            text=ba.Lstr(
112                value='${A}...',
113                subs=[('${A}', ba.Lstr(resource='loadingText'))],
114            ),
115            size=(self._width - 60, 100),
116            h_align='center',
117            v_align='center',
118        )
119
120        ba.containerwidget(
121            edit=self.root_widget, cancel_button=self._cancel_button
122        )
123
124        ba.internal.tournament_query(
125            args={
126                'tournamentIDs': [tournament_id],
127                'numScores': 50,
128                'source': 'scores window',
129            },
130            callback=ba.WeakCall(self._on_tournament_query_response),
131        )
def on_popup_cancel(self) -> None:
248    def on_popup_cancel(self) -> None:
249        ba.playsound(ba.getsound('swish'))
250        self._transition_out()

Called when the popup is canceled.

Cancels can occur due to clicking outside the window, hitting escape, etc.