bauiv1lib.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 8import logging 9from enum import Enum 10from typing import override, TYPE_CHECKING 11 12from bauiv1lib.tabs import TabRow 13import bauiv1 as bui 14 15if TYPE_CHECKING: 16 from bauiv1lib.play import PlaylistSelectContext 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 bui.NotFoundError("GatherTab's window no longer exists.") 31 return window 32 33 def on_activate( 34 self, 35 parent_widget: bui.Widget, 36 tab_button: bui.Widget, 37 region_width: float, 38 region_height: float, 39 region_left: float, 40 region_bottom: float, 41 ) -> bui.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 raise RuntimeError('Should not get here.') 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.""" 57 58 59class GatherWindow(bui.MainWindow): 60 """Window for joining/inviting friends.""" 61 62 class TabID(Enum): 63 """Our available tab types.""" 64 65 ABOUT = 'about' 66 INTERNET = 'internet' 67 PRIVATE = 'private' 68 NEARBY = 'nearby' 69 MANUAL = 'manual' 70 71 def __init__( 72 self, 73 transition: str | None = 'in_right', 74 origin_widget: bui.Widget | None = None, 75 ): 76 # pylint: disable=too-many-statements 77 # pylint: disable=too-many-locals 78 # pylint: disable=cyclic-import 79 from bauiv1lib.gather.abouttab import AboutGatherTab 80 from bauiv1lib.gather.manualtab import ManualGatherTab 81 from bauiv1lib.gather.privatetab import PrivateGatherTab 82 from bauiv1lib.gather.publictab import PublicGatherTab 83 from bauiv1lib.gather.nearbytab import NearbyGatherTab 84 85 plus = bui.app.plus 86 assert plus is not None 87 88 bui.set_analytics_screen('Gather Window') 89 uiscale = bui.app.ui_v1.uiscale 90 self._width = 1640 if uiscale is bui.UIScale.SMALL else 1040 91 x_offs = 200 if uiscale is bui.UIScale.SMALL else 0 92 self._height = ( 93 550 94 if uiscale is bui.UIScale.SMALL 95 else 680 if uiscale is bui.UIScale.MEDIUM else 800 96 ) 97 self._current_tab: GatherWindow.TabID | None = None 98 extra_top = 20 if uiscale is bui.UIScale.SMALL else 0 99 self._r = 'gatherWindow' 100 101 super().__init__( 102 root_widget=bui.containerwidget( 103 size=(self._width, self._height + extra_top), 104 toolbar_visibility=( 105 'menu_tokens' 106 if uiscale is bui.UIScale.SMALL 107 else 'menu_full' 108 ), 109 scale=( 110 1.15 111 if uiscale is bui.UIScale.SMALL 112 else 0.95 if uiscale is bui.UIScale.MEDIUM else 0.7 113 ), 114 stack_offset=( 115 (0, -20) 116 if uiscale is bui.UIScale.SMALL 117 else (0, 0) if uiscale is bui.UIScale.MEDIUM else (0, 0) 118 ), 119 ), 120 transition=transition, 121 origin_widget=origin_widget, 122 ) 123 124 if uiscale is bui.UIScale.SMALL: 125 bui.containerwidget( 126 edit=self._root_widget, on_cancel_call=self.main_window_back 127 ) 128 self._back_button = None 129 else: 130 self._back_button = btn = bui.buttonwidget( 131 parent=self._root_widget, 132 position=(70 + x_offs, self._height - 74), 133 size=(140, 60), 134 scale=1.1, 135 autoselect=True, 136 label=bui.Lstr(resource='backText'), 137 button_type='back', 138 on_activate_call=self.main_window_back, 139 ) 140 bui.containerwidget(edit=self._root_widget, cancel_button=btn) 141 bui.buttonwidget( 142 edit=btn, 143 button_type='backSmall', 144 position=(70 + x_offs, self._height - 78), 145 size=(60, 60), 146 label=bui.charstr(bui.SpecialChar.BACK), 147 ) 148 149 condensed = uiscale is not bui.UIScale.LARGE 150 t_offs_y = ( 151 0 if not condensed else 25 if uiscale is bui.UIScale.MEDIUM else 33 152 ) 153 bui.textwidget( 154 parent=self._root_widget, 155 position=(self._width * 0.5, self._height - 42 + t_offs_y), 156 size=(0, 0), 157 color=bui.app.ui_v1.title_color, 158 scale=( 159 1.5 160 if not condensed 161 else 1.0 if uiscale is bui.UIScale.MEDIUM else 1.0 162 ), 163 h_align='center', 164 v_align='center', 165 text=bui.Lstr(resource=f'{self._r}.titleText'), 166 maxwidth=320, 167 ) 168 169 scroll_buffer_h = 130 + 2 * x_offs 170 tab_buffer_h = (320 if condensed else 250) + 2 * x_offs 171 172 # Build up the set of tabs we want. 173 tabdefs: list[tuple[GatherWindow.TabID, bui.Lstr]] = [ 174 (self.TabID.ABOUT, bui.Lstr(resource=f'{self._r}.aboutText')) 175 ] 176 if plus.get_v1_account_misc_read_val('enablePublicParties', True): 177 tabdefs.append( 178 ( 179 self.TabID.INTERNET, 180 bui.Lstr(resource=f'{self._r}.publicText'), 181 ) 182 ) 183 tabdefs.append( 184 (self.TabID.PRIVATE, bui.Lstr(resource=f'{self._r}.privateText')) 185 ) 186 tabdefs.append( 187 (self.TabID.NEARBY, bui.Lstr(resource=f'{self._r}.nearbyText')) 188 ) 189 tabdefs.append( 190 (self.TabID.MANUAL, bui.Lstr(resource=f'{self._r}.manualText')) 191 ) 192 193 # On small UI, push our tabs up closer to the top of the screen to 194 # save a bit of space. 195 tabs_top_extra = 42 if condensed else 0 196 self._tab_row = TabRow( 197 self._root_widget, 198 tabdefs, 199 pos=(tab_buffer_h * 0.5, self._height - 130 + tabs_top_extra), 200 size=(self._width - tab_buffer_h, 50), 201 on_select_call=bui.WeakCall(self._set_tab), 202 ) 203 204 # Now instantiate handlers for these tabs. 205 tabtypes: dict[GatherWindow.TabID, type[GatherTab]] = { 206 self.TabID.ABOUT: AboutGatherTab, 207 self.TabID.MANUAL: ManualGatherTab, 208 self.TabID.PRIVATE: PrivateGatherTab, 209 self.TabID.INTERNET: PublicGatherTab, 210 self.TabID.NEARBY: NearbyGatherTab, 211 } 212 self._tabs: dict[GatherWindow.TabID, GatherTab] = {} 213 for tab_id in self._tab_row.tabs: 214 tabtype = tabtypes.get(tab_id) 215 if tabtype is not None: 216 self._tabs[tab_id] = tabtype(self) 217 218 bui.widget( 219 edit=self._tab_row.tabs[tabdefs[-1][0]].button, 220 right_widget=bui.get_special_widget('squad_button'), 221 ) 222 if uiscale is bui.UIScale.SMALL: 223 bui.widget( 224 edit=self._tab_row.tabs[tabdefs[0][0]].button, 225 left_widget=bui.get_special_widget('back_button'), 226 ) 227 228 self._scroll_width = self._width - scroll_buffer_h 229 self._scroll_height = self._height - 180.0 + tabs_top_extra 230 231 self._scroll_left = (self._width - self._scroll_width) * 0.5 232 self._scroll_bottom = ( 233 self._height - self._scroll_height - 79 - 48 + tabs_top_extra 234 ) 235 buffer_h = 10 236 buffer_v = 4 237 238 # Not actually using a scroll widget anymore; just an image. 239 bui.imagewidget( 240 parent=self._root_widget, 241 position=( 242 self._scroll_left - buffer_h, 243 self._scroll_bottom - buffer_v, 244 ), 245 size=( 246 self._scroll_width + 2 * buffer_h, 247 self._scroll_height + 2 * buffer_v, 248 ), 249 texture=bui.gettexture('scrollWidget'), 250 mesh_transparent=bui.getmesh('softEdgeOutside'), 251 ) 252 self._tab_container: bui.Widget | None = None 253 254 self._restore_state() 255 256 @override 257 def get_main_window_state(self) -> bui.MainWindowState: 258 # Support recreating our window for back/refresh purposes. 259 cls = type(self) 260 return bui.BasicMainWindowState( 261 create_call=lambda transition, origin_widget: cls( 262 transition=transition, origin_widget=origin_widget 263 ) 264 ) 265 266 @override 267 def on_main_window_close(self) -> None: 268 self._save_state() 269 270 def playlist_select( 271 self, 272 origin_widget: bui.Widget, 273 context: PlaylistSelectContext, 274 ) -> None: 275 """Called by the private-hosting tab to select a playlist.""" 276 from bauiv1lib.play import PlayWindow 277 278 # Avoid redundant window spawns. 279 if not self.main_window_has_control(): 280 return 281 282 playwindow = PlayWindow( 283 origin_widget=origin_widget, playlist_select_context=context 284 ) 285 self.main_window_replace(playwindow) 286 287 # Grab the newly-set main-window's back-state; that will lead us 288 # back here once we're done going down our main-window 289 # rabbit-hole for playlist selection. 290 context.back_state = playwindow.main_window_back_state 291 292 def _set_tab(self, tab_id: TabID) -> None: 293 if self._current_tab is tab_id: 294 return 295 prev_tab_id = self._current_tab 296 self._current_tab = tab_id 297 298 # We wanna preserve our current tab between runs. 299 cfg = bui.app.config 300 cfg['Gather Tab'] = tab_id.value 301 cfg.commit() 302 303 # Update tab colors based on which is selected. 304 self._tab_row.update_appearance(tab_id) 305 306 if prev_tab_id is not None: 307 prev_tab = self._tabs.get(prev_tab_id) 308 if prev_tab is not None: 309 prev_tab.on_deactivate() 310 311 # Clear up prev container if it hasn't been done. 312 if self._tab_container: 313 self._tab_container.delete() 314 315 tab = self._tabs.get(tab_id) 316 if tab is not None: 317 self._tab_container = tab.on_activate( 318 self._root_widget, 319 self._tab_row.tabs[tab_id].button, 320 self._scroll_width, 321 self._scroll_height, 322 self._scroll_left, 323 self._scroll_bottom, 324 ) 325 return 326 327 def _save_state(self) -> None: 328 try: 329 for tab in self._tabs.values(): 330 tab.save_state() 331 332 sel = self._root_widget.get_selected_child() 333 selected_tab_ids = [ 334 tab_id 335 for tab_id, tab in self._tab_row.tabs.items() 336 if sel == tab.button 337 ] 338 if sel == self._back_button: 339 sel_name = 'Back' 340 elif selected_tab_ids: 341 assert len(selected_tab_ids) == 1 342 sel_name = f'Tab:{selected_tab_ids[0].value}' 343 elif sel == self._tab_container: 344 sel_name = 'TabContainer' 345 else: 346 raise ValueError(f'unrecognized selection: \'{sel}\'') 347 assert bui.app.classic is not None 348 bui.app.ui_v1.window_states[type(self)] = { 349 'sel_name': sel_name, 350 } 351 except Exception: 352 logging.exception('Error saving state for %s.', self) 353 354 def _restore_state(self) -> None: 355 try: 356 for tab in self._tabs.values(): 357 tab.restore_state() 358 359 sel: bui.Widget | None 360 assert bui.app.classic is not None 361 winstate = bui.app.ui_v1.window_states.get(type(self), {}) 362 sel_name = winstate.get('sel_name', None) 363 assert isinstance(sel_name, (str, type(None))) 364 current_tab = self.TabID.ABOUT 365 gather_tab_val = bui.app.config.get('Gather Tab') 366 try: 367 stored_tab = self.TabID(gather_tab_val) 368 if stored_tab in self._tab_row.tabs: 369 current_tab = stored_tab 370 except ValueError: 371 pass 372 self._set_tab(current_tab) 373 if sel_name == 'Back': 374 sel = self._back_button 375 elif sel_name == 'TabContainer': 376 sel = self._tab_container 377 elif isinstance(sel_name, str) and sel_name.startswith('Tab:'): 378 try: 379 sel_tab_id = self.TabID(sel_name.split(':')[-1]) 380 except ValueError: 381 sel_tab_id = self.TabID.ABOUT 382 sel = self._tab_row.tabs[sel_tab_id].button 383 else: 384 sel = self._tab_row.tabs[current_tab].button 385 bui.containerwidget(edit=self._root_widget, selected_child=sel) 386 387 except Exception: 388 logging.exception('Error restoring state for %s.', self)
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 bui.NotFoundError("GatherTab's window no longer exists.") 32 return window 33 34 def on_activate( 35 self, 36 parent_widget: bui.Widget, 37 tab_button: bui.Widget, 38 region_width: float, 39 region_height: float, 40 region_left: float, 41 region_bottom: float, 42 ) -> bui.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 raise RuntimeError('Should not get here.') 49 50 def on_deactivate(self) -> None: 51 """Called when the tab will no longer be the active one.""" 52 53 def save_state(self) -> None: 54 """Called when the parent window is saving state.""" 55 56 def restore_state(self) -> None: 57 """Called when the parent window is restoring state."""
Defines a tab for use in the gather UI.
GatherTab(window: GatherWindow)
window: GatherWindow
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 bui.NotFoundError("GatherTab's window no longer exists.") 32 return window
The GatherWindow that this tab belongs to.
def
on_activate( self, parent_widget: _bauiv1.Widget, tab_button: _bauiv1.Widget, region_width: float, region_height: float, region_left: float, region_bottom: float) -> _bauiv1.Widget:
34 def on_activate( 35 self, 36 parent_widget: bui.Widget, 37 tab_button: bui.Widget, 38 region_width: float, 39 region_height: float, 40 region_left: float, 41 region_bottom: float, 42 ) -> bui.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 raise RuntimeError('Should not get here.')
Called when the tab becomes the active one.
The tab should create and return a container widget covering the specified region.
class
GatherWindow(bauiv1._uitypes.MainWindow):
60class GatherWindow(bui.MainWindow): 61 """Window for joining/inviting friends.""" 62 63 class TabID(Enum): 64 """Our available tab types.""" 65 66 ABOUT = 'about' 67 INTERNET = 'internet' 68 PRIVATE = 'private' 69 NEARBY = 'nearby' 70 MANUAL = 'manual' 71 72 def __init__( 73 self, 74 transition: str | None = 'in_right', 75 origin_widget: bui.Widget | None = None, 76 ): 77 # pylint: disable=too-many-statements 78 # pylint: disable=too-many-locals 79 # pylint: disable=cyclic-import 80 from bauiv1lib.gather.abouttab import AboutGatherTab 81 from bauiv1lib.gather.manualtab import ManualGatherTab 82 from bauiv1lib.gather.privatetab import PrivateGatherTab 83 from bauiv1lib.gather.publictab import PublicGatherTab 84 from bauiv1lib.gather.nearbytab import NearbyGatherTab 85 86 plus = bui.app.plus 87 assert plus is not None 88 89 bui.set_analytics_screen('Gather Window') 90 uiscale = bui.app.ui_v1.uiscale 91 self._width = 1640 if uiscale is bui.UIScale.SMALL else 1040 92 x_offs = 200 if uiscale is bui.UIScale.SMALL else 0 93 self._height = ( 94 550 95 if uiscale is bui.UIScale.SMALL 96 else 680 if uiscale is bui.UIScale.MEDIUM else 800 97 ) 98 self._current_tab: GatherWindow.TabID | None = None 99 extra_top = 20 if uiscale is bui.UIScale.SMALL else 0 100 self._r = 'gatherWindow' 101 102 super().__init__( 103 root_widget=bui.containerwidget( 104 size=(self._width, self._height + extra_top), 105 toolbar_visibility=( 106 'menu_tokens' 107 if uiscale is bui.UIScale.SMALL 108 else 'menu_full' 109 ), 110 scale=( 111 1.15 112 if uiscale is bui.UIScale.SMALL 113 else 0.95 if uiscale is bui.UIScale.MEDIUM else 0.7 114 ), 115 stack_offset=( 116 (0, -20) 117 if uiscale is bui.UIScale.SMALL 118 else (0, 0) if uiscale is bui.UIScale.MEDIUM else (0, 0) 119 ), 120 ), 121 transition=transition, 122 origin_widget=origin_widget, 123 ) 124 125 if uiscale is bui.UIScale.SMALL: 126 bui.containerwidget( 127 edit=self._root_widget, on_cancel_call=self.main_window_back 128 ) 129 self._back_button = None 130 else: 131 self._back_button = btn = bui.buttonwidget( 132 parent=self._root_widget, 133 position=(70 + x_offs, self._height - 74), 134 size=(140, 60), 135 scale=1.1, 136 autoselect=True, 137 label=bui.Lstr(resource='backText'), 138 button_type='back', 139 on_activate_call=self.main_window_back, 140 ) 141 bui.containerwidget(edit=self._root_widget, cancel_button=btn) 142 bui.buttonwidget( 143 edit=btn, 144 button_type='backSmall', 145 position=(70 + x_offs, self._height - 78), 146 size=(60, 60), 147 label=bui.charstr(bui.SpecialChar.BACK), 148 ) 149 150 condensed = uiscale is not bui.UIScale.LARGE 151 t_offs_y = ( 152 0 if not condensed else 25 if uiscale is bui.UIScale.MEDIUM else 33 153 ) 154 bui.textwidget( 155 parent=self._root_widget, 156 position=(self._width * 0.5, self._height - 42 + t_offs_y), 157 size=(0, 0), 158 color=bui.app.ui_v1.title_color, 159 scale=( 160 1.5 161 if not condensed 162 else 1.0 if uiscale is bui.UIScale.MEDIUM else 1.0 163 ), 164 h_align='center', 165 v_align='center', 166 text=bui.Lstr(resource=f'{self._r}.titleText'), 167 maxwidth=320, 168 ) 169 170 scroll_buffer_h = 130 + 2 * x_offs 171 tab_buffer_h = (320 if condensed else 250) + 2 * x_offs 172 173 # Build up the set of tabs we want. 174 tabdefs: list[tuple[GatherWindow.TabID, bui.Lstr]] = [ 175 (self.TabID.ABOUT, bui.Lstr(resource=f'{self._r}.aboutText')) 176 ] 177 if plus.get_v1_account_misc_read_val('enablePublicParties', True): 178 tabdefs.append( 179 ( 180 self.TabID.INTERNET, 181 bui.Lstr(resource=f'{self._r}.publicText'), 182 ) 183 ) 184 tabdefs.append( 185 (self.TabID.PRIVATE, bui.Lstr(resource=f'{self._r}.privateText')) 186 ) 187 tabdefs.append( 188 (self.TabID.NEARBY, bui.Lstr(resource=f'{self._r}.nearbyText')) 189 ) 190 tabdefs.append( 191 (self.TabID.MANUAL, bui.Lstr(resource=f'{self._r}.manualText')) 192 ) 193 194 # On small UI, push our tabs up closer to the top of the screen to 195 # save a bit of space. 196 tabs_top_extra = 42 if condensed else 0 197 self._tab_row = TabRow( 198 self._root_widget, 199 tabdefs, 200 pos=(tab_buffer_h * 0.5, self._height - 130 + tabs_top_extra), 201 size=(self._width - tab_buffer_h, 50), 202 on_select_call=bui.WeakCall(self._set_tab), 203 ) 204 205 # Now instantiate handlers for these tabs. 206 tabtypes: dict[GatherWindow.TabID, type[GatherTab]] = { 207 self.TabID.ABOUT: AboutGatherTab, 208 self.TabID.MANUAL: ManualGatherTab, 209 self.TabID.PRIVATE: PrivateGatherTab, 210 self.TabID.INTERNET: PublicGatherTab, 211 self.TabID.NEARBY: NearbyGatherTab, 212 } 213 self._tabs: dict[GatherWindow.TabID, GatherTab] = {} 214 for tab_id in self._tab_row.tabs: 215 tabtype = tabtypes.get(tab_id) 216 if tabtype is not None: 217 self._tabs[tab_id] = tabtype(self) 218 219 bui.widget( 220 edit=self._tab_row.tabs[tabdefs[-1][0]].button, 221 right_widget=bui.get_special_widget('squad_button'), 222 ) 223 if uiscale is bui.UIScale.SMALL: 224 bui.widget( 225 edit=self._tab_row.tabs[tabdefs[0][0]].button, 226 left_widget=bui.get_special_widget('back_button'), 227 ) 228 229 self._scroll_width = self._width - scroll_buffer_h 230 self._scroll_height = self._height - 180.0 + tabs_top_extra 231 232 self._scroll_left = (self._width - self._scroll_width) * 0.5 233 self._scroll_bottom = ( 234 self._height - self._scroll_height - 79 - 48 + tabs_top_extra 235 ) 236 buffer_h = 10 237 buffer_v = 4 238 239 # Not actually using a scroll widget anymore; just an image. 240 bui.imagewidget( 241 parent=self._root_widget, 242 position=( 243 self._scroll_left - buffer_h, 244 self._scroll_bottom - buffer_v, 245 ), 246 size=( 247 self._scroll_width + 2 * buffer_h, 248 self._scroll_height + 2 * buffer_v, 249 ), 250 texture=bui.gettexture('scrollWidget'), 251 mesh_transparent=bui.getmesh('softEdgeOutside'), 252 ) 253 self._tab_container: bui.Widget | None = None 254 255 self._restore_state() 256 257 @override 258 def get_main_window_state(self) -> bui.MainWindowState: 259 # Support recreating our window for back/refresh purposes. 260 cls = type(self) 261 return bui.BasicMainWindowState( 262 create_call=lambda transition, origin_widget: cls( 263 transition=transition, origin_widget=origin_widget 264 ) 265 ) 266 267 @override 268 def on_main_window_close(self) -> None: 269 self._save_state() 270 271 def playlist_select( 272 self, 273 origin_widget: bui.Widget, 274 context: PlaylistSelectContext, 275 ) -> None: 276 """Called by the private-hosting tab to select a playlist.""" 277 from bauiv1lib.play import PlayWindow 278 279 # Avoid redundant window spawns. 280 if not self.main_window_has_control(): 281 return 282 283 playwindow = PlayWindow( 284 origin_widget=origin_widget, playlist_select_context=context 285 ) 286 self.main_window_replace(playwindow) 287 288 # Grab the newly-set main-window's back-state; that will lead us 289 # back here once we're done going down our main-window 290 # rabbit-hole for playlist selection. 291 context.back_state = playwindow.main_window_back_state 292 293 def _set_tab(self, tab_id: TabID) -> None: 294 if self._current_tab is tab_id: 295 return 296 prev_tab_id = self._current_tab 297 self._current_tab = tab_id 298 299 # We wanna preserve our current tab between runs. 300 cfg = bui.app.config 301 cfg['Gather Tab'] = tab_id.value 302 cfg.commit() 303 304 # Update tab colors based on which is selected. 305 self._tab_row.update_appearance(tab_id) 306 307 if prev_tab_id is not None: 308 prev_tab = self._tabs.get(prev_tab_id) 309 if prev_tab is not None: 310 prev_tab.on_deactivate() 311 312 # Clear up prev container if it hasn't been done. 313 if self._tab_container: 314 self._tab_container.delete() 315 316 tab = self._tabs.get(tab_id) 317 if tab is not None: 318 self._tab_container = tab.on_activate( 319 self._root_widget, 320 self._tab_row.tabs[tab_id].button, 321 self._scroll_width, 322 self._scroll_height, 323 self._scroll_left, 324 self._scroll_bottom, 325 ) 326 return 327 328 def _save_state(self) -> None: 329 try: 330 for tab in self._tabs.values(): 331 tab.save_state() 332 333 sel = self._root_widget.get_selected_child() 334 selected_tab_ids = [ 335 tab_id 336 for tab_id, tab in self._tab_row.tabs.items() 337 if sel == tab.button 338 ] 339 if sel == self._back_button: 340 sel_name = 'Back' 341 elif selected_tab_ids: 342 assert len(selected_tab_ids) == 1 343 sel_name = f'Tab:{selected_tab_ids[0].value}' 344 elif sel == self._tab_container: 345 sel_name = 'TabContainer' 346 else: 347 raise ValueError(f'unrecognized selection: \'{sel}\'') 348 assert bui.app.classic is not None 349 bui.app.ui_v1.window_states[type(self)] = { 350 'sel_name': sel_name, 351 } 352 except Exception: 353 logging.exception('Error saving state for %s.', self) 354 355 def _restore_state(self) -> None: 356 try: 357 for tab in self._tabs.values(): 358 tab.restore_state() 359 360 sel: bui.Widget | None 361 assert bui.app.classic is not None 362 winstate = bui.app.ui_v1.window_states.get(type(self), {}) 363 sel_name = winstate.get('sel_name', None) 364 assert isinstance(sel_name, (str, type(None))) 365 current_tab = self.TabID.ABOUT 366 gather_tab_val = bui.app.config.get('Gather Tab') 367 try: 368 stored_tab = self.TabID(gather_tab_val) 369 if stored_tab in self._tab_row.tabs: 370 current_tab = stored_tab 371 except ValueError: 372 pass 373 self._set_tab(current_tab) 374 if sel_name == 'Back': 375 sel = self._back_button 376 elif sel_name == 'TabContainer': 377 sel = self._tab_container 378 elif isinstance(sel_name, str) and sel_name.startswith('Tab:'): 379 try: 380 sel_tab_id = self.TabID(sel_name.split(':')[-1]) 381 except ValueError: 382 sel_tab_id = self.TabID.ABOUT 383 sel = self._tab_row.tabs[sel_tab_id].button 384 else: 385 sel = self._tab_row.tabs[current_tab].button 386 bui.containerwidget(edit=self._root_widget, selected_child=sel) 387 388 except Exception: 389 logging.exception('Error restoring state for %s.', self)
Window for joining/inviting friends.
GatherWindow( transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
72 def __init__( 73 self, 74 transition: str | None = 'in_right', 75 origin_widget: bui.Widget | None = None, 76 ): 77 # pylint: disable=too-many-statements 78 # pylint: disable=too-many-locals 79 # pylint: disable=cyclic-import 80 from bauiv1lib.gather.abouttab import AboutGatherTab 81 from bauiv1lib.gather.manualtab import ManualGatherTab 82 from bauiv1lib.gather.privatetab import PrivateGatherTab 83 from bauiv1lib.gather.publictab import PublicGatherTab 84 from bauiv1lib.gather.nearbytab import NearbyGatherTab 85 86 plus = bui.app.plus 87 assert plus is not None 88 89 bui.set_analytics_screen('Gather Window') 90 uiscale = bui.app.ui_v1.uiscale 91 self._width = 1640 if uiscale is bui.UIScale.SMALL else 1040 92 x_offs = 200 if uiscale is bui.UIScale.SMALL else 0 93 self._height = ( 94 550 95 if uiscale is bui.UIScale.SMALL 96 else 680 if uiscale is bui.UIScale.MEDIUM else 800 97 ) 98 self._current_tab: GatherWindow.TabID | None = None 99 extra_top = 20 if uiscale is bui.UIScale.SMALL else 0 100 self._r = 'gatherWindow' 101 102 super().__init__( 103 root_widget=bui.containerwidget( 104 size=(self._width, self._height + extra_top), 105 toolbar_visibility=( 106 'menu_tokens' 107 if uiscale is bui.UIScale.SMALL 108 else 'menu_full' 109 ), 110 scale=( 111 1.15 112 if uiscale is bui.UIScale.SMALL 113 else 0.95 if uiscale is bui.UIScale.MEDIUM else 0.7 114 ), 115 stack_offset=( 116 (0, -20) 117 if uiscale is bui.UIScale.SMALL 118 else (0, 0) if uiscale is bui.UIScale.MEDIUM else (0, 0) 119 ), 120 ), 121 transition=transition, 122 origin_widget=origin_widget, 123 ) 124 125 if uiscale is bui.UIScale.SMALL: 126 bui.containerwidget( 127 edit=self._root_widget, on_cancel_call=self.main_window_back 128 ) 129 self._back_button = None 130 else: 131 self._back_button = btn = bui.buttonwidget( 132 parent=self._root_widget, 133 position=(70 + x_offs, self._height - 74), 134 size=(140, 60), 135 scale=1.1, 136 autoselect=True, 137 label=bui.Lstr(resource='backText'), 138 button_type='back', 139 on_activate_call=self.main_window_back, 140 ) 141 bui.containerwidget(edit=self._root_widget, cancel_button=btn) 142 bui.buttonwidget( 143 edit=btn, 144 button_type='backSmall', 145 position=(70 + x_offs, self._height - 78), 146 size=(60, 60), 147 label=bui.charstr(bui.SpecialChar.BACK), 148 ) 149 150 condensed = uiscale is not bui.UIScale.LARGE 151 t_offs_y = ( 152 0 if not condensed else 25 if uiscale is bui.UIScale.MEDIUM else 33 153 ) 154 bui.textwidget( 155 parent=self._root_widget, 156 position=(self._width * 0.5, self._height - 42 + t_offs_y), 157 size=(0, 0), 158 color=bui.app.ui_v1.title_color, 159 scale=( 160 1.5 161 if not condensed 162 else 1.0 if uiscale is bui.UIScale.MEDIUM else 1.0 163 ), 164 h_align='center', 165 v_align='center', 166 text=bui.Lstr(resource=f'{self._r}.titleText'), 167 maxwidth=320, 168 ) 169 170 scroll_buffer_h = 130 + 2 * x_offs 171 tab_buffer_h = (320 if condensed else 250) + 2 * x_offs 172 173 # Build up the set of tabs we want. 174 tabdefs: list[tuple[GatherWindow.TabID, bui.Lstr]] = [ 175 (self.TabID.ABOUT, bui.Lstr(resource=f'{self._r}.aboutText')) 176 ] 177 if plus.get_v1_account_misc_read_val('enablePublicParties', True): 178 tabdefs.append( 179 ( 180 self.TabID.INTERNET, 181 bui.Lstr(resource=f'{self._r}.publicText'), 182 ) 183 ) 184 tabdefs.append( 185 (self.TabID.PRIVATE, bui.Lstr(resource=f'{self._r}.privateText')) 186 ) 187 tabdefs.append( 188 (self.TabID.NEARBY, bui.Lstr(resource=f'{self._r}.nearbyText')) 189 ) 190 tabdefs.append( 191 (self.TabID.MANUAL, bui.Lstr(resource=f'{self._r}.manualText')) 192 ) 193 194 # On small UI, push our tabs up closer to the top of the screen to 195 # save a bit of space. 196 tabs_top_extra = 42 if condensed else 0 197 self._tab_row = TabRow( 198 self._root_widget, 199 tabdefs, 200 pos=(tab_buffer_h * 0.5, self._height - 130 + tabs_top_extra), 201 size=(self._width - tab_buffer_h, 50), 202 on_select_call=bui.WeakCall(self._set_tab), 203 ) 204 205 # Now instantiate handlers for these tabs. 206 tabtypes: dict[GatherWindow.TabID, type[GatherTab]] = { 207 self.TabID.ABOUT: AboutGatherTab, 208 self.TabID.MANUAL: ManualGatherTab, 209 self.TabID.PRIVATE: PrivateGatherTab, 210 self.TabID.INTERNET: PublicGatherTab, 211 self.TabID.NEARBY: NearbyGatherTab, 212 } 213 self._tabs: dict[GatherWindow.TabID, GatherTab] = {} 214 for tab_id in self._tab_row.tabs: 215 tabtype = tabtypes.get(tab_id) 216 if tabtype is not None: 217 self._tabs[tab_id] = tabtype(self) 218 219 bui.widget( 220 edit=self._tab_row.tabs[tabdefs[-1][0]].button, 221 right_widget=bui.get_special_widget('squad_button'), 222 ) 223 if uiscale is bui.UIScale.SMALL: 224 bui.widget( 225 edit=self._tab_row.tabs[tabdefs[0][0]].button, 226 left_widget=bui.get_special_widget('back_button'), 227 ) 228 229 self._scroll_width = self._width - scroll_buffer_h 230 self._scroll_height = self._height - 180.0 + tabs_top_extra 231 232 self._scroll_left = (self._width - self._scroll_width) * 0.5 233 self._scroll_bottom = ( 234 self._height - self._scroll_height - 79 - 48 + tabs_top_extra 235 ) 236 buffer_h = 10 237 buffer_v = 4 238 239 # Not actually using a scroll widget anymore; just an image. 240 bui.imagewidget( 241 parent=self._root_widget, 242 position=( 243 self._scroll_left - buffer_h, 244 self._scroll_bottom - buffer_v, 245 ), 246 size=( 247 self._scroll_width + 2 * buffer_h, 248 self._scroll_height + 2 * buffer_v, 249 ), 250 texture=bui.gettexture('scrollWidget'), 251 mesh_transparent=bui.getmesh('softEdgeOutside'), 252 ) 253 self._tab_container: bui.Widget | None = None 254 255 self._restore_state()
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.
257 @override 258 def get_main_window_state(self) -> bui.MainWindowState: 259 # Support recreating our window for back/refresh purposes. 260 cls = type(self) 261 return bui.BasicMainWindowState( 262 create_call=lambda transition, origin_widget: cls( 263 transition=transition, origin_widget=origin_widget 264 ) 265 )
Return a WindowState to recreate this window, if supported.
@override
def
on_main_window_close(self) -> None:
Called before transitioning out a main window.
A good opportunity to save window state/etc.
def
playlist_select( self, origin_widget: _bauiv1.Widget, context: bauiv1lib.play.PlaylistSelectContext) -> None:
271 def playlist_select( 272 self, 273 origin_widget: bui.Widget, 274 context: PlaylistSelectContext, 275 ) -> None: 276 """Called by the private-hosting tab to select a playlist.""" 277 from bauiv1lib.play import PlayWindow 278 279 # Avoid redundant window spawns. 280 if not self.main_window_has_control(): 281 return 282 283 playwindow = PlayWindow( 284 origin_widget=origin_widget, playlist_select_context=context 285 ) 286 self.main_window_replace(playwindow) 287 288 # Grab the newly-set main-window's back-state; that will lead us 289 # back here once we're done going down our main-window 290 # rabbit-hole for playlist selection. 291 context.back_state = playwindow.main_window_back_state
Called by the private-hosting tab to select a playlist.
Inherited Members
- bauiv1._uitypes.MainWindow
- main_window_back_state
- main_window_is_top_level
- main_window_close
- main_window_has_control
- main_window_back
- main_window_replace
- bauiv1._uitypes.Window
- get_root_widget
class
GatherWindow.TabID(enum.Enum):
63 class TabID(Enum): 64 """Our available tab types.""" 65 66 ABOUT = 'about' 67 INTERNET = 'internet' 68 PRIVATE = 'private' 69 NEARBY = 'nearby' 70 MANUAL = 'manual'
Our available tab types.
Inherited Members
- enum.Enum
- name
- value