Source code for bauiv1lib.store.button

# Released under the MIT License. See LICENSE for details.
#
"""UI functionality for a button leading to the store."""
from __future__ import annotations

import logging
from typing import TYPE_CHECKING

from efro.util import utc_now

import bauiv1 as bui

if TYPE_CHECKING:
    from typing import Any, Sequence, Callable


[docs] class StoreButton: """A button leading to the store.""" def __init__( self, parent: bui.Widget, position: Sequence[float], size: Sequence[float], scale: float, on_activate_call: Callable[[], Any] | None = None, transition_delay: float | None = None, color: Sequence[float] | None = None, textcolor: Sequence[float] | None = None, show_tickets: bool = False, button_type: str | None = None, sale_scale: float = 1.0, ): self._position = position self._size = size self._scale = scale if on_activate_call is None: on_activate_call = bui.WeakCall(self._default_on_activate_call) self._on_activate_call = on_activate_call self._button = bui.buttonwidget( parent=parent, size=size, label='' if show_tickets else bui.Lstr(resource='storeText'), scale=scale, autoselect=True, on_activate_call=self._on_activate, transition_delay=transition_delay, color=color, button_type=button_type, ) self._title_text: bui.Widget | None self._ticket_text: bui.Widget | None if show_tickets: self._title_text = bui.textwidget( parent=parent, position=( position[0] + size[0] * 0.5 * scale, position[1] + size[1] * 0.65 * scale, ), size=(0, 0), h_align='center', v_align='center', maxwidth=size[0] * scale * 0.65, text=bui.Lstr(resource='storeText'), draw_controller=self._button, scale=scale, transition_delay=transition_delay, color=textcolor, ) self._ticket_text = bui.textwidget( parent=parent, size=(0, 0), h_align='center', v_align='center', maxwidth=size[0] * scale * 0.85, text='', color=(0.2, 1.0, 0.2), flatness=1.0, shadow=0.0, scale=scale * 0.6, transition_delay=transition_delay, ) else: self._title_text = None self._ticket_text = None self._circle_rad = 12 * scale self._circle_center = (0.0, 0.0) self._sale_circle_center = (0.0, 0.0) self._available_purchase_backing = bui.imagewidget( parent=parent, color=(1, 0, 0), draw_controller=self._button, size=(2.2 * self._circle_rad, 2.2 * self._circle_rad), texture=bui.gettexture('circleShadow'), transition_delay=transition_delay, ) self._available_purchase_text = bui.textwidget( parent=parent, size=(0, 0), h_align='center', v_align='center', text='', draw_controller=self._button, color=(1, 1, 1), flatness=1.0, shadow=1.0, scale=0.6 * scale, maxwidth=self._circle_rad * 1.4, transition_delay=transition_delay, ) self._sale_circle_rad = 18 * scale * sale_scale self._sale_backing = bui.imagewidget( parent=parent, color=(0.5, 0, 1.0), draw_controller=self._button, size=(2 * self._sale_circle_rad, 2 * self._sale_circle_rad), texture=bui.gettexture('circleZigZag'), transition_delay=transition_delay, ) self._sale_title_text = bui.textwidget( parent=parent, size=(0, 0), h_align='center', v_align='center', draw_controller=self._button, color=(0, 1, 0), flatness=1.0, shadow=0.0, scale=0.5 * scale * sale_scale, maxwidth=self._sale_circle_rad * 1.5, transition_delay=transition_delay, ) self._sale_time_text = bui.textwidget( parent=parent, size=(0, 0), h_align='center', v_align='center', draw_controller=self._button, color=(0, 1, 0), flatness=1.0, shadow=0.0, scale=0.4 * scale * sale_scale, maxwidth=self._sale_circle_rad * 1.5, transition_delay=transition_delay, ) self.set_position(position) self._update_timer = bui.AppTimer( 1.0, bui.WeakCall(self._update), repeat=True ) self._update() def _on_activate(self) -> None: bui.increment_analytics_count('Store button press') self._on_activate_call()
[docs] def set_position(self, position: Sequence[float]) -> None: """Set the button position.""" self._position = position self._circle_center = ( position[0] + 0.1 * self._size[0] * self._scale, position[1] + self._size[1] * self._scale * 0.8, ) self._sale_circle_center = ( position[0] + 0.07 * self._size[0] * self._scale, position[1] + self._size[1] * self._scale * 0.8, ) if not self._button: return bui.buttonwidget(edit=self._button, position=self._position) if self._title_text is not None: bui.textwidget( edit=self._title_text, position=( self._position[0] + self._size[0] * 0.5 * self._scale, self._position[1] + self._size[1] * 0.65 * self._scale, ), ) if self._ticket_text is not None: bui.textwidget( edit=self._ticket_text, position=( position[0] + self._size[0] * 0.5 * self._scale, position[1] + self._size[1] * 0.28 * self._scale, ), size=(0, 0), ) bui.imagewidget( edit=self._available_purchase_backing, position=( self._circle_center[0] - self._circle_rad * 1.02, self._circle_center[1] - self._circle_rad * 1.13, ), ) bui.textwidget( edit=self._available_purchase_text, position=self._circle_center ) bui.imagewidget( edit=self._sale_backing, position=( self._sale_circle_center[0] - self._sale_circle_rad, self._sale_circle_center[1] - self._sale_circle_rad, ), ) bui.textwidget( edit=self._sale_title_text, position=( self._sale_circle_center[0], self._sale_circle_center[1] + self._sale_circle_rad * 0.3, ), ) bui.textwidget( edit=self._sale_time_text, position=( self._sale_circle_center[0], self._sale_circle_center[1] - self._sale_circle_rad * 0.3, ), )
def _default_on_activate_call(self) -> None: # pylint: disable=cyclic-import from bauiv1lib.account import show_sign_in_prompt from bauiv1lib.store.browser import StoreBrowserWindow plus = bui.app.plus assert plus is not None if plus.get_v1_account_state() != 'signed_in': show_sign_in_prompt() return StoreBrowserWindow(modal=True, origin_widget=self._button)
[docs] def get_button(self) -> bui.Widget: """Return the underlying button widget.""" return self._button
def _update(self) -> None: # pylint: disable=too-many-branches # pylint: disable=cyclic-import from babase import SpecialChar plus = bui.app.plus assert plus is not None assert bui.app.classic is not None store = bui.app.classic.store if not self._button: return # Our instance may outlive our UI objects. if self._ticket_text is not None: if plus.get_v1_account_state() == 'signed_in': sval = bui.charstr(SpecialChar.TICKET) + str( plus.get_v1_account_ticket_count() ) else: sval = '-' bui.textwidget(edit=self._ticket_text, text=sval) available_purchases = store.get_available_purchase_count() # Old pro sale stuff.. sale_time = store.get_available_sale_time('extras') # ..also look for new style sales. if sale_time is None: import datetime sales_raw = plus.get_v1_account_misc_read_val('sales', {}) sale_times = [] try: # Look at the current set of sales; filter any with time # remaining that we don't own. for sale_item, sale_info in list(sales_raw.items()): if not plus.get_purchased(sale_item): to_end = ( datetime.datetime.fromtimestamp( sale_info['e'], datetime.UTC ) - utc_now() ).total_seconds() if to_end > 0: sale_times.append(to_end) except Exception: logging.exception('Error parsing sales.') if sale_times: sale_time = int(min(sale_times) * 1000) if sale_time is not None: bui.textwidget( edit=self._sale_title_text, text=bui.Lstr(resource='store.saleText'), ) bui.textwidget( edit=self._sale_time_text, text=bui.timestring(sale_time / 1000.0, centi=False), ) bui.imagewidget(edit=self._sale_backing, opacity=1.0) bui.imagewidget(edit=self._available_purchase_backing, opacity=1.0) bui.textwidget(edit=self._available_purchase_text, text='') bui.imagewidget(edit=self._available_purchase_backing, opacity=0.0) else: bui.imagewidget(edit=self._sale_backing, opacity=0.0) bui.textwidget(edit=self._sale_time_text, text='') bui.textwidget(edit=self._sale_title_text, text='') if available_purchases > 0: bui.textwidget( edit=self._available_purchase_text, text=str(available_purchases), ) bui.imagewidget( edit=self._available_purchase_backing, opacity=1.0 ) else: bui.textwidget(edit=self._available_purchase_text, text='') bui.imagewidget( edit=self._available_purchase_backing, opacity=0.0 )