Source code for bauiv1lib.gather

# Released under the MIT License. See LICENSE for details.
#
"""Provides UI for inviting/joining friends."""

from __future__ import annotations

import weakref
import logging
from enum import Enum

from bauiv1lib.tabs import TabRow
import bauiv1 as bui


[docs] class GatherTab: """Defines a tab for use in the gather UI.""" def __init__(self, window: GatherWindow) -> None: self._window = weakref.ref(window) @property def window(self) -> GatherWindow: """The GatherWindow that this tab belongs to.""" window = self._window() if window is None: raise bui.NotFoundError("GatherTab's window no longer exists.") return window
[docs] def on_activate( self, parent_widget: bui.Widget, tab_button: bui.Widget, region_width: float, region_height: float, region_left: float, region_bottom: float, ) -> bui.Widget: """Called when the tab becomes the active one. The tab should create and return a container widget covering the specified region. """ raise RuntimeError('Should not get here.')
[docs] def on_deactivate(self) -> None: """Called when the tab will no longer be the active one."""
[docs] def save_state(self) -> None: """Called when the parent window is saving state."""
[docs] def restore_state(self) -> None: """Called when the parent window is restoring state."""
[docs] class GatherWindow(bui.Window): """Window for joining/inviting friends."""
[docs] class TabID(Enum): """Our available tab types.""" ABOUT = 'about' INTERNET = 'internet' PRIVATE = 'private' NEARBY = 'nearby' MANUAL = 'manual'
def __init__( self, transition: str | None = 'in_right', origin_widget: bui.Widget | None = None, ): # pylint: disable=too-many-statements # pylint: disable=too-many-locals # pylint: disable=cyclic-import from bauiv1lib.gather.abouttab import AboutGatherTab from bauiv1lib.gather.manualtab import ManualGatherTab from bauiv1lib.gather.privatetab import PrivateGatherTab from bauiv1lib.gather.publictab import PublicGatherTab from bauiv1lib.gather.nearbytab import NearbyGatherTab plus = bui.app.plus assert plus is not None bui.set_analytics_screen('Gather Window') scale_origin: tuple[float, float] | None if origin_widget is not None: self._transition_out = 'out_scale' scale_origin = origin_widget.get_screen_space_center() transition = 'in_scale' else: self._transition_out = 'out_right' scale_origin = None assert bui.app.classic is not None bui.app.ui_v1.set_main_menu_location('Gather') bui.set_party_icon_always_visible(True) uiscale = bui.app.ui_v1.uiscale self._width = 1440 if uiscale is bui.UIScale.SMALL else 1040 x_offs = 200 if uiscale is bui.UIScale.SMALL else 0 self._height = ( 582 if uiscale is bui.UIScale.SMALL else 680 if uiscale is bui.UIScale.MEDIUM else 800 ) self._current_tab: GatherWindow.TabID | None = None extra_top = 20 if uiscale is bui.UIScale.SMALL else 0 self._r = 'gatherWindow' super().__init__( root_widget=bui.containerwidget( size=(self._width, self._height + extra_top), transition=transition, toolbar_visibility='menu_minimal', scale_origin_stack_offset=scale_origin, scale=( 1.3 if uiscale is bui.UIScale.SMALL else 0.97 if uiscale is bui.UIScale.MEDIUM else 0.8 ), stack_offset=( (0, -11) if uiscale is bui.UIScale.SMALL else (0, 0) if uiscale is bui.UIScale.MEDIUM else (0, 0) ), ) ) if uiscale is bui.UIScale.SMALL and bui.app.ui_v1.use_toolbars: bui.containerwidget( edit=self._root_widget, on_cancel_call=self._back ) self._back_button = None else: self._back_button = btn = bui.buttonwidget( parent=self._root_widget, position=(70 + x_offs, self._height - 74), size=(140, 60), scale=1.1, autoselect=True, label=bui.Lstr(resource='backText'), button_type='back', on_activate_call=self._back, ) bui.containerwidget(edit=self._root_widget, cancel_button=btn) bui.buttonwidget( edit=btn, button_type='backSmall', position=(70 + x_offs, self._height - 78), size=(60, 60), label=bui.charstr(bui.SpecialChar.BACK), ) condensed = uiscale is not bui.UIScale.LARGE t_offs_y = ( 0 if not condensed else 25 if uiscale is bui.UIScale.MEDIUM else 17 ) bui.textwidget( parent=self._root_widget, position=(self._width * 0.5, self._height - 42 + t_offs_y), size=(0, 0), color=bui.app.ui_v1.title_color, scale=( 1.5 if not condensed else 1.0 if uiscale is bui.UIScale.MEDIUM else 0.6 ), h_align='center', v_align='center', text=bui.Lstr(resource=self._r + '.titleText'), maxwidth=550, ) scroll_buffer_h = 130 + 2 * x_offs tab_buffer_h = (320 if condensed else 250) + 2 * x_offs # Build up the set of tabs we want. tabdefs: list[tuple[GatherWindow.TabID, bui.Lstr]] = [ (self.TabID.ABOUT, bui.Lstr(resource=self._r + '.aboutText')) ] if plus.get_v1_account_misc_read_val('enablePublicParties', True): tabdefs.append( ( self.TabID.INTERNET, bui.Lstr(resource=self._r + '.publicText'), ) ) tabdefs.append( (self.TabID.PRIVATE, bui.Lstr(resource=self._r + '.privateText')) ) tabdefs.append( (self.TabID.NEARBY, bui.Lstr(resource=self._r + '.nearbyText')) ) tabdefs.append( (self.TabID.MANUAL, bui.Lstr(resource=self._r + '.manualText')) ) # On small UI, push our tabs up closer to the top of the screen to # save a bit of space. tabs_top_extra = 42 if condensed else 0 self._tab_row = TabRow( self._root_widget, tabdefs, pos=(tab_buffer_h * 0.5, self._height - 130 + tabs_top_extra), size=(self._width - tab_buffer_h, 50), on_select_call=bui.WeakCall(self._set_tab), ) # Now instantiate handlers for these tabs. tabtypes: dict[GatherWindow.TabID, type[GatherTab]] = { self.TabID.ABOUT: AboutGatherTab, self.TabID.MANUAL: ManualGatherTab, self.TabID.PRIVATE: PrivateGatherTab, self.TabID.INTERNET: PublicGatherTab, self.TabID.NEARBY: NearbyGatherTab, } self._tabs: dict[GatherWindow.TabID, GatherTab] = {} for tab_id in self._tab_row.tabs: tabtype = tabtypes.get(tab_id) if tabtype is not None: self._tabs[tab_id] = tabtype(self) if bui.app.ui_v1.use_toolbars: bui.widget( edit=self._tab_row.tabs[tabdefs[-1][0]].button, right_widget=bui.get_special_widget('party_button'), ) if uiscale is bui.UIScale.SMALL: bui.widget( edit=self._tab_row.tabs[tabdefs[0][0]].button, left_widget=bui.get_special_widget('back_button'), ) self._scroll_width = self._width - scroll_buffer_h self._scroll_height = self._height - 180.0 + tabs_top_extra self._scroll_left = (self._width - self._scroll_width) * 0.5 self._scroll_bottom = ( self._height - self._scroll_height - 79 - 48 + tabs_top_extra ) buffer_h = 10 buffer_v = 4 # Not actually using a scroll widget anymore; just an image. bui.imagewidget( parent=self._root_widget, position=( self._scroll_left - buffer_h, self._scroll_bottom - buffer_v, ), size=( self._scroll_width + 2 * buffer_h, self._scroll_height + 2 * buffer_v, ), texture=bui.gettexture('scrollWidget'), mesh_transparent=bui.getmesh('softEdgeOutside'), ) self._tab_container: bui.Widget | None = None self._restore_state() def __del__(self) -> None: bui.set_party_icon_always_visible(False)
[docs] def playlist_select(self, origin_widget: bui.Widget) -> None: """Called by the private-hosting tab to select a playlist.""" from bauiv1lib.play import PlayWindow # no-op if our underlying widget is dead or on its way out. if not self._root_widget or self._root_widget.transitioning_out: return self._save_state() bui.containerwidget(edit=self._root_widget, transition='out_left') assert bui.app.classic is not None bui.app.ui_v1.selecting_private_party_playlist = True bui.app.ui_v1.set_main_menu_window( PlayWindow(origin_widget=origin_widget).get_root_widget(), from_window=self._root_widget, )
def _set_tab(self, tab_id: TabID) -> None: if self._current_tab is tab_id: return prev_tab_id = self._current_tab self._current_tab = tab_id # We wanna preserve our current tab between runs. cfg = bui.app.config cfg['Gather Tab'] = tab_id.value cfg.commit() # Update tab colors based on which is selected. self._tab_row.update_appearance(tab_id) if prev_tab_id is not None: prev_tab = self._tabs.get(prev_tab_id) if prev_tab is not None: prev_tab.on_deactivate() # Clear up prev container if it hasn't been done. if self._tab_container: self._tab_container.delete() tab = self._tabs.get(tab_id) if tab is not None: self._tab_container = tab.on_activate( self._root_widget, self._tab_row.tabs[tab_id].button, self._scroll_width, self._scroll_height, self._scroll_left, self._scroll_bottom, ) return def _save_state(self) -> None: try: for tab in self._tabs.values(): tab.save_state() sel = self._root_widget.get_selected_child() selected_tab_ids = [ tab_id for tab_id, tab in self._tab_row.tabs.items() if sel == tab.button ] if sel == self._back_button: sel_name = 'Back' elif selected_tab_ids: assert len(selected_tab_ids) == 1 sel_name = f'Tab:{selected_tab_ids[0].value}' elif sel == self._tab_container: sel_name = 'TabContainer' else: raise ValueError(f'unrecognized selection: \'{sel}\'') assert bui.app.classic is not None bui.app.ui_v1.window_states[type(self)] = { 'sel_name': sel_name, } except Exception: logging.exception('Error saving state for %s.', self) def _restore_state(self) -> None: from efro.util import enum_by_value try: for tab in self._tabs.values(): tab.restore_state() sel: bui.Widget | None assert bui.app.classic is not None winstate = bui.app.ui_v1.window_states.get(type(self), {}) sel_name = winstate.get('sel_name', None) assert isinstance(sel_name, (str, type(None))) current_tab = self.TabID.ABOUT gather_tab_val = bui.app.config.get('Gather Tab') try: stored_tab = enum_by_value(self.TabID, gather_tab_val) if stored_tab in self._tab_row.tabs: current_tab = stored_tab except ValueError: pass self._set_tab(current_tab) if sel_name == 'Back': sel = self._back_button elif sel_name == 'TabContainer': sel = self._tab_container elif isinstance(sel_name, str) and sel_name.startswith('Tab:'): try: sel_tab_id = enum_by_value( self.TabID, sel_name.split(':')[-1] ) except ValueError: sel_tab_id = self.TabID.ABOUT sel = self._tab_row.tabs[sel_tab_id].button else: sel = self._tab_row.tabs[current_tab].button bui.containerwidget(edit=self._root_widget, selected_child=sel) except Exception: logging.exception('Error restoring state for %s.', self) def _back(self) -> None: from bauiv1lib.mainmenu import MainMenuWindow # no-op if our underlying widget is dead or on its way out. if not self._root_widget or self._root_widget.transitioning_out: return self._save_state() bui.containerwidget( edit=self._root_widget, transition=self._transition_out ) assert bui.app.classic is not None bui.app.ui_v1.set_main_menu_window( MainMenuWindow(transition='in_left').get_root_widget(), from_window=self._root_widget, )