bastd.ui.gather
Provides UI for inviting/joining friends.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides UI for inviting/joining friends.""" 4 5from __future__ import annotations 6 7import weakref 8from enum import Enum 9from typing import TYPE_CHECKING 10 11import _ba 12import ba 13from bastd.ui.tabs import TabRow 14 15if TYPE_CHECKING: 16 pass 17 18 19class GatherTab: 20 """Defines a tab for use in the gather UI.""" 21 22 def __init__(self, window: GatherWindow) -> None: 23 self._window = weakref.ref(window) 24 25 @property 26 def window(self) -> GatherWindow: 27 """The GatherWindow that this tab belongs to.""" 28 window = self._window() 29 if window is None: 30 raise ba.NotFoundError("GatherTab's window no longer exists.") 31 return window 32 33 def on_activate( 34 self, 35 parent_widget: ba.Widget, 36 tab_button: ba.Widget, 37 region_width: float, 38 region_height: float, 39 region_left: float, 40 region_bottom: float, 41 ) -> ba.Widget: 42 """Called when the tab becomes the active one. 43 44 The tab should create and return a container widget covering the 45 specified region. 46 """ 47 48 def on_deactivate(self) -> None: 49 """Called when the tab will no longer be the active one.""" 50 51 def save_state(self) -> None: 52 """Called when the parent window is saving state.""" 53 54 def restore_state(self) -> None: 55 """Called when the parent window is restoring state.""" 56 57 58class GatherWindow(ba.Window): 59 """Window for joining/inviting friends.""" 60 61 class TabID(Enum): 62 """Our available tab types.""" 63 ABOUT = 'about' 64 INTERNET = 'internet' 65 PRIVATE = 'private' 66 NEARBY = 'nearby' 67 MANUAL = 'manual' 68 69 def __init__(self, 70 transition: str | None = 'in_right', 71 origin_widget: ba.Widget | None = None): 72 # pylint: disable=too-many-statements 73 # pylint: disable=too-many-locals 74 # pylint: disable=cyclic-import 75 from bastd.ui.gather.abouttab import AboutGatherTab 76 from bastd.ui.gather.manualtab import ManualGatherTab 77 from bastd.ui.gather.privatetab import PrivateGatherTab 78 from bastd.ui.gather.publictab import PublicGatherTab 79 from bastd.ui.gather.nearbytab import NearbyGatherTab 80 81 ba.set_analytics_screen('Gather Window') 82 scale_origin: tuple[float, float] | None 83 if origin_widget is not None: 84 self._transition_out = 'out_scale' 85 scale_origin = origin_widget.get_screen_space_center() 86 transition = 'in_scale' 87 else: 88 self._transition_out = 'out_right' 89 scale_origin = None 90 ba.app.ui.set_main_menu_location('Gather') 91 _ba.set_party_icon_always_visible(True) 92 uiscale = ba.app.ui.uiscale 93 self._width = 1240 if uiscale is ba.UIScale.SMALL else 1040 94 x_offs = 100 if uiscale is ba.UIScale.SMALL else 0 95 self._height = (582 if uiscale is ba.UIScale.SMALL else 96 680 if uiscale is ba.UIScale.MEDIUM else 800) 97 self._current_tab: GatherWindow.TabID | None = None 98 extra_top = 20 if uiscale is ba.UIScale.SMALL else 0 99 self._r = 'gatherWindow' 100 101 super().__init__(root_widget=ba.containerwidget( 102 size=(self._width, self._height + extra_top), 103 transition=transition, 104 toolbar_visibility='menu_minimal', 105 scale_origin_stack_offset=scale_origin, 106 scale=(1.3 if uiscale is ba.UIScale.SMALL else 107 0.97 if uiscale is ba.UIScale.MEDIUM else 0.8), 108 stack_offset=(0, -11) if uiscale is ba.UIScale.SMALL else ( 109 0, 0) if uiscale is ba.UIScale.MEDIUM else (0, 0))) 110 111 if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars: 112 ba.containerwidget(edit=self._root_widget, 113 on_cancel_call=self._back) 114 self._back_button = None 115 else: 116 self._back_button = btn = ba.buttonwidget( 117 parent=self._root_widget, 118 position=(70 + x_offs, self._height - 74), 119 size=(140, 60), 120 scale=1.1, 121 autoselect=True, 122 label=ba.Lstr(resource='backText'), 123 button_type='back', 124 on_activate_call=self._back) 125 ba.containerwidget(edit=self._root_widget, cancel_button=btn) 126 ba.buttonwidget(edit=btn, 127 button_type='backSmall', 128 position=(70 + x_offs, self._height - 78), 129 size=(60, 60), 130 label=ba.charstr(ba.SpecialChar.BACK)) 131 132 condensed = uiscale is not ba.UIScale.LARGE 133 t_offs_y = (0 if not condensed else 134 25 if uiscale is ba.UIScale.MEDIUM else 17) 135 ba.textwidget(parent=self._root_widget, 136 position=(self._width * 0.5, 137 self._height - 42 + t_offs_y), 138 size=(0, 0), 139 color=ba.app.ui.title_color, 140 scale=(1.5 if not condensed else 141 1.0 if uiscale is ba.UIScale.MEDIUM else 0.6), 142 h_align='center', 143 v_align='center', 144 text=ba.Lstr(resource=self._r + '.titleText'), 145 maxwidth=550) 146 147 scroll_buffer_h = 130 + 2 * x_offs 148 tab_buffer_h = ((320 if condensed else 250) + 2 * x_offs) 149 150 # Build up the set of tabs we want. 151 tabdefs: list[tuple[GatherWindow.TabID, ba.Lstr]] = [ 152 (self.TabID.ABOUT, ba.Lstr(resource=self._r + '.aboutText')) 153 ] 154 if _ba.get_v1_account_misc_read_val('enablePublicParties', True): 155 tabdefs.append((self.TabID.INTERNET, 156 ba.Lstr(resource=self._r + '.publicText'))) 157 tabdefs.append( 158 (self.TabID.PRIVATE, ba.Lstr(resource=self._r + '.privateText'))) 159 tabdefs.append( 160 (self.TabID.NEARBY, ba.Lstr(resource=self._r + '.nearbyText'))) 161 tabdefs.append( 162 (self.TabID.MANUAL, ba.Lstr(resource=self._r + '.manualText'))) 163 164 # On small UI, push our tabs up closer to the top of the screen to 165 # save a bit of space. 166 tabs_top_extra = 42 if condensed else 0 167 self._tab_row = TabRow(self._root_widget, 168 tabdefs, 169 pos=(tab_buffer_h * 0.5, 170 self._height - 130 + tabs_top_extra), 171 size=(self._width - tab_buffer_h, 50), 172 on_select_call=ba.WeakCall(self._set_tab)) 173 174 # Now instantiate handlers for these tabs. 175 tabtypes: dict[GatherWindow.TabID, type[GatherTab]] = { 176 self.TabID.ABOUT: AboutGatherTab, 177 self.TabID.MANUAL: ManualGatherTab, 178 self.TabID.PRIVATE: PrivateGatherTab, 179 self.TabID.INTERNET: PublicGatherTab, 180 self.TabID.NEARBY: NearbyGatherTab 181 } 182 self._tabs: dict[GatherWindow.TabID, GatherTab] = {} 183 for tab_id in self._tab_row.tabs: 184 tabtype = tabtypes.get(tab_id) 185 if tabtype is not None: 186 self._tabs[tab_id] = tabtype(self) 187 188 if ba.app.ui.use_toolbars: 189 ba.widget(edit=self._tab_row.tabs[tabdefs[-1][0]].button, 190 right_widget=_ba.get_special_widget('party_button')) 191 if uiscale is ba.UIScale.SMALL: 192 ba.widget(edit=self._tab_row.tabs[tabdefs[0][0]].button, 193 left_widget=_ba.get_special_widget('back_button')) 194 195 self._scroll_width = self._width - scroll_buffer_h 196 self._scroll_height = self._height - 180.0 + tabs_top_extra 197 198 self._scroll_left = (self._width - self._scroll_width) * 0.5 199 self._scroll_bottom = (self._height - self._scroll_height - 79 - 48 + 200 tabs_top_extra) 201 buffer_h = 10 202 buffer_v = 4 203 204 # Not actually using a scroll widget anymore; just an image. 205 ba.imagewidget(parent=self._root_widget, 206 position=(self._scroll_left - buffer_h, 207 self._scroll_bottom - buffer_v), 208 size=(self._scroll_width + 2 * buffer_h, 209 self._scroll_height + 2 * buffer_v), 210 texture=ba.gettexture('scrollWidget'), 211 model_transparent=ba.getmodel('softEdgeOutside')) 212 self._tab_container: ba.Widget | None = None 213 214 self._restore_state() 215 216 def __del__(self) -> None: 217 _ba.set_party_icon_always_visible(False) 218 219 def playlist_select(self, origin_widget: ba.Widget) -> None: 220 """Called by the private-hosting tab to select a playlist.""" 221 from bastd.ui.play import PlayWindow 222 self._save_state() 223 ba.containerwidget(edit=self._root_widget, transition='out_left') 224 ba.app.ui.selecting_private_party_playlist = True 225 ba.app.ui.set_main_menu_window( 226 PlayWindow(origin_widget=origin_widget).get_root_widget()) 227 228 def _set_tab(self, tab_id: TabID) -> None: 229 if self._current_tab is tab_id: 230 return 231 prev_tab_id = self._current_tab 232 self._current_tab = tab_id 233 234 # We wanna preserve our current tab between runs. 235 cfg = ba.app.config 236 cfg['Gather Tab'] = tab_id.value 237 cfg.commit() 238 239 # Update tab colors based on which is selected. 240 self._tab_row.update_appearance(tab_id) 241 242 if prev_tab_id is not None: 243 prev_tab = self._tabs.get(prev_tab_id) 244 if prev_tab is not None: 245 prev_tab.on_deactivate() 246 247 # Clear up prev container if it hasn't been done. 248 if self._tab_container: 249 self._tab_container.delete() 250 251 tab = self._tabs.get(tab_id) 252 if tab is not None: 253 self._tab_container = tab.on_activate( 254 self._root_widget, 255 self._tab_row.tabs[tab_id].button, 256 self._scroll_width, 257 self._scroll_height, 258 self._scroll_left, 259 self._scroll_bottom, 260 ) 261 return 262 263 def _save_state(self) -> None: 264 try: 265 for tab in self._tabs.values(): 266 tab.save_state() 267 268 sel = self._root_widget.get_selected_child() 269 selected_tab_ids = [ 270 tab_id for tab_id, tab in self._tab_row.tabs.items() 271 if sel == tab.button 272 ] 273 if sel == self._back_button: 274 sel_name = 'Back' 275 elif selected_tab_ids: 276 assert len(selected_tab_ids) == 1 277 sel_name = f'Tab:{selected_tab_ids[0].value}' 278 elif sel == self._tab_container: 279 sel_name = 'TabContainer' 280 else: 281 raise ValueError(f'unrecognized selection: \'{sel}\'') 282 ba.app.ui.window_states[type(self)] = { 283 'sel_name': sel_name, 284 } 285 except Exception: 286 ba.print_exception(f'Error saving state for {self}.') 287 288 def _restore_state(self) -> None: 289 from efro.util import enum_by_value 290 try: 291 for tab in self._tabs.values(): 292 tab.restore_state() 293 294 sel: ba.Widget | None 295 winstate = ba.app.ui.window_states.get(type(self), {}) 296 sel_name = winstate.get('sel_name', None) 297 assert isinstance(sel_name, (str, type(None))) 298 current_tab = self.TabID.ABOUT 299 gather_tab_val = ba.app.config.get('Gather Tab') 300 try: 301 stored_tab = enum_by_value(self.TabID, gather_tab_val) 302 if stored_tab in self._tab_row.tabs: 303 current_tab = stored_tab 304 except ValueError: 305 pass 306 self._set_tab(current_tab) 307 if sel_name == 'Back': 308 sel = self._back_button 309 elif sel_name == 'TabContainer': 310 sel = self._tab_container 311 elif isinstance(sel_name, str) and sel_name.startswith('Tab:'): 312 try: 313 sel_tab_id = enum_by_value(self.TabID, 314 sel_name.split(':')[-1]) 315 except ValueError: 316 sel_tab_id = self.TabID.ABOUT 317 sel = self._tab_row.tabs[sel_tab_id].button 318 else: 319 sel = self._tab_row.tabs[current_tab].button 320 ba.containerwidget(edit=self._root_widget, selected_child=sel) 321 except Exception: 322 ba.print_exception('Error restoring gather-win state.') 323 324 def _back(self) -> None: 325 from bastd.ui.mainmenu import MainMenuWindow 326 self._save_state() 327 ba.containerwidget(edit=self._root_widget, 328 transition=self._transition_out) 329 ba.app.ui.set_main_menu_window( 330 MainMenuWindow(transition='in_left').get_root_widget())
class
GatherTab:
20class GatherTab: 21 """Defines a tab for use in the gather UI.""" 22 23 def __init__(self, window: GatherWindow) -> None: 24 self._window = weakref.ref(window) 25 26 @property 27 def window(self) -> GatherWindow: 28 """The GatherWindow that this tab belongs to.""" 29 window = self._window() 30 if window is None: 31 raise ba.NotFoundError("GatherTab's window no longer exists.") 32 return window 33 34 def on_activate( 35 self, 36 parent_widget: ba.Widget, 37 tab_button: ba.Widget, 38 region_width: float, 39 region_height: float, 40 region_left: float, 41 region_bottom: float, 42 ) -> ba.Widget: 43 """Called when the tab becomes the active one. 44 45 The tab should create and return a container widget covering the 46 specified region. 47 """ 48 49 def on_deactivate(self) -> None: 50 """Called when the tab will no longer be the active one.""" 51 52 def save_state(self) -> None: 53 """Called when the parent window is saving state.""" 54 55 def restore_state(self) -> None: 56 """Called when the parent window is restoring state."""
Defines a tab for use in the gather UI.
GatherTab(window: bastd.ui.gather.GatherWindow)
def
on_activate( self, parent_widget: _ba.Widget, tab_button: _ba.Widget, region_width: float, region_height: float, region_left: float, region_bottom: float) -> _ba.Widget:
34 def on_activate( 35 self, 36 parent_widget: ba.Widget, 37 tab_button: ba.Widget, 38 region_width: float, 39 region_height: float, 40 region_left: float, 41 region_bottom: float, 42 ) -> ba.Widget: 43 """Called when the tab becomes the active one. 44 45 The tab should create and return a container widget covering the 46 specified region. 47 """
Called when the tab becomes the active one.
The tab should create and return a container widget covering the specified region.
class
GatherWindow(ba.ui.Window):
59class GatherWindow(ba.Window): 60 """Window for joining/inviting friends.""" 61 62 class TabID(Enum): 63 """Our available tab types.""" 64 ABOUT = 'about' 65 INTERNET = 'internet' 66 PRIVATE = 'private' 67 NEARBY = 'nearby' 68 MANUAL = 'manual' 69 70 def __init__(self, 71 transition: str | None = 'in_right', 72 origin_widget: ba.Widget | None = None): 73 # pylint: disable=too-many-statements 74 # pylint: disable=too-many-locals 75 # pylint: disable=cyclic-import 76 from bastd.ui.gather.abouttab import AboutGatherTab 77 from bastd.ui.gather.manualtab import ManualGatherTab 78 from bastd.ui.gather.privatetab import PrivateGatherTab 79 from bastd.ui.gather.publictab import PublicGatherTab 80 from bastd.ui.gather.nearbytab import NearbyGatherTab 81 82 ba.set_analytics_screen('Gather Window') 83 scale_origin: tuple[float, float] | None 84 if origin_widget is not None: 85 self._transition_out = 'out_scale' 86 scale_origin = origin_widget.get_screen_space_center() 87 transition = 'in_scale' 88 else: 89 self._transition_out = 'out_right' 90 scale_origin = None 91 ba.app.ui.set_main_menu_location('Gather') 92 _ba.set_party_icon_always_visible(True) 93 uiscale = ba.app.ui.uiscale 94 self._width = 1240 if uiscale is ba.UIScale.SMALL else 1040 95 x_offs = 100 if uiscale is ba.UIScale.SMALL else 0 96 self._height = (582 if uiscale is ba.UIScale.SMALL else 97 680 if uiscale is ba.UIScale.MEDIUM else 800) 98 self._current_tab: GatherWindow.TabID | None = None 99 extra_top = 20 if uiscale is ba.UIScale.SMALL else 0 100 self._r = 'gatherWindow' 101 102 super().__init__(root_widget=ba.containerwidget( 103 size=(self._width, self._height + extra_top), 104 transition=transition, 105 toolbar_visibility='menu_minimal', 106 scale_origin_stack_offset=scale_origin, 107 scale=(1.3 if uiscale is ba.UIScale.SMALL else 108 0.97 if uiscale is ba.UIScale.MEDIUM else 0.8), 109 stack_offset=(0, -11) if uiscale is ba.UIScale.SMALL else ( 110 0, 0) if uiscale is ba.UIScale.MEDIUM else (0, 0))) 111 112 if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars: 113 ba.containerwidget(edit=self._root_widget, 114 on_cancel_call=self._back) 115 self._back_button = None 116 else: 117 self._back_button = btn = ba.buttonwidget( 118 parent=self._root_widget, 119 position=(70 + x_offs, self._height - 74), 120 size=(140, 60), 121 scale=1.1, 122 autoselect=True, 123 label=ba.Lstr(resource='backText'), 124 button_type='back', 125 on_activate_call=self._back) 126 ba.containerwidget(edit=self._root_widget, cancel_button=btn) 127 ba.buttonwidget(edit=btn, 128 button_type='backSmall', 129 position=(70 + x_offs, self._height - 78), 130 size=(60, 60), 131 label=ba.charstr(ba.SpecialChar.BACK)) 132 133 condensed = uiscale is not ba.UIScale.LARGE 134 t_offs_y = (0 if not condensed else 135 25 if uiscale is ba.UIScale.MEDIUM else 17) 136 ba.textwidget(parent=self._root_widget, 137 position=(self._width * 0.5, 138 self._height - 42 + t_offs_y), 139 size=(0, 0), 140 color=ba.app.ui.title_color, 141 scale=(1.5 if not condensed else 142 1.0 if uiscale is ba.UIScale.MEDIUM else 0.6), 143 h_align='center', 144 v_align='center', 145 text=ba.Lstr(resource=self._r + '.titleText'), 146 maxwidth=550) 147 148 scroll_buffer_h = 130 + 2 * x_offs 149 tab_buffer_h = ((320 if condensed else 250) + 2 * x_offs) 150 151 # Build up the set of tabs we want. 152 tabdefs: list[tuple[GatherWindow.TabID, ba.Lstr]] = [ 153 (self.TabID.ABOUT, ba.Lstr(resource=self._r + '.aboutText')) 154 ] 155 if _ba.get_v1_account_misc_read_val('enablePublicParties', True): 156 tabdefs.append((self.TabID.INTERNET, 157 ba.Lstr(resource=self._r + '.publicText'))) 158 tabdefs.append( 159 (self.TabID.PRIVATE, ba.Lstr(resource=self._r + '.privateText'))) 160 tabdefs.append( 161 (self.TabID.NEARBY, ba.Lstr(resource=self._r + '.nearbyText'))) 162 tabdefs.append( 163 (self.TabID.MANUAL, ba.Lstr(resource=self._r + '.manualText'))) 164 165 # On small UI, push our tabs up closer to the top of the screen to 166 # save a bit of space. 167 tabs_top_extra = 42 if condensed else 0 168 self._tab_row = TabRow(self._root_widget, 169 tabdefs, 170 pos=(tab_buffer_h * 0.5, 171 self._height - 130 + tabs_top_extra), 172 size=(self._width - tab_buffer_h, 50), 173 on_select_call=ba.WeakCall(self._set_tab)) 174 175 # Now instantiate handlers for these tabs. 176 tabtypes: dict[GatherWindow.TabID, type[GatherTab]] = { 177 self.TabID.ABOUT: AboutGatherTab, 178 self.TabID.MANUAL: ManualGatherTab, 179 self.TabID.PRIVATE: PrivateGatherTab, 180 self.TabID.INTERNET: PublicGatherTab, 181 self.TabID.NEARBY: NearbyGatherTab 182 } 183 self._tabs: dict[GatherWindow.TabID, GatherTab] = {} 184 for tab_id in self._tab_row.tabs: 185 tabtype = tabtypes.get(tab_id) 186 if tabtype is not None: 187 self._tabs[tab_id] = tabtype(self) 188 189 if ba.app.ui.use_toolbars: 190 ba.widget(edit=self._tab_row.tabs[tabdefs[-1][0]].button, 191 right_widget=_ba.get_special_widget('party_button')) 192 if uiscale is ba.UIScale.SMALL: 193 ba.widget(edit=self._tab_row.tabs[tabdefs[0][0]].button, 194 left_widget=_ba.get_special_widget('back_button')) 195 196 self._scroll_width = self._width - scroll_buffer_h 197 self._scroll_height = self._height - 180.0 + tabs_top_extra 198 199 self._scroll_left = (self._width - self._scroll_width) * 0.5 200 self._scroll_bottom = (self._height - self._scroll_height - 79 - 48 + 201 tabs_top_extra) 202 buffer_h = 10 203 buffer_v = 4 204 205 # Not actually using a scroll widget anymore; just an image. 206 ba.imagewidget(parent=self._root_widget, 207 position=(self._scroll_left - buffer_h, 208 self._scroll_bottom - buffer_v), 209 size=(self._scroll_width + 2 * buffer_h, 210 self._scroll_height + 2 * buffer_v), 211 texture=ba.gettexture('scrollWidget'), 212 model_transparent=ba.getmodel('softEdgeOutside')) 213 self._tab_container: ba.Widget | None = None 214 215 self._restore_state() 216 217 def __del__(self) -> None: 218 _ba.set_party_icon_always_visible(False) 219 220 def playlist_select(self, origin_widget: ba.Widget) -> None: 221 """Called by the private-hosting tab to select a playlist.""" 222 from bastd.ui.play import PlayWindow 223 self._save_state() 224 ba.containerwidget(edit=self._root_widget, transition='out_left') 225 ba.app.ui.selecting_private_party_playlist = True 226 ba.app.ui.set_main_menu_window( 227 PlayWindow(origin_widget=origin_widget).get_root_widget()) 228 229 def _set_tab(self, tab_id: TabID) -> None: 230 if self._current_tab is tab_id: 231 return 232 prev_tab_id = self._current_tab 233 self._current_tab = tab_id 234 235 # We wanna preserve our current tab between runs. 236 cfg = ba.app.config 237 cfg['Gather Tab'] = tab_id.value 238 cfg.commit() 239 240 # Update tab colors based on which is selected. 241 self._tab_row.update_appearance(tab_id) 242 243 if prev_tab_id is not None: 244 prev_tab = self._tabs.get(prev_tab_id) 245 if prev_tab is not None: 246 prev_tab.on_deactivate() 247 248 # Clear up prev container if it hasn't been done. 249 if self._tab_container: 250 self._tab_container.delete() 251 252 tab = self._tabs.get(tab_id) 253 if tab is not None: 254 self._tab_container = tab.on_activate( 255 self._root_widget, 256 self._tab_row.tabs[tab_id].button, 257 self._scroll_width, 258 self._scroll_height, 259 self._scroll_left, 260 self._scroll_bottom, 261 ) 262 return 263 264 def _save_state(self) -> None: 265 try: 266 for tab in self._tabs.values(): 267 tab.save_state() 268 269 sel = self._root_widget.get_selected_child() 270 selected_tab_ids = [ 271 tab_id for tab_id, tab in self._tab_row.tabs.items() 272 if sel == tab.button 273 ] 274 if sel == self._back_button: 275 sel_name = 'Back' 276 elif selected_tab_ids: 277 assert len(selected_tab_ids) == 1 278 sel_name = f'Tab:{selected_tab_ids[0].value}' 279 elif sel == self._tab_container: 280 sel_name = 'TabContainer' 281 else: 282 raise ValueError(f'unrecognized selection: \'{sel}\'') 283 ba.app.ui.window_states[type(self)] = { 284 'sel_name': sel_name, 285 } 286 except Exception: 287 ba.print_exception(f'Error saving state for {self}.') 288 289 def _restore_state(self) -> None: 290 from efro.util import enum_by_value 291 try: 292 for tab in self._tabs.values(): 293 tab.restore_state() 294 295 sel: ba.Widget | None 296 winstate = ba.app.ui.window_states.get(type(self), {}) 297 sel_name = winstate.get('sel_name', None) 298 assert isinstance(sel_name, (str, type(None))) 299 current_tab = self.TabID.ABOUT 300 gather_tab_val = ba.app.config.get('Gather Tab') 301 try: 302 stored_tab = enum_by_value(self.TabID, gather_tab_val) 303 if stored_tab in self._tab_row.tabs: 304 current_tab = stored_tab 305 except ValueError: 306 pass 307 self._set_tab(current_tab) 308 if sel_name == 'Back': 309 sel = self._back_button 310 elif sel_name == 'TabContainer': 311 sel = self._tab_container 312 elif isinstance(sel_name, str) and sel_name.startswith('Tab:'): 313 try: 314 sel_tab_id = enum_by_value(self.TabID, 315 sel_name.split(':')[-1]) 316 except ValueError: 317 sel_tab_id = self.TabID.ABOUT 318 sel = self._tab_row.tabs[sel_tab_id].button 319 else: 320 sel = self._tab_row.tabs[current_tab].button 321 ba.containerwidget(edit=self._root_widget, selected_child=sel) 322 except Exception: 323 ba.print_exception('Error restoring gather-win state.') 324 325 def _back(self) -> None: 326 from bastd.ui.mainmenu import MainMenuWindow 327 self._save_state() 328 ba.containerwidget(edit=self._root_widget, 329 transition=self._transition_out) 330 ba.app.ui.set_main_menu_window( 331 MainMenuWindow(transition='in_left').get_root_widget())
Window for joining/inviting friends.
GatherWindow( transition: str | None = 'in_right', origin_widget: _ba.Widget | None = None)
70 def __init__(self, 71 transition: str | None = 'in_right', 72 origin_widget: ba.Widget | None = None): 73 # pylint: disable=too-many-statements 74 # pylint: disable=too-many-locals 75 # pylint: disable=cyclic-import 76 from bastd.ui.gather.abouttab import AboutGatherTab 77 from bastd.ui.gather.manualtab import ManualGatherTab 78 from bastd.ui.gather.privatetab import PrivateGatherTab 79 from bastd.ui.gather.publictab import PublicGatherTab 80 from bastd.ui.gather.nearbytab import NearbyGatherTab 81 82 ba.set_analytics_screen('Gather Window') 83 scale_origin: tuple[float, float] | None 84 if origin_widget is not None: 85 self._transition_out = 'out_scale' 86 scale_origin = origin_widget.get_screen_space_center() 87 transition = 'in_scale' 88 else: 89 self._transition_out = 'out_right' 90 scale_origin = None 91 ba.app.ui.set_main_menu_location('Gather') 92 _ba.set_party_icon_always_visible(True) 93 uiscale = ba.app.ui.uiscale 94 self._width = 1240 if uiscale is ba.UIScale.SMALL else 1040 95 x_offs = 100 if uiscale is ba.UIScale.SMALL else 0 96 self._height = (582 if uiscale is ba.UIScale.SMALL else 97 680 if uiscale is ba.UIScale.MEDIUM else 800) 98 self._current_tab: GatherWindow.TabID | None = None 99 extra_top = 20 if uiscale is ba.UIScale.SMALL else 0 100 self._r = 'gatherWindow' 101 102 super().__init__(root_widget=ba.containerwidget( 103 size=(self._width, self._height + extra_top), 104 transition=transition, 105 toolbar_visibility='menu_minimal', 106 scale_origin_stack_offset=scale_origin, 107 scale=(1.3 if uiscale is ba.UIScale.SMALL else 108 0.97 if uiscale is ba.UIScale.MEDIUM else 0.8), 109 stack_offset=(0, -11) if uiscale is ba.UIScale.SMALL else ( 110 0, 0) if uiscale is ba.UIScale.MEDIUM else (0, 0))) 111 112 if uiscale is ba.UIScale.SMALL and ba.app.ui.use_toolbars: 113 ba.containerwidget(edit=self._root_widget, 114 on_cancel_call=self._back) 115 self._back_button = None 116 else: 117 self._back_button = btn = ba.buttonwidget( 118 parent=self._root_widget, 119 position=(70 + x_offs, self._height - 74), 120 size=(140, 60), 121 scale=1.1, 122 autoselect=True, 123 label=ba.Lstr(resource='backText'), 124 button_type='back', 125 on_activate_call=self._back) 126 ba.containerwidget(edit=self._root_widget, cancel_button=btn) 127 ba.buttonwidget(edit=btn, 128 button_type='backSmall', 129 position=(70 + x_offs, self._height - 78), 130 size=(60, 60), 131 label=ba.charstr(ba.SpecialChar.BACK)) 132 133 condensed = uiscale is not ba.UIScale.LARGE 134 t_offs_y = (0 if not condensed else 135 25 if uiscale is ba.UIScale.MEDIUM else 17) 136 ba.textwidget(parent=self._root_widget, 137 position=(self._width * 0.5, 138 self._height - 42 + t_offs_y), 139 size=(0, 0), 140 color=ba.app.ui.title_color, 141 scale=(1.5 if not condensed else 142 1.0 if uiscale is ba.UIScale.MEDIUM else 0.6), 143 h_align='center', 144 v_align='center', 145 text=ba.Lstr(resource=self._r + '.titleText'), 146 maxwidth=550) 147 148 scroll_buffer_h = 130 + 2 * x_offs 149 tab_buffer_h = ((320 if condensed else 250) + 2 * x_offs) 150 151 # Build up the set of tabs we want. 152 tabdefs: list[tuple[GatherWindow.TabID, ba.Lstr]] = [ 153 (self.TabID.ABOUT, ba.Lstr(resource=self._r + '.aboutText')) 154 ] 155 if _ba.get_v1_account_misc_read_val('enablePublicParties', True): 156 tabdefs.append((self.TabID.INTERNET, 157 ba.Lstr(resource=self._r + '.publicText'))) 158 tabdefs.append( 159 (self.TabID.PRIVATE, ba.Lstr(resource=self._r + '.privateText'))) 160 tabdefs.append( 161 (self.TabID.NEARBY, ba.Lstr(resource=self._r + '.nearbyText'))) 162 tabdefs.append( 163 (self.TabID.MANUAL, ba.Lstr(resource=self._r + '.manualText'))) 164 165 # On small UI, push our tabs up closer to the top of the screen to 166 # save a bit of space. 167 tabs_top_extra = 42 if condensed else 0 168 self._tab_row = TabRow(self._root_widget, 169 tabdefs, 170 pos=(tab_buffer_h * 0.5, 171 self._height - 130 + tabs_top_extra), 172 size=(self._width - tab_buffer_h, 50), 173 on_select_call=ba.WeakCall(self._set_tab)) 174 175 # Now instantiate handlers for these tabs. 176 tabtypes: dict[GatherWindow.TabID, type[GatherTab]] = { 177 self.TabID.ABOUT: AboutGatherTab, 178 self.TabID.MANUAL: ManualGatherTab, 179 self.TabID.PRIVATE: PrivateGatherTab, 180 self.TabID.INTERNET: PublicGatherTab, 181 self.TabID.NEARBY: NearbyGatherTab 182 } 183 self._tabs: dict[GatherWindow.TabID, GatherTab] = {} 184 for tab_id in self._tab_row.tabs: 185 tabtype = tabtypes.get(tab_id) 186 if tabtype is not None: 187 self._tabs[tab_id] = tabtype(self) 188 189 if ba.app.ui.use_toolbars: 190 ba.widget(edit=self._tab_row.tabs[tabdefs[-1][0]].button, 191 right_widget=_ba.get_special_widget('party_button')) 192 if uiscale is ba.UIScale.SMALL: 193 ba.widget(edit=self._tab_row.tabs[tabdefs[0][0]].button, 194 left_widget=_ba.get_special_widget('back_button')) 195 196 self._scroll_width = self._width - scroll_buffer_h 197 self._scroll_height = self._height - 180.0 + tabs_top_extra 198 199 self._scroll_left = (self._width - self._scroll_width) * 0.5 200 self._scroll_bottom = (self._height - self._scroll_height - 79 - 48 + 201 tabs_top_extra) 202 buffer_h = 10 203 buffer_v = 4 204 205 # Not actually using a scroll widget anymore; just an image. 206 ba.imagewidget(parent=self._root_widget, 207 position=(self._scroll_left - buffer_h, 208 self._scroll_bottom - buffer_v), 209 size=(self._scroll_width + 2 * buffer_h, 210 self._scroll_height + 2 * buffer_v), 211 texture=ba.gettexture('scrollWidget'), 212 model_transparent=ba.getmodel('softEdgeOutside')) 213 self._tab_container: ba.Widget | None = None 214 215 self._restore_state()
def
playlist_select(self, origin_widget: _ba.Widget) -> None:
220 def playlist_select(self, origin_widget: ba.Widget) -> None: 221 """Called by the private-hosting tab to select a playlist.""" 222 from bastd.ui.play import PlayWindow 223 self._save_state() 224 ba.containerwidget(edit=self._root_widget, transition='out_left') 225 ba.app.ui.selecting_private_party_playlist = True 226 ba.app.ui.set_main_menu_window( 227 PlayWindow(origin_widget=origin_widget).get_root_widget())
Called by the private-hosting tab to select a playlist.
Inherited Members
- ba.ui.Window
- get_root_widget
class
GatherWindow.TabID(enum.Enum):
62 class TabID(Enum): 63 """Our available tab types.""" 64 ABOUT = 'about' 65 INTERNET = 'internet' 66 PRIVATE = 'private' 67 NEARBY = 'nearby' 68 MANUAL = 'manual'
Our available tab types.
Inherited Members
- enum.Enum
- name
- value