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