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