bastd.ui.playlist.addgame

Provides a window for selecting a game type to add to a playlist.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides a window for selecting a game type to add to a playlist."""
  4
  5from __future__ import annotations
  6
  7from typing import TYPE_CHECKING
  8
  9import ba
 10import ba.internal
 11
 12if TYPE_CHECKING:
 13    from bastd.ui.playlist.editcontroller import PlaylistEditController
 14
 15
 16class PlaylistAddGameWindow(ba.Window):
 17    """Window for selecting a game type to add to a playlist."""
 18
 19    def __init__(
 20        self,
 21        editcontroller: PlaylistEditController,
 22        transition: str = 'in_right',
 23    ):
 24        self._editcontroller = editcontroller
 25        self._r = 'addGameWindow'
 26        uiscale = ba.app.ui.uiscale
 27        self._width = 750 if uiscale is ba.UIScale.SMALL else 650
 28        x_inset = 50 if uiscale is ba.UIScale.SMALL else 0
 29        self._height = (
 30            346
 31            if uiscale is ba.UIScale.SMALL
 32            else 380
 33            if uiscale is ba.UIScale.MEDIUM
 34            else 440
 35        )
 36        top_extra = 30 if uiscale is ba.UIScale.SMALL else 20
 37        self._scroll_width = 210
 38
 39        super().__init__(
 40            root_widget=ba.containerwidget(
 41                size=(self._width, self._height + top_extra),
 42                transition=transition,
 43                scale=(
 44                    2.17
 45                    if uiscale is ba.UIScale.SMALL
 46                    else 1.5
 47                    if uiscale is ba.UIScale.MEDIUM
 48                    else 1.0
 49                ),
 50                stack_offset=(0, 1) if uiscale is ba.UIScale.SMALL else (0, 0),
 51            )
 52        )
 53
 54        self._back_button = ba.buttonwidget(
 55            parent=self._root_widget,
 56            position=(58 + x_inset, self._height - 53),
 57            size=(165, 70),
 58            scale=0.75,
 59            text_scale=1.2,
 60            label=ba.Lstr(resource='backText'),
 61            autoselect=True,
 62            button_type='back',
 63            on_activate_call=self._back,
 64        )
 65        self._select_button = select_button = ba.buttonwidget(
 66            parent=self._root_widget,
 67            position=(self._width - (172 + x_inset), self._height - 50),
 68            autoselect=True,
 69            size=(160, 60),
 70            scale=0.75,
 71            text_scale=1.2,
 72            label=ba.Lstr(resource='selectText'),
 73            on_activate_call=self._add,
 74        )
 75
 76        if ba.app.ui.use_toolbars:
 77            ba.widget(
 78                edit=select_button,
 79                right_widget=ba.internal.get_special_widget('party_button'),
 80            )
 81
 82        ba.textwidget(
 83            parent=self._root_widget,
 84            position=(self._width * 0.5, self._height - 28),
 85            size=(0, 0),
 86            scale=1.0,
 87            text=ba.Lstr(resource=self._r + '.titleText'),
 88            h_align='center',
 89            color=ba.app.ui.title_color,
 90            maxwidth=250,
 91            v_align='center',
 92        )
 93        v = self._height - 64
 94
 95        self._selected_title_text = ba.textwidget(
 96            parent=self._root_widget,
 97            position=(x_inset + self._scroll_width + 50 + 30, v - 15),
 98            size=(0, 0),
 99            scale=1.0,
100            color=(0.7, 1.0, 0.7, 1.0),
101            maxwidth=self._width - self._scroll_width - 150 - x_inset * 2,
102            h_align='left',
103            v_align='center',
104        )
105        v -= 30
106
107        self._selected_description_text = ba.textwidget(
108            parent=self._root_widget,
109            position=(x_inset + self._scroll_width + 50 + 30, v),
110            size=(0, 0),
111            scale=0.7,
112            color=(0.5, 0.8, 0.5, 1.0),
113            maxwidth=self._width - self._scroll_width - 150 - x_inset * 2,
114            h_align='left',
115        )
116
117        scroll_height = self._height - 100
118
119        v = self._height - 60
120
121        self._scrollwidget = ba.scrollwidget(
122            parent=self._root_widget,
123            position=(x_inset + 61, v - scroll_height),
124            size=(self._scroll_width, scroll_height),
125            highlight=False,
126        )
127        ba.widget(
128            edit=self._scrollwidget,
129            up_widget=self._back_button,
130            left_widget=self._back_button,
131            right_widget=select_button,
132        )
133        self._column: ba.Widget | None = None
134
135        v -= 35
136        ba.containerwidget(
137            edit=self._root_widget,
138            cancel_button=self._back_button,
139            start_button=select_button,
140        )
141        self._selected_game_type: type[ba.GameActivity] | None = None
142
143        ba.containerwidget(
144            edit=self._root_widget, selected_child=self._scrollwidget
145        )
146
147        self._game_types: list[type[ba.GameActivity]] = []
148
149        # Get actual games loading in the bg.
150        ba.app.meta.load_exported_classes(
151            ba.GameActivity,
152            self._on_game_types_loaded,
153            completion_cb_in_bg_thread=True,
154        )
155
156        # Refresh with our initial empty list. We'll refresh again once
157        # game loading is complete.
158        self._refresh()
159
160    def _on_game_types_loaded(
161        self, gametypes: list[type[ba.GameActivity]]
162    ) -> None:
163        from ba.internal import get_unowned_game_types
164
165        # We asked for a bg thread completion cb so we can do some
166        # filtering here in the bg thread where its not gonna cause hitches.
167        assert not ba.in_logic_thread()
168        sessiontype = self._editcontroller.get_session_type()
169        unowned = get_unowned_game_types()
170        self._game_types = [
171            gt
172            for gt in gametypes
173            if gt not in unowned and gt.supports_session_type(sessiontype)
174        ]
175
176        # Sort in the current language.
177        self._game_types.sort(key=lambda g: g.get_display_string().evaluate())
178
179        # Tell ourself to refresh back in the logic thread.
180        ba.pushcall(self._refresh, from_other_thread=True)
181
182    def _refresh(self, select_get_more_games_button: bool = False) -> None:
183        # from ba.internal import get_game_types
184
185        if self._column is not None:
186            self._column.delete()
187
188        self._column = ba.columnwidget(
189            parent=self._scrollwidget, border=2, margin=0
190        )
191
192        for i, gametype in enumerate(self._game_types):
193
194            def _doit() -> None:
195                if self._select_button:
196                    ba.timer(
197                        0.1,
198                        self._select_button.activate,
199                        timetype=ba.TimeType.REAL,
200                    )
201
202            txt = ba.textwidget(
203                parent=self._column,
204                position=(0, 0),
205                size=(self._width - 88, 24),
206                text=gametype.get_display_string(),
207                h_align='left',
208                v_align='center',
209                color=(0.8, 0.8, 0.8, 1.0),
210                maxwidth=self._scroll_width * 0.8,
211                on_select_call=ba.Call(self._set_selected_game_type, gametype),
212                always_highlight=True,
213                selectable=True,
214                on_activate_call=_doit,
215            )
216            if i == 0:
217                ba.widget(edit=txt, up_widget=self._back_button)
218
219        self._get_more_games_button = ba.buttonwidget(
220            parent=self._column,
221            autoselect=True,
222            label=ba.Lstr(resource=self._r + '.getMoreGamesText'),
223            color=(0.54, 0.52, 0.67),
224            textcolor=(0.7, 0.65, 0.7),
225            on_activate_call=self._on_get_more_games_press,
226            size=(178, 50),
227        )
228        if select_get_more_games_button:
229            ba.containerwidget(
230                edit=self._column,
231                selected_child=self._get_more_games_button,
232                visible_child=self._get_more_games_button,
233            )
234
235    def _on_get_more_games_press(self) -> None:
236        from bastd.ui.account import show_sign_in_prompt
237        from bastd.ui.store.browser import StoreBrowserWindow
238
239        if ba.internal.get_v1_account_state() != 'signed_in':
240            show_sign_in_prompt()
241            return
242        StoreBrowserWindow(
243            modal=True,
244            show_tab=StoreBrowserWindow.TabID.MINIGAMES,
245            on_close_call=self._on_store_close,
246            origin_widget=self._get_more_games_button,
247        )
248
249    def _on_store_close(self) -> None:
250        self._refresh(select_get_more_games_button=True)
251
252    def _add(self) -> None:
253        ba.internal.lock_all_input()  # Make sure no more commands happen.
254        ba.timer(0.1, ba.internal.unlock_all_input, timetype=ba.TimeType.REAL)
255        assert self._selected_game_type is not None
256        self._editcontroller.add_game_type_selected(self._selected_game_type)
257
258    def _set_selected_game_type(self, gametype: type[ba.GameActivity]) -> None:
259        self._selected_game_type = gametype
260        ba.textwidget(
261            edit=self._selected_title_text, text=gametype.get_display_string()
262        )
263        ba.textwidget(
264            edit=self._selected_description_text,
265            text=gametype.get_description_display_string(
266                self._editcontroller.get_session_type()
267            ),
268        )
269
270    def _back(self) -> None:
271        self._editcontroller.add_game_cancelled()
class PlaylistAddGameWindow(ba.ui.Window):
 17class PlaylistAddGameWindow(ba.Window):
 18    """Window for selecting a game type to add to a playlist."""
 19
 20    def __init__(
 21        self,
 22        editcontroller: PlaylistEditController,
 23        transition: str = 'in_right',
 24    ):
 25        self._editcontroller = editcontroller
 26        self._r = 'addGameWindow'
 27        uiscale = ba.app.ui.uiscale
 28        self._width = 750 if uiscale is ba.UIScale.SMALL else 650
 29        x_inset = 50 if uiscale is ba.UIScale.SMALL else 0
 30        self._height = (
 31            346
 32            if uiscale is ba.UIScale.SMALL
 33            else 380
 34            if uiscale is ba.UIScale.MEDIUM
 35            else 440
 36        )
 37        top_extra = 30 if uiscale is ba.UIScale.SMALL else 20
 38        self._scroll_width = 210
 39
 40        super().__init__(
 41            root_widget=ba.containerwidget(
 42                size=(self._width, self._height + top_extra),
 43                transition=transition,
 44                scale=(
 45                    2.17
 46                    if uiscale is ba.UIScale.SMALL
 47                    else 1.5
 48                    if uiscale is ba.UIScale.MEDIUM
 49                    else 1.0
 50                ),
 51                stack_offset=(0, 1) if uiscale is ba.UIScale.SMALL else (0, 0),
 52            )
 53        )
 54
 55        self._back_button = ba.buttonwidget(
 56            parent=self._root_widget,
 57            position=(58 + x_inset, self._height - 53),
 58            size=(165, 70),
 59            scale=0.75,
 60            text_scale=1.2,
 61            label=ba.Lstr(resource='backText'),
 62            autoselect=True,
 63            button_type='back',
 64            on_activate_call=self._back,
 65        )
 66        self._select_button = select_button = ba.buttonwidget(
 67            parent=self._root_widget,
 68            position=(self._width - (172 + x_inset), self._height - 50),
 69            autoselect=True,
 70            size=(160, 60),
 71            scale=0.75,
 72            text_scale=1.2,
 73            label=ba.Lstr(resource='selectText'),
 74            on_activate_call=self._add,
 75        )
 76
 77        if ba.app.ui.use_toolbars:
 78            ba.widget(
 79                edit=select_button,
 80                right_widget=ba.internal.get_special_widget('party_button'),
 81            )
 82
 83        ba.textwidget(
 84            parent=self._root_widget,
 85            position=(self._width * 0.5, self._height - 28),
 86            size=(0, 0),
 87            scale=1.0,
 88            text=ba.Lstr(resource=self._r + '.titleText'),
 89            h_align='center',
 90            color=ba.app.ui.title_color,
 91            maxwidth=250,
 92            v_align='center',
 93        )
 94        v = self._height - 64
 95
 96        self._selected_title_text = ba.textwidget(
 97            parent=self._root_widget,
 98            position=(x_inset + self._scroll_width + 50 + 30, v - 15),
 99            size=(0, 0),
100            scale=1.0,
101            color=(0.7, 1.0, 0.7, 1.0),
102            maxwidth=self._width - self._scroll_width - 150 - x_inset * 2,
103            h_align='left',
104            v_align='center',
105        )
106        v -= 30
107
108        self._selected_description_text = ba.textwidget(
109            parent=self._root_widget,
110            position=(x_inset + self._scroll_width + 50 + 30, v),
111            size=(0, 0),
112            scale=0.7,
113            color=(0.5, 0.8, 0.5, 1.0),
114            maxwidth=self._width - self._scroll_width - 150 - x_inset * 2,
115            h_align='left',
116        )
117
118        scroll_height = self._height - 100
119
120        v = self._height - 60
121
122        self._scrollwidget = ba.scrollwidget(
123            parent=self._root_widget,
124            position=(x_inset + 61, v - scroll_height),
125            size=(self._scroll_width, scroll_height),
126            highlight=False,
127        )
128        ba.widget(
129            edit=self._scrollwidget,
130            up_widget=self._back_button,
131            left_widget=self._back_button,
132            right_widget=select_button,
133        )
134        self._column: ba.Widget | None = None
135
136        v -= 35
137        ba.containerwidget(
138            edit=self._root_widget,
139            cancel_button=self._back_button,
140            start_button=select_button,
141        )
142        self._selected_game_type: type[ba.GameActivity] | None = None
143
144        ba.containerwidget(
145            edit=self._root_widget, selected_child=self._scrollwidget
146        )
147
148        self._game_types: list[type[ba.GameActivity]] = []
149
150        # Get actual games loading in the bg.
151        ba.app.meta.load_exported_classes(
152            ba.GameActivity,
153            self._on_game_types_loaded,
154            completion_cb_in_bg_thread=True,
155        )
156
157        # Refresh with our initial empty list. We'll refresh again once
158        # game loading is complete.
159        self._refresh()
160
161    def _on_game_types_loaded(
162        self, gametypes: list[type[ba.GameActivity]]
163    ) -> None:
164        from ba.internal import get_unowned_game_types
165
166        # We asked for a bg thread completion cb so we can do some
167        # filtering here in the bg thread where its not gonna cause hitches.
168        assert not ba.in_logic_thread()
169        sessiontype = self._editcontroller.get_session_type()
170        unowned = get_unowned_game_types()
171        self._game_types = [
172            gt
173            for gt in gametypes
174            if gt not in unowned and gt.supports_session_type(sessiontype)
175        ]
176
177        # Sort in the current language.
178        self._game_types.sort(key=lambda g: g.get_display_string().evaluate())
179
180        # Tell ourself to refresh back in the logic thread.
181        ba.pushcall(self._refresh, from_other_thread=True)
182
183    def _refresh(self, select_get_more_games_button: bool = False) -> None:
184        # from ba.internal import get_game_types
185
186        if self._column is not None:
187            self._column.delete()
188
189        self._column = ba.columnwidget(
190            parent=self._scrollwidget, border=2, margin=0
191        )
192
193        for i, gametype in enumerate(self._game_types):
194
195            def _doit() -> None:
196                if self._select_button:
197                    ba.timer(
198                        0.1,
199                        self._select_button.activate,
200                        timetype=ba.TimeType.REAL,
201                    )
202
203            txt = ba.textwidget(
204                parent=self._column,
205                position=(0, 0),
206                size=(self._width - 88, 24),
207                text=gametype.get_display_string(),
208                h_align='left',
209                v_align='center',
210                color=(0.8, 0.8, 0.8, 1.0),
211                maxwidth=self._scroll_width * 0.8,
212                on_select_call=ba.Call(self._set_selected_game_type, gametype),
213                always_highlight=True,
214                selectable=True,
215                on_activate_call=_doit,
216            )
217            if i == 0:
218                ba.widget(edit=txt, up_widget=self._back_button)
219
220        self._get_more_games_button = ba.buttonwidget(
221            parent=self._column,
222            autoselect=True,
223            label=ba.Lstr(resource=self._r + '.getMoreGamesText'),
224            color=(0.54, 0.52, 0.67),
225            textcolor=(0.7, 0.65, 0.7),
226            on_activate_call=self._on_get_more_games_press,
227            size=(178, 50),
228        )
229        if select_get_more_games_button:
230            ba.containerwidget(
231                edit=self._column,
232                selected_child=self._get_more_games_button,
233                visible_child=self._get_more_games_button,
234            )
235
236    def _on_get_more_games_press(self) -> None:
237        from bastd.ui.account import show_sign_in_prompt
238        from bastd.ui.store.browser import StoreBrowserWindow
239
240        if ba.internal.get_v1_account_state() != 'signed_in':
241            show_sign_in_prompt()
242            return
243        StoreBrowserWindow(
244            modal=True,
245            show_tab=StoreBrowserWindow.TabID.MINIGAMES,
246            on_close_call=self._on_store_close,
247            origin_widget=self._get_more_games_button,
248        )
249
250    def _on_store_close(self) -> None:
251        self._refresh(select_get_more_games_button=True)
252
253    def _add(self) -> None:
254        ba.internal.lock_all_input()  # Make sure no more commands happen.
255        ba.timer(0.1, ba.internal.unlock_all_input, timetype=ba.TimeType.REAL)
256        assert self._selected_game_type is not None
257        self._editcontroller.add_game_type_selected(self._selected_game_type)
258
259    def _set_selected_game_type(self, gametype: type[ba.GameActivity]) -> None:
260        self._selected_game_type = gametype
261        ba.textwidget(
262            edit=self._selected_title_text, text=gametype.get_display_string()
263        )
264        ba.textwidget(
265            edit=self._selected_description_text,
266            text=gametype.get_description_display_string(
267                self._editcontroller.get_session_type()
268            ),
269        )
270
271    def _back(self) -> None:
272        self._editcontroller.add_game_cancelled()

Window for selecting a game type to add to a playlist.

PlaylistAddGameWindow( editcontroller: bastd.ui.playlist.editcontroller.PlaylistEditController, transition: str = 'in_right')
 20    def __init__(
 21        self,
 22        editcontroller: PlaylistEditController,
 23        transition: str = 'in_right',
 24    ):
 25        self._editcontroller = editcontroller
 26        self._r = 'addGameWindow'
 27        uiscale = ba.app.ui.uiscale
 28        self._width = 750 if uiscale is ba.UIScale.SMALL else 650
 29        x_inset = 50 if uiscale is ba.UIScale.SMALL else 0
 30        self._height = (
 31            346
 32            if uiscale is ba.UIScale.SMALL
 33            else 380
 34            if uiscale is ba.UIScale.MEDIUM
 35            else 440
 36        )
 37        top_extra = 30 if uiscale is ba.UIScale.SMALL else 20
 38        self._scroll_width = 210
 39
 40        super().__init__(
 41            root_widget=ba.containerwidget(
 42                size=(self._width, self._height + top_extra),
 43                transition=transition,
 44                scale=(
 45                    2.17
 46                    if uiscale is ba.UIScale.SMALL
 47                    else 1.5
 48                    if uiscale is ba.UIScale.MEDIUM
 49                    else 1.0
 50                ),
 51                stack_offset=(0, 1) if uiscale is ba.UIScale.SMALL else (0, 0),
 52            )
 53        )
 54
 55        self._back_button = ba.buttonwidget(
 56            parent=self._root_widget,
 57            position=(58 + x_inset, self._height - 53),
 58            size=(165, 70),
 59            scale=0.75,
 60            text_scale=1.2,
 61            label=ba.Lstr(resource='backText'),
 62            autoselect=True,
 63            button_type='back',
 64            on_activate_call=self._back,
 65        )
 66        self._select_button = select_button = ba.buttonwidget(
 67            parent=self._root_widget,
 68            position=(self._width - (172 + x_inset), self._height - 50),
 69            autoselect=True,
 70            size=(160, 60),
 71            scale=0.75,
 72            text_scale=1.2,
 73            label=ba.Lstr(resource='selectText'),
 74            on_activate_call=self._add,
 75        )
 76
 77        if ba.app.ui.use_toolbars:
 78            ba.widget(
 79                edit=select_button,
 80                right_widget=ba.internal.get_special_widget('party_button'),
 81            )
 82
 83        ba.textwidget(
 84            parent=self._root_widget,
 85            position=(self._width * 0.5, self._height - 28),
 86            size=(0, 0),
 87            scale=1.0,
 88            text=ba.Lstr(resource=self._r + '.titleText'),
 89            h_align='center',
 90            color=ba.app.ui.title_color,
 91            maxwidth=250,
 92            v_align='center',
 93        )
 94        v = self._height - 64
 95
 96        self._selected_title_text = ba.textwidget(
 97            parent=self._root_widget,
 98            position=(x_inset + self._scroll_width + 50 + 30, v - 15),
 99            size=(0, 0),
100            scale=1.0,
101            color=(0.7, 1.0, 0.7, 1.0),
102            maxwidth=self._width - self._scroll_width - 150 - x_inset * 2,
103            h_align='left',
104            v_align='center',
105        )
106        v -= 30
107
108        self._selected_description_text = ba.textwidget(
109            parent=self._root_widget,
110            position=(x_inset + self._scroll_width + 50 + 30, v),
111            size=(0, 0),
112            scale=0.7,
113            color=(0.5, 0.8, 0.5, 1.0),
114            maxwidth=self._width - self._scroll_width - 150 - x_inset * 2,
115            h_align='left',
116        )
117
118        scroll_height = self._height - 100
119
120        v = self._height - 60
121
122        self._scrollwidget = ba.scrollwidget(
123            parent=self._root_widget,
124            position=(x_inset + 61, v - scroll_height),
125            size=(self._scroll_width, scroll_height),
126            highlight=False,
127        )
128        ba.widget(
129            edit=self._scrollwidget,
130            up_widget=self._back_button,
131            left_widget=self._back_button,
132            right_widget=select_button,
133        )
134        self._column: ba.Widget | None = None
135
136        v -= 35
137        ba.containerwidget(
138            edit=self._root_widget,
139            cancel_button=self._back_button,
140            start_button=select_button,
141        )
142        self._selected_game_type: type[ba.GameActivity] | None = None
143
144        ba.containerwidget(
145            edit=self._root_widget, selected_child=self._scrollwidget
146        )
147
148        self._game_types: list[type[ba.GameActivity]] = []
149
150        # Get actual games loading in the bg.
151        ba.app.meta.load_exported_classes(
152            ba.GameActivity,
153            self._on_game_types_loaded,
154            completion_cb_in_bg_thread=True,
155        )
156
157        # Refresh with our initial empty list. We'll refresh again once
158        # game loading is complete.
159        self._refresh()
Inherited Members
ba.ui.Window
get_root_widget