bauiv1lib.chest

Provides chest related ui.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Provides chest related ui."""
  4
  5from __future__ import annotations
  6
  7from typing import override, TYPE_CHECKING
  8
  9import bacommon.cloud
 10import bauiv1 as bui
 11
 12if TYPE_CHECKING:
 13    pass
 14
 15
 16class ChestWindow(bui.MainWindow):
 17    """Allows operations on a chest."""
 18
 19    # def __del__(self) -> None:
 20    #     print('~ChestWindow()')
 21
 22    def __init__(
 23        self,
 24        index: int,
 25        transition: str | None = 'in_right',
 26        origin_widget: bui.Widget | None = None,
 27    ):
 28        # print('ChestWindow()')
 29
 30        self._index = index
 31
 32        assert bui.app.classic is not None
 33        uiscale = bui.app.ui_v1.uiscale
 34        self._width = 1050 if uiscale is bui.UIScale.SMALL else 850
 35        self._height = (
 36            500
 37            if uiscale is bui.UIScale.SMALL
 38            else 500 if uiscale is bui.UIScale.MEDIUM else 500
 39        )
 40        self._xoffs = 70 if uiscale is bui.UIScale.SMALL else 0
 41        self._yoffs = -42 if uiscale is bui.UIScale.SMALL else -25
 42        self._action_in_flight = False
 43        self._open_now_button: bui.Widget | None = None
 44        self._watch_ad_button: bui.Widget | None = None
 45
 46        # The set of widgets we keep when doing a clear.
 47        self._core_widgets: list[bui.Widget] = []
 48
 49        super().__init__(
 50            root_widget=bui.containerwidget(
 51                size=(self._width, self._height),
 52                toolbar_visibility='menu_full',
 53                scale=(
 54                    1.55
 55                    if uiscale is bui.UIScale.SMALL
 56                    else 1.1 if uiscale is bui.UIScale.MEDIUM else 0.9
 57                ),
 58                stack_offset=(
 59                    (0, 0)
 60                    if uiscale is bui.UIScale.SMALL
 61                    else (0, 15) if uiscale is bui.UIScale.MEDIUM else (0, 0)
 62                ),
 63            ),
 64            transition=transition,
 65            origin_widget=origin_widget,
 66        )
 67
 68        self._title_text = bui.textwidget(
 69            parent=self._root_widget,
 70            position=(0, self._height - 45 + self._yoffs),
 71            size=(self._width, 25),
 72            text=f'Chest Slot {self._index + 1}',
 73            color=bui.app.ui_v1.title_color,
 74            maxwidth=150.0,
 75            h_align='center',
 76            v_align='center',
 77        )
 78        self._core_widgets.append(self._title_text)
 79
 80        if uiscale is bui.UIScale.SMALL:
 81            bui.containerwidget(
 82                edit=self._root_widget, on_cancel_call=self.main_window_back
 83            )
 84        else:
 85            btn = bui.buttonwidget(
 86                parent=self._root_widget,
 87                position=(self._xoffs + 50, self._height - 55 + self._yoffs),
 88                size=(60, 55),
 89                scale=0.8,
 90                label=bui.charstr(bui.SpecialChar.BACK),
 91                button_type='backSmall',
 92                extra_touch_border_scale=2.0,
 93                autoselect=True,
 94                on_activate_call=self.main_window_back,
 95            )
 96            bui.containerwidget(edit=self._root_widget, cancel_button=btn)
 97            self._core_widgets.append(btn)
 98
 99        self._infotext = bui.textwidget(
100            parent=self._root_widget,
101            position=(self._width * 0.5, self._height - 200 + self._yoffs),
102            size=(0, 0),
103            text=bui.Lstr(resource='loadingText'),
104            maxwidth=700,
105            scale=0.7,
106            color=(0.6, 0.5, 0.6),
107            h_align='center',
108            v_align='center',
109        )
110        self._core_widgets.append(self._infotext)
111
112        plus = bui.app.plus
113        if plus is None:
114            self._error('Plus feature-set is not present.')
115            return
116
117        if plus.accounts.primary is None:
118            self._error(bui.Lstr(resource='notSignedInText'))
119            return
120
121        # Start by showing info/options for our target chest. Note that
122        # we always ask the server for these values even though we may
123        # have them through our appmode subscription which updates the
124        # chest UI. This is because the wait_for_connectivity()
125        # mechanism will often bring our window up a split second before
126        # the chest subscription receives its first values which would
127        # lead us to incorrectly think there is no chest there. If we
128        # want to optimize this in the future we could perhaps use local
129        # values only if there is a chest present in them.
130        assert not self._action_in_flight
131        self._action_in_flight = True
132        with plus.accounts.primary:
133            plus.cloud.send_message_cb(
134                bacommon.cloud.BSChestInfoMessage(chest_id=str(self._index)),
135                on_response=bui.WeakCall(self._on_chest_info_response),
136            )
137
138    def _on_chest_info_response(
139        self, response: bacommon.cloud.BSChestInfoResponse | Exception
140    ) -> None:
141        assert self._action_in_flight  # Should be us.
142        self._action_in_flight = False
143
144        if isinstance(response, Exception):
145            self._error(
146                bui.Lstr(resource='internal.unavailableNoConnectionText')
147            )
148            return
149
150        if response.chest is None:
151            self._show_about_chest_slots()
152            return
153
154        self.show_chest_actions(response.chest)
155
156    def _on_chest_action_response(
157        self, response: bacommon.cloud.BSChestActionResponse | Exception
158    ) -> None:
159        assert self._action_in_flight  # Should be us.
160        self._action_in_flight = False
161
162        # Communication/local error:
163        if isinstance(response, Exception):
164            self._error(
165                bui.Lstr(resource='internal.unavailableNoConnectionText')
166            )
167            return
168
169        # Server-side error:
170        if response.error is not None:
171            self._error(bui.Lstr(translate=('serverResponses', response.error)))
172            return
173
174        # If there's contents listed in the response, show them.
175        if response.contents is not None:
176            print('WOULD SHOW CONTENTS:', response.contents)
177        else:
178            # Otherwise we're done here; just close out our UI.
179            self.main_window_back()
180
181    def show_chest_actions(
182        self, chest: bacommon.cloud.BSChestInfoResponse.Chest
183    ) -> None:
184        """Show state for our chest."""
185        # pylint: disable=cyclic-import
186        from baclassic import ClassicAppMode
187
188        # We expect to be run under classic.
189        mode = bui.app.mode
190        if not isinstance(mode, ClassicAppMode):
191            self._error('Classic app mode not active.')
192            return
193
194        now = bui.utc_now_cloud()
195        secs_till_open = max(0.0, (chest.unlock_time - now).total_seconds())
196        tstr = bui.timestring(secs_till_open, centi=False)
197
198        bui.textwidget(
199            parent=self._root_widget,
200            position=(self._width * 0.5, self._height - 120 + self._yoffs),
201            size=(0, 0),
202            text=tstr,
203            maxwidth=700,
204            scale=0.7,
205            color=(0.6, 0.5, 0.6),
206            h_align='center',
207            v_align='center',
208        )
209        self._open_now_button = bui.buttonwidget(
210            parent=self._root_widget,
211            position=(
212                self._width * 0.5 - 200,
213                self._height - 250 + self._yoffs,
214            ),
215            size=(150, 100),
216            label=f'OPEN NOW FOR {chest.unlock_tokens} TOKENS',
217            button_type='square',
218            autoselect=True,
219            on_activate_call=bui.WeakCall(
220                self._open_now_press, chest.unlock_tokens
221            ),
222        )
223
224        self._watch_ad_button = bui.buttonwidget(
225            parent=self._root_widget,
226            position=(
227                self._width * 0.5 + 50,
228                self._height - 250 + self._yoffs,
229            ),
230            size=(150, 100),
231            label='WATCH AN AD TO REDUCE WAIT',
232            button_type='square',
233            autoselect=True,
234            on_activate_call=bui.WeakCall(self._watch_ad_press),
235        )
236        bui.textwidget(edit=self._infotext, text='')
237
238    def _open_now_press(self, token_payment: int) -> None:
239
240        # Allow only one in-flight action at once.
241        if self._action_in_flight:
242            bui.screenmessage(
243                bui.Lstr(resource='pleaseWaitText'), color=(1, 0, 0)
244            )
245            bui.getsound('error').play()
246            return
247
248        plus = bui.app.plus
249        assert plus is not None
250
251        if plus.accounts.primary is None:
252            self._error(bui.Lstr(resource='notSignedInText'))
253            return
254
255        self._action_in_flight = True
256        with plus.accounts.primary:
257            plus.cloud.send_message_cb(
258                bacommon.cloud.BSChestActionMessage(
259                    chest_id=str(self._index),
260                    action=bacommon.cloud.BSChestActionMessage.Action.UNLOCK,
261                    token_payment=token_payment,
262                ),
263                on_response=bui.WeakCall(self._on_chest_action_response),
264            )
265
266        # Convey that something is in progress.
267        if self._open_now_button:
268            bui.buttonwidget(edit=self._open_now_button, label='...')
269
270    def _watch_ad_press(self) -> None:
271
272        # Allow only one in-flight action at once.
273        if self._action_in_flight:
274            bui.screenmessage(
275                bui.Lstr(resource='pleaseWaitText'), color=(1, 0, 0)
276            )
277            bui.getsound('error').play()
278            return
279
280        plus = bui.app.plus
281        assert plus is not None
282
283        if plus.accounts.primary is None:
284            self._error(bui.Lstr(resource='notSignedInText'))
285            return
286
287        self._action_in_flight = True
288        with plus.accounts.primary:
289            plus.cloud.send_message_cb(
290                bacommon.cloud.BSChestActionMessage(
291                    chest_id=str(self._index),
292                    action=bacommon.cloud.BSChestActionMessage.Action.AD,
293                    token_payment=0,
294                ),
295                on_response=bui.WeakCall(self._on_chest_action_response),
296            )
297
298        # Convey that something is in progress.
299        if self._watch_ad_button:
300            bui.buttonwidget(edit=self._watch_ad_button, label='...')
301
302    def _reset(self) -> None:
303        """Clear all non-permanent widgets."""
304        for widget in self._root_widget.get_children():
305            if widget not in self._core_widgets:
306                widget.delete()
307
308    def _error(self, msg: str | bui.Lstr) -> None:
309        """Put ourself in an error state with a visible error message."""
310        self._reset()
311        bui.textwidget(edit=self._infotext, text=msg, color=(1, 0, 0))
312
313    def _show_about_chest_slots(self) -> None:
314        self._reset()
315        msg = (
316            'This empty slot can hold a treasure chest.\n'
317            'Treasure chests are earned through gameplay.'
318        )
319        bui.textwidget(edit=self._infotext, text=msg, color=(1, 1, 1))
320
321    @override
322    def get_main_window_state(self) -> bui.MainWindowState:
323        # Support recreating our window for back/refresh purposes.
324        cls = type(self)
325
326        # Pull anything we need from self out here; if we do it in the
327        # lambda we keep self alive which is bad.
328        index = self._index
329
330        return bui.BasicMainWindowState(
331            create_call=lambda transition, origin_widget: cls(
332                index=index, transition=transition, origin_widget=origin_widget
333            )
334        )
335
336
337# Slight hack: we define different classes for our different chest slots
338# so that the default UI behavior is to replace each other when
339# different ones are pressed. If they are all the same class then the
340# default behavior for such presses is to toggle the existing one back
341# off.
342
343
344class ChestWindow0(ChestWindow):
345    """Child class of ChestWindow for slighty hackish reasons."""
346
347
348class ChestWindow1(ChestWindow):
349    """Child class of ChestWindow for slighty hackish reasons."""
350
351
352class ChestWindow2(ChestWindow):
353    """Child class of ChestWindow for slighty hackish reasons."""
354
355
356class ChestWindow3(ChestWindow):
357    """Child class of ChestWindow for slighty hackish reasons."""
class ChestWindow(bauiv1._uitypes.MainWindow):
 17class ChestWindow(bui.MainWindow):
 18    """Allows operations on a chest."""
 19
 20    # def __del__(self) -> None:
 21    #     print('~ChestWindow()')
 22
 23    def __init__(
 24        self,
 25        index: int,
 26        transition: str | None = 'in_right',
 27        origin_widget: bui.Widget | None = None,
 28    ):
 29        # print('ChestWindow()')
 30
 31        self._index = index
 32
 33        assert bui.app.classic is not None
 34        uiscale = bui.app.ui_v1.uiscale
 35        self._width = 1050 if uiscale is bui.UIScale.SMALL else 850
 36        self._height = (
 37            500
 38            if uiscale is bui.UIScale.SMALL
 39            else 500 if uiscale is bui.UIScale.MEDIUM else 500
 40        )
 41        self._xoffs = 70 if uiscale is bui.UIScale.SMALL else 0
 42        self._yoffs = -42 if uiscale is bui.UIScale.SMALL else -25
 43        self._action_in_flight = False
 44        self._open_now_button: bui.Widget | None = None
 45        self._watch_ad_button: bui.Widget | None = None
 46
 47        # The set of widgets we keep when doing a clear.
 48        self._core_widgets: list[bui.Widget] = []
 49
 50        super().__init__(
 51            root_widget=bui.containerwidget(
 52                size=(self._width, self._height),
 53                toolbar_visibility='menu_full',
 54                scale=(
 55                    1.55
 56                    if uiscale is bui.UIScale.SMALL
 57                    else 1.1 if uiscale is bui.UIScale.MEDIUM else 0.9
 58                ),
 59                stack_offset=(
 60                    (0, 0)
 61                    if uiscale is bui.UIScale.SMALL
 62                    else (0, 15) if uiscale is bui.UIScale.MEDIUM else (0, 0)
 63                ),
 64            ),
 65            transition=transition,
 66            origin_widget=origin_widget,
 67        )
 68
 69        self._title_text = bui.textwidget(
 70            parent=self._root_widget,
 71            position=(0, self._height - 45 + self._yoffs),
 72            size=(self._width, 25),
 73            text=f'Chest Slot {self._index + 1}',
 74            color=bui.app.ui_v1.title_color,
 75            maxwidth=150.0,
 76            h_align='center',
 77            v_align='center',
 78        )
 79        self._core_widgets.append(self._title_text)
 80
 81        if uiscale is bui.UIScale.SMALL:
 82            bui.containerwidget(
 83                edit=self._root_widget, on_cancel_call=self.main_window_back
 84            )
 85        else:
 86            btn = bui.buttonwidget(
 87                parent=self._root_widget,
 88                position=(self._xoffs + 50, self._height - 55 + self._yoffs),
 89                size=(60, 55),
 90                scale=0.8,
 91                label=bui.charstr(bui.SpecialChar.BACK),
 92                button_type='backSmall',
 93                extra_touch_border_scale=2.0,
 94                autoselect=True,
 95                on_activate_call=self.main_window_back,
 96            )
 97            bui.containerwidget(edit=self._root_widget, cancel_button=btn)
 98            self._core_widgets.append(btn)
 99
100        self._infotext = bui.textwidget(
101            parent=self._root_widget,
102            position=(self._width * 0.5, self._height - 200 + self._yoffs),
103            size=(0, 0),
104            text=bui.Lstr(resource='loadingText'),
105            maxwidth=700,
106            scale=0.7,
107            color=(0.6, 0.5, 0.6),
108            h_align='center',
109            v_align='center',
110        )
111        self._core_widgets.append(self._infotext)
112
113        plus = bui.app.plus
114        if plus is None:
115            self._error('Plus feature-set is not present.')
116            return
117
118        if plus.accounts.primary is None:
119            self._error(bui.Lstr(resource='notSignedInText'))
120            return
121
122        # Start by showing info/options for our target chest. Note that
123        # we always ask the server for these values even though we may
124        # have them through our appmode subscription which updates the
125        # chest UI. This is because the wait_for_connectivity()
126        # mechanism will often bring our window up a split second before
127        # the chest subscription receives its first values which would
128        # lead us to incorrectly think there is no chest there. If we
129        # want to optimize this in the future we could perhaps use local
130        # values only if there is a chest present in them.
131        assert not self._action_in_flight
132        self._action_in_flight = True
133        with plus.accounts.primary:
134            plus.cloud.send_message_cb(
135                bacommon.cloud.BSChestInfoMessage(chest_id=str(self._index)),
136                on_response=bui.WeakCall(self._on_chest_info_response),
137            )
138
139    def _on_chest_info_response(
140        self, response: bacommon.cloud.BSChestInfoResponse | Exception
141    ) -> None:
142        assert self._action_in_flight  # Should be us.
143        self._action_in_flight = False
144
145        if isinstance(response, Exception):
146            self._error(
147                bui.Lstr(resource='internal.unavailableNoConnectionText')
148            )
149            return
150
151        if response.chest is None:
152            self._show_about_chest_slots()
153            return
154
155        self.show_chest_actions(response.chest)
156
157    def _on_chest_action_response(
158        self, response: bacommon.cloud.BSChestActionResponse | Exception
159    ) -> None:
160        assert self._action_in_flight  # Should be us.
161        self._action_in_flight = False
162
163        # Communication/local error:
164        if isinstance(response, Exception):
165            self._error(
166                bui.Lstr(resource='internal.unavailableNoConnectionText')
167            )
168            return
169
170        # Server-side error:
171        if response.error is not None:
172            self._error(bui.Lstr(translate=('serverResponses', response.error)))
173            return
174
175        # If there's contents listed in the response, show them.
176        if response.contents is not None:
177            print('WOULD SHOW CONTENTS:', response.contents)
178        else:
179            # Otherwise we're done here; just close out our UI.
180            self.main_window_back()
181
182    def show_chest_actions(
183        self, chest: bacommon.cloud.BSChestInfoResponse.Chest
184    ) -> None:
185        """Show state for our chest."""
186        # pylint: disable=cyclic-import
187        from baclassic import ClassicAppMode
188
189        # We expect to be run under classic.
190        mode = bui.app.mode
191        if not isinstance(mode, ClassicAppMode):
192            self._error('Classic app mode not active.')
193            return
194
195        now = bui.utc_now_cloud()
196        secs_till_open = max(0.0, (chest.unlock_time - now).total_seconds())
197        tstr = bui.timestring(secs_till_open, centi=False)
198
199        bui.textwidget(
200            parent=self._root_widget,
201            position=(self._width * 0.5, self._height - 120 + self._yoffs),
202            size=(0, 0),
203            text=tstr,
204            maxwidth=700,
205            scale=0.7,
206            color=(0.6, 0.5, 0.6),
207            h_align='center',
208            v_align='center',
209        )
210        self._open_now_button = bui.buttonwidget(
211            parent=self._root_widget,
212            position=(
213                self._width * 0.5 - 200,
214                self._height - 250 + self._yoffs,
215            ),
216            size=(150, 100),
217            label=f'OPEN NOW FOR {chest.unlock_tokens} TOKENS',
218            button_type='square',
219            autoselect=True,
220            on_activate_call=bui.WeakCall(
221                self._open_now_press, chest.unlock_tokens
222            ),
223        )
224
225        self._watch_ad_button = bui.buttonwidget(
226            parent=self._root_widget,
227            position=(
228                self._width * 0.5 + 50,
229                self._height - 250 + self._yoffs,
230            ),
231            size=(150, 100),
232            label='WATCH AN AD TO REDUCE WAIT',
233            button_type='square',
234            autoselect=True,
235            on_activate_call=bui.WeakCall(self._watch_ad_press),
236        )
237        bui.textwidget(edit=self._infotext, text='')
238
239    def _open_now_press(self, token_payment: int) -> None:
240
241        # Allow only one in-flight action at once.
242        if self._action_in_flight:
243            bui.screenmessage(
244                bui.Lstr(resource='pleaseWaitText'), color=(1, 0, 0)
245            )
246            bui.getsound('error').play()
247            return
248
249        plus = bui.app.plus
250        assert plus is not None
251
252        if plus.accounts.primary is None:
253            self._error(bui.Lstr(resource='notSignedInText'))
254            return
255
256        self._action_in_flight = True
257        with plus.accounts.primary:
258            plus.cloud.send_message_cb(
259                bacommon.cloud.BSChestActionMessage(
260                    chest_id=str(self._index),
261                    action=bacommon.cloud.BSChestActionMessage.Action.UNLOCK,
262                    token_payment=token_payment,
263                ),
264                on_response=bui.WeakCall(self._on_chest_action_response),
265            )
266
267        # Convey that something is in progress.
268        if self._open_now_button:
269            bui.buttonwidget(edit=self._open_now_button, label='...')
270
271    def _watch_ad_press(self) -> None:
272
273        # Allow only one in-flight action at once.
274        if self._action_in_flight:
275            bui.screenmessage(
276                bui.Lstr(resource='pleaseWaitText'), color=(1, 0, 0)
277            )
278            bui.getsound('error').play()
279            return
280
281        plus = bui.app.plus
282        assert plus is not None
283
284        if plus.accounts.primary is None:
285            self._error(bui.Lstr(resource='notSignedInText'))
286            return
287
288        self._action_in_flight = True
289        with plus.accounts.primary:
290            plus.cloud.send_message_cb(
291                bacommon.cloud.BSChestActionMessage(
292                    chest_id=str(self._index),
293                    action=bacommon.cloud.BSChestActionMessage.Action.AD,
294                    token_payment=0,
295                ),
296                on_response=bui.WeakCall(self._on_chest_action_response),
297            )
298
299        # Convey that something is in progress.
300        if self._watch_ad_button:
301            bui.buttonwidget(edit=self._watch_ad_button, label='...')
302
303    def _reset(self) -> None:
304        """Clear all non-permanent widgets."""
305        for widget in self._root_widget.get_children():
306            if widget not in self._core_widgets:
307                widget.delete()
308
309    def _error(self, msg: str | bui.Lstr) -> None:
310        """Put ourself in an error state with a visible error message."""
311        self._reset()
312        bui.textwidget(edit=self._infotext, text=msg, color=(1, 0, 0))
313
314    def _show_about_chest_slots(self) -> None:
315        self._reset()
316        msg = (
317            'This empty slot can hold a treasure chest.\n'
318            'Treasure chests are earned through gameplay.'
319        )
320        bui.textwidget(edit=self._infotext, text=msg, color=(1, 1, 1))
321
322    @override
323    def get_main_window_state(self) -> bui.MainWindowState:
324        # Support recreating our window for back/refresh purposes.
325        cls = type(self)
326
327        # Pull anything we need from self out here; if we do it in the
328        # lambda we keep self alive which is bad.
329        index = self._index
330
331        return bui.BasicMainWindowState(
332            create_call=lambda transition, origin_widget: cls(
333                index=index, transition=transition, origin_widget=origin_widget
334            )
335        )

Allows operations on a chest.

ChestWindow( index: int, transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
 23    def __init__(
 24        self,
 25        index: int,
 26        transition: str | None = 'in_right',
 27        origin_widget: bui.Widget | None = None,
 28    ):
 29        # print('ChestWindow()')
 30
 31        self._index = index
 32
 33        assert bui.app.classic is not None
 34        uiscale = bui.app.ui_v1.uiscale
 35        self._width = 1050 if uiscale is bui.UIScale.SMALL else 850
 36        self._height = (
 37            500
 38            if uiscale is bui.UIScale.SMALL
 39            else 500 if uiscale is bui.UIScale.MEDIUM else 500
 40        )
 41        self._xoffs = 70 if uiscale is bui.UIScale.SMALL else 0
 42        self._yoffs = -42 if uiscale is bui.UIScale.SMALL else -25
 43        self._action_in_flight = False
 44        self._open_now_button: bui.Widget | None = None
 45        self._watch_ad_button: bui.Widget | None = None
 46
 47        # The set of widgets we keep when doing a clear.
 48        self._core_widgets: list[bui.Widget] = []
 49
 50        super().__init__(
 51            root_widget=bui.containerwidget(
 52                size=(self._width, self._height),
 53                toolbar_visibility='menu_full',
 54                scale=(
 55                    1.55
 56                    if uiscale is bui.UIScale.SMALL
 57                    else 1.1 if uiscale is bui.UIScale.MEDIUM else 0.9
 58                ),
 59                stack_offset=(
 60                    (0, 0)
 61                    if uiscale is bui.UIScale.SMALL
 62                    else (0, 15) if uiscale is bui.UIScale.MEDIUM else (0, 0)
 63                ),
 64            ),
 65            transition=transition,
 66            origin_widget=origin_widget,
 67        )
 68
 69        self._title_text = bui.textwidget(
 70            parent=self._root_widget,
 71            position=(0, self._height - 45 + self._yoffs),
 72            size=(self._width, 25),
 73            text=f'Chest Slot {self._index + 1}',
 74            color=bui.app.ui_v1.title_color,
 75            maxwidth=150.0,
 76            h_align='center',
 77            v_align='center',
 78        )
 79        self._core_widgets.append(self._title_text)
 80
 81        if uiscale is bui.UIScale.SMALL:
 82            bui.containerwidget(
 83                edit=self._root_widget, on_cancel_call=self.main_window_back
 84            )
 85        else:
 86            btn = bui.buttonwidget(
 87                parent=self._root_widget,
 88                position=(self._xoffs + 50, self._height - 55 + self._yoffs),
 89                size=(60, 55),
 90                scale=0.8,
 91                label=bui.charstr(bui.SpecialChar.BACK),
 92                button_type='backSmall',
 93                extra_touch_border_scale=2.0,
 94                autoselect=True,
 95                on_activate_call=self.main_window_back,
 96            )
 97            bui.containerwidget(edit=self._root_widget, cancel_button=btn)
 98            self._core_widgets.append(btn)
 99
100        self._infotext = bui.textwidget(
101            parent=self._root_widget,
102            position=(self._width * 0.5, self._height - 200 + self._yoffs),
103            size=(0, 0),
104            text=bui.Lstr(resource='loadingText'),
105            maxwidth=700,
106            scale=0.7,
107            color=(0.6, 0.5, 0.6),
108            h_align='center',
109            v_align='center',
110        )
111        self._core_widgets.append(self._infotext)
112
113        plus = bui.app.plus
114        if plus is None:
115            self._error('Plus feature-set is not present.')
116            return
117
118        if plus.accounts.primary is None:
119            self._error(bui.Lstr(resource='notSignedInText'))
120            return
121
122        # Start by showing info/options for our target chest. Note that
123        # we always ask the server for these values even though we may
124        # have them through our appmode subscription which updates the
125        # chest UI. This is because the wait_for_connectivity()
126        # mechanism will often bring our window up a split second before
127        # the chest subscription receives its first values which would
128        # lead us to incorrectly think there is no chest there. If we
129        # want to optimize this in the future we could perhaps use local
130        # values only if there is a chest present in them.
131        assert not self._action_in_flight
132        self._action_in_flight = True
133        with plus.accounts.primary:
134            plus.cloud.send_message_cb(
135                bacommon.cloud.BSChestInfoMessage(chest_id=str(self._index)),
136                on_response=bui.WeakCall(self._on_chest_info_response),
137            )

Create a MainWindow given a root widget and transition info.

Automatically handles in and out transitions on the provided widget, so there is no need to set transitions when creating it.

def show_chest_actions(self, chest: bacommon.cloud.BSChestInfoResponse.Chest) -> None:
182    def show_chest_actions(
183        self, chest: bacommon.cloud.BSChestInfoResponse.Chest
184    ) -> None:
185        """Show state for our chest."""
186        # pylint: disable=cyclic-import
187        from baclassic import ClassicAppMode
188
189        # We expect to be run under classic.
190        mode = bui.app.mode
191        if not isinstance(mode, ClassicAppMode):
192            self._error('Classic app mode not active.')
193            return
194
195        now = bui.utc_now_cloud()
196        secs_till_open = max(0.0, (chest.unlock_time - now).total_seconds())
197        tstr = bui.timestring(secs_till_open, centi=False)
198
199        bui.textwidget(
200            parent=self._root_widget,
201            position=(self._width * 0.5, self._height - 120 + self._yoffs),
202            size=(0, 0),
203            text=tstr,
204            maxwidth=700,
205            scale=0.7,
206            color=(0.6, 0.5, 0.6),
207            h_align='center',
208            v_align='center',
209        )
210        self._open_now_button = bui.buttonwidget(
211            parent=self._root_widget,
212            position=(
213                self._width * 0.5 - 200,
214                self._height - 250 + self._yoffs,
215            ),
216            size=(150, 100),
217            label=f'OPEN NOW FOR {chest.unlock_tokens} TOKENS',
218            button_type='square',
219            autoselect=True,
220            on_activate_call=bui.WeakCall(
221                self._open_now_press, chest.unlock_tokens
222            ),
223        )
224
225        self._watch_ad_button = bui.buttonwidget(
226            parent=self._root_widget,
227            position=(
228                self._width * 0.5 + 50,
229                self._height - 250 + self._yoffs,
230            ),
231            size=(150, 100),
232            label='WATCH AN AD TO REDUCE WAIT',
233            button_type='square',
234            autoselect=True,
235            on_activate_call=bui.WeakCall(self._watch_ad_press),
236        )
237        bui.textwidget(edit=self._infotext, text='')

Show state for our chest.

@override
def get_main_window_state(self) -> bauiv1.MainWindowState:
322    @override
323    def get_main_window_state(self) -> bui.MainWindowState:
324        # Support recreating our window for back/refresh purposes.
325        cls = type(self)
326
327        # Pull anything we need from self out here; if we do it in the
328        # lambda we keep self alive which is bad.
329        index = self._index
330
331        return bui.BasicMainWindowState(
332            create_call=lambda transition, origin_widget: cls(
333                index=index, transition=transition, origin_widget=origin_widget
334            )
335        )

Return a WindowState to recreate this window, if supported.

class ChestWindow0(ChestWindow):
345class ChestWindow0(ChestWindow):
346    """Child class of ChestWindow for slighty hackish reasons."""

Child class of ChestWindow for slighty hackish reasons.

class ChestWindow1(ChestWindow):
349class ChestWindow1(ChestWindow):
350    """Child class of ChestWindow for slighty hackish reasons."""

Child class of ChestWindow for slighty hackish reasons.

class ChestWindow2(ChestWindow):
353class ChestWindow2(ChestWindow):
354    """Child class of ChestWindow for slighty hackish reasons."""

Child class of ChestWindow for slighty hackish reasons.

class ChestWindow3(ChestWindow):
357class ChestWindow3(ChestWindow):
358    """Child class of ChestWindow for slighty hackish reasons."""

Child class of ChestWindow for slighty hackish reasons.