bauiv1lib.playlist.edit
Provides a window for editing individual game playlists.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Provides a window for editing individual game playlists.""" 4 5from __future__ import annotations 6 7import logging 8from typing import TYPE_CHECKING, cast, override 9 10import bascenev1 as bs 11import bauiv1 as bui 12 13if TYPE_CHECKING: 14 from bauiv1lib.playlist.editcontroller import PlaylistEditController 15 16 17class PlaylistEditWindow(bui.MainWindow): 18 """Window for editing an individual game playlist.""" 19 20 def __init__( 21 self, 22 editcontroller: PlaylistEditController, 23 transition: str | None = 'in_right', 24 origin_widget: bui.Widget | None = None, 25 ): 26 # pylint: disable=too-many-statements 27 # pylint: disable=too-many-locals 28 prev_selection: str | None 29 self._editcontroller = editcontroller 30 self._r = 'editGameListWindow' 31 prev_selection = self._editcontroller.get_edit_ui_selection() 32 33 assert bui.app.classic is not None 34 uiscale = bui.app.ui_v1.uiscale 35 self._width = 870 if uiscale is bui.UIScale.SMALL else 670 36 x_inset = 100 if uiscale is bui.UIScale.SMALL else 0 37 self._height = ( 38 500 39 if uiscale is bui.UIScale.SMALL 40 else 470 if uiscale is bui.UIScale.MEDIUM else 540 41 ) 42 yoffs = -68 if uiscale is bui.UIScale.SMALL else 0 43 44 super().__init__( 45 root_widget=bui.containerwidget( 46 size=(self._width, self._height), 47 scale=( 48 1.76 49 if uiscale is bui.UIScale.SMALL 50 else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0 51 ), 52 stack_offset=( 53 (0, 0) if uiscale is bui.UIScale.SMALL else (0, 0) 54 ), 55 ), 56 transition=transition, 57 origin_widget=origin_widget, 58 ) 59 cancel_button = bui.buttonwidget( 60 parent=self._root_widget, 61 position=(35 + x_inset, self._height - 60 + yoffs), 62 scale=0.8, 63 size=(175, 60), 64 autoselect=True, 65 label=bui.Lstr(resource='cancelText'), 66 text_scale=1.2, 67 ) 68 save_button = btn = bui.buttonwidget( 69 parent=self._root_widget, 70 position=(self._width - (195 + x_inset), self._height - 60 + yoffs), 71 scale=0.8, 72 size=(190, 60), 73 autoselect=True, 74 left_widget=cancel_button, 75 label=bui.Lstr(resource='saveText'), 76 text_scale=1.2, 77 ) 78 79 bui.widget( 80 edit=btn, 81 right_widget=bui.get_special_widget('squad_button'), 82 ) 83 84 bui.widget( 85 edit=cancel_button, 86 left_widget=cancel_button, 87 right_widget=save_button, 88 ) 89 90 bui.textwidget( 91 parent=self._root_widget, 92 position=(-10, self._height - 50 + yoffs), 93 size=(self._width, 25), 94 text=bui.Lstr(resource=f'{self._r}.titleText'), 95 color=bui.app.ui_v1.title_color, 96 scale=1.05, 97 h_align='center', 98 v_align='center', 99 maxwidth=270, 100 ) 101 102 v = self._height - 115.0 + yoffs 103 104 self._scroll_width = self._width - (205 + 2 * x_inset) 105 106 bui.textwidget( 107 parent=self._root_widget, 108 text=bui.Lstr(resource=f'{self._r}.listNameText'), 109 position=(196 + x_inset, v + 31), 110 maxwidth=150, 111 color=(0.8, 0.8, 0.8, 0.5), 112 size=(0, 0), 113 scale=0.75, 114 h_align='right', 115 v_align='center', 116 ) 117 118 self._text_field = bui.textwidget( 119 parent=self._root_widget, 120 position=(210 + x_inset, v + 7), 121 size=(self._scroll_width - 53, 43), 122 text=self._editcontroller.getname(), 123 h_align='left', 124 v_align='center', 125 max_chars=40, 126 maxwidth=380, 127 autoselect=True, 128 color=(0.9, 0.9, 0.9, 1.0), 129 description=bui.Lstr(resource=f'{self._r}.listNameText'), 130 editable=True, 131 padding=4, 132 on_return_press_call=self._save_press_with_sound, 133 ) 134 bui.widget(edit=cancel_button, down_widget=self._text_field) 135 136 self._list_widgets: list[bui.Widget] = [] 137 138 h = 40 + x_inset 139 v = self._height - 172.0 + yoffs 140 141 b_color = (0.6, 0.53, 0.63) 142 b_textcolor = (0.75, 0.7, 0.8) 143 144 v -= 2.0 145 v += 63 146 147 scl = ( 148 1.03 149 if uiscale is bui.UIScale.SMALL 150 else 1.36 if uiscale is bui.UIScale.MEDIUM else 1.74 151 ) 152 v -= 63.0 * scl 153 154 add_game_button = bui.buttonwidget( 155 parent=self._root_widget, 156 position=(h, v), 157 size=(110, 61.0 * scl), 158 on_activate_call=self._add, 159 on_select_call=bui.Call(self._set_ui_selection, 'add_button'), 160 autoselect=True, 161 button_type='square', 162 color=b_color, 163 textcolor=b_textcolor, 164 text_scale=0.8, 165 label=bui.Lstr(resource=f'{self._r}.addGameText'), 166 ) 167 bui.widget(edit=add_game_button, up_widget=self._text_field) 168 v -= 63.0 * scl 169 170 self._edit_button = edit_game_button = bui.buttonwidget( 171 parent=self._root_widget, 172 position=(h, v), 173 size=(110, 61.0 * scl), 174 on_activate_call=self._edit, 175 on_select_call=bui.Call(self._set_ui_selection, 'editButton'), 176 autoselect=True, 177 button_type='square', 178 color=b_color, 179 textcolor=b_textcolor, 180 text_scale=0.8, 181 label=bui.Lstr(resource=f'{self._r}.editGameText'), 182 ) 183 v -= 63.0 * scl 184 185 remove_game_button = bui.buttonwidget( 186 parent=self._root_widget, 187 position=(h, v), 188 size=(110, 61.0 * scl), 189 text_scale=0.8, 190 on_activate_call=self._remove, 191 autoselect=True, 192 button_type='square', 193 color=b_color, 194 textcolor=b_textcolor, 195 label=bui.Lstr(resource=f'{self._r}.removeGameText'), 196 ) 197 v -= 40 198 h += 9 199 bui.buttonwidget( 200 parent=self._root_widget, 201 position=(h, v), 202 size=(42, 35), 203 on_activate_call=self._move_up, 204 label=bui.charstr(bui.SpecialChar.UP_ARROW), 205 button_type='square', 206 color=b_color, 207 textcolor=b_textcolor, 208 autoselect=True, 209 repeat=True, 210 ) 211 h += 52 212 bui.buttonwidget( 213 parent=self._root_widget, 214 position=(h, v), 215 size=(42, 35), 216 on_activate_call=self._move_down, 217 autoselect=True, 218 button_type='square', 219 color=b_color, 220 textcolor=b_textcolor, 221 label=bui.charstr(bui.SpecialChar.DOWN_ARROW), 222 repeat=True, 223 ) 224 225 v = self._height - 100 + yoffs 226 scroll_height = self._height - ( 227 250 if uiscale is bui.UIScale.SMALL else 155 228 ) 229 scrollwidget = bui.scrollwidget( 230 parent=self._root_widget, 231 position=(160 + x_inset, v - scroll_height), 232 highlight=False, 233 on_select_call=bui.Call(self._set_ui_selection, 'gameList'), 234 size=(self._scroll_width, (scroll_height - 15)), 235 ) 236 bui.widget( 237 edit=scrollwidget, 238 left_widget=add_game_button, 239 right_widget=scrollwidget, 240 ) 241 self._columnwidget = bui.columnwidget( 242 parent=scrollwidget, border=2, margin=0 243 ) 244 bui.widget(edit=self._columnwidget, up_widget=self._text_field) 245 246 for button in [add_game_button, edit_game_button, remove_game_button]: 247 bui.widget( 248 edit=button, left_widget=button, right_widget=scrollwidget 249 ) 250 251 self._refresh() 252 253 bui.buttonwidget(edit=cancel_button, on_activate_call=self._cancel) 254 bui.containerwidget( 255 edit=self._root_widget, 256 cancel_button=cancel_button, 257 selected_child=scrollwidget, 258 ) 259 260 bui.buttonwidget(edit=save_button, on_activate_call=self._save_press) 261 bui.containerwidget(edit=self._root_widget, start_button=save_button) 262 263 if prev_selection == 'add_button': 264 bui.containerwidget( 265 edit=self._root_widget, selected_child=add_game_button 266 ) 267 elif prev_selection == 'editButton': 268 bui.containerwidget( 269 edit=self._root_widget, selected_child=edit_game_button 270 ) 271 elif prev_selection == 'gameList': 272 bui.containerwidget( 273 edit=self._root_widget, selected_child=scrollwidget 274 ) 275 276 @override 277 def get_main_window_state(self) -> bui.MainWindowState: 278 # Support recreating our window for back/refresh purposes. 279 cls = type(self) 280 281 editcontroller = self._editcontroller 282 283 return bui.BasicMainWindowState( 284 create_call=lambda transition, origin_widget: cls( 285 transition=transition, 286 origin_widget=origin_widget, 287 editcontroller=editcontroller, 288 ) 289 ) 290 291 def _set_ui_selection(self, selection: str) -> None: 292 self._editcontroller.set_edit_ui_selection(selection) 293 294 def _cancel(self) -> None: 295 296 # no-op if our underlying widget is dead or on its way out. 297 if not self._root_widget or self._root_widget.transitioning_out: 298 return 299 300 bui.getsound('powerdown01').play() 301 self.main_window_back() 302 303 def _add(self) -> None: 304 # Store list name then tell the session to perform an add. 305 self._editcontroller.setname( 306 cast(str, bui.textwidget(query=self._text_field)) 307 ) 308 self._editcontroller.add_game_pressed(from_window=self) 309 310 def _edit(self) -> None: 311 # Store list name then tell the session to perform an add. 312 self._editcontroller.setname( 313 cast(str, bui.textwidget(query=self._text_field)) 314 ) 315 self._editcontroller.edit_game_pressed(from_window=self) 316 317 def _save_press(self) -> None: 318 319 # No-op if we're not in control. 320 if not self.main_window_has_control(): 321 return 322 323 # no-op if our underlying widget is dead or on its way out. 324 if not self._root_widget or self._root_widget.transitioning_out: 325 return 326 327 plus = bui.app.plus 328 assert plus is not None 329 330 new_name = cast(str, bui.textwidget(query=self._text_field)) 331 if ( 332 new_name != self._editcontroller.get_existing_playlist_name() 333 and new_name 334 in bui.app.config[ 335 self._editcontroller.get_config_name() + ' Playlists' 336 ] 337 ): 338 bui.screenmessage( 339 bui.Lstr(resource=f'{self._r}.cantSaveAlreadyExistsText') 340 ) 341 bui.getsound('error').play() 342 return 343 if not new_name: 344 bui.getsound('error').play() 345 return 346 if not self._editcontroller.get_playlist(): 347 bui.screenmessage( 348 bui.Lstr(resource=f'{self._r}.cantSaveEmptyListText') 349 ) 350 bui.getsound('error').play() 351 return 352 353 # We couldn't actually replace the default list anyway, but disallow 354 # using its exact name to avoid confusion. 355 if new_name == self._editcontroller.get_default_list_name().evaluate(): 356 bui.screenmessage( 357 bui.Lstr(resource=f'{self._r}.cantOverwriteDefaultText') 358 ) 359 bui.getsound('error').play() 360 return 361 362 # If we had an old one, delete it. 363 if self._editcontroller.get_existing_playlist_name() is not None: 364 plus.add_v1_account_transaction( 365 { 366 'type': 'REMOVE_PLAYLIST', 367 'playlistType': self._editcontroller.get_config_name(), 368 'playlistName': ( 369 self._editcontroller.get_existing_playlist_name() 370 ), 371 } 372 ) 373 374 plus.add_v1_account_transaction( 375 { 376 'type': 'ADD_PLAYLIST', 377 'playlistType': self._editcontroller.get_config_name(), 378 'playlistName': new_name, 379 'playlist': self._editcontroller.get_playlist(), 380 } 381 ) 382 plus.run_v1_account_transactions() 383 384 bui.getsound('gunCocking').play() 385 386 self.main_window_back() 387 388 def _save_press_with_sound(self) -> None: 389 bui.getsound('swish').play() 390 self._save_press() 391 392 def _select(self, index: int) -> None: 393 self._editcontroller.set_selected_index(index) 394 395 def _refresh(self) -> None: 396 # Need to grab this here as rebuilding the list will 397 # change it otherwise. 398 old_selection_index = self._editcontroller.get_selected_index() 399 400 while self._list_widgets: 401 self._list_widgets.pop().delete() 402 for index, pentry in enumerate(self._editcontroller.get_playlist()): 403 try: 404 cls = bui.getclass(pentry['type'], subclassof=bs.GameActivity) 405 desc = cls.get_settings_display_string(pentry) 406 except Exception: 407 logging.exception('Error in playlist refresh.') 408 desc = "(invalid: '" + pentry['type'] + "')" 409 410 txtw = bui.textwidget( 411 parent=self._columnwidget, 412 size=(self._width - 80, 30), 413 on_select_call=bui.Call(self._select, index), 414 always_highlight=True, 415 color=(0.8, 0.8, 0.8, 1.0), 416 padding=0, 417 maxwidth=self._scroll_width * 0.93, 418 text=desc, 419 on_activate_call=self._edit_button.activate, 420 v_align='center', 421 selectable=True, 422 ) 423 bui.widget(edit=txtw, show_buffer_top=50, show_buffer_bottom=50) 424 425 # Wanna be able to jump up to the text field from the top one. 426 if index == 0: 427 bui.widget(edit=txtw, up_widget=self._text_field) 428 self._list_widgets.append(txtw) 429 if old_selection_index == index: 430 bui.columnwidget( 431 edit=self._columnwidget, 432 selected_child=txtw, 433 visible_child=txtw, 434 ) 435 436 def _move_down(self) -> None: 437 playlist = self._editcontroller.get_playlist() 438 index = self._editcontroller.get_selected_index() 439 if index >= len(playlist) - 1: 440 return 441 tmp = playlist[index] 442 playlist[index] = playlist[index + 1] 443 playlist[index + 1] = tmp 444 index += 1 445 self._editcontroller.set_playlist(playlist) 446 self._editcontroller.set_selected_index(index) 447 self._refresh() 448 449 def _move_up(self) -> None: 450 playlist = self._editcontroller.get_playlist() 451 index = self._editcontroller.get_selected_index() 452 if index < 1: 453 return 454 tmp = playlist[index] 455 playlist[index] = playlist[index - 1] 456 playlist[index - 1] = tmp 457 index -= 1 458 self._editcontroller.set_playlist(playlist) 459 self._editcontroller.set_selected_index(index) 460 self._refresh() 461 462 def _remove(self) -> None: 463 playlist = self._editcontroller.get_playlist() 464 index = self._editcontroller.get_selected_index() 465 if not playlist: 466 return 467 del playlist[index] 468 if index >= len(playlist): 469 index = len(playlist) - 1 470 self._editcontroller.set_playlist(playlist) 471 self._editcontroller.set_selected_index(index) 472 bui.getsound('shieldDown').play() 473 self._refresh()
class
PlaylistEditWindow(bauiv1._uitypes.MainWindow):
18class PlaylistEditWindow(bui.MainWindow): 19 """Window for editing an individual game playlist.""" 20 21 def __init__( 22 self, 23 editcontroller: PlaylistEditController, 24 transition: str | None = 'in_right', 25 origin_widget: bui.Widget | None = None, 26 ): 27 # pylint: disable=too-many-statements 28 # pylint: disable=too-many-locals 29 prev_selection: str | None 30 self._editcontroller = editcontroller 31 self._r = 'editGameListWindow' 32 prev_selection = self._editcontroller.get_edit_ui_selection() 33 34 assert bui.app.classic is not None 35 uiscale = bui.app.ui_v1.uiscale 36 self._width = 870 if uiscale is bui.UIScale.SMALL else 670 37 x_inset = 100 if uiscale is bui.UIScale.SMALL else 0 38 self._height = ( 39 500 40 if uiscale is bui.UIScale.SMALL 41 else 470 if uiscale is bui.UIScale.MEDIUM else 540 42 ) 43 yoffs = -68 if uiscale is bui.UIScale.SMALL else 0 44 45 super().__init__( 46 root_widget=bui.containerwidget( 47 size=(self._width, self._height), 48 scale=( 49 1.76 50 if uiscale is bui.UIScale.SMALL 51 else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0 52 ), 53 stack_offset=( 54 (0, 0) if uiscale is bui.UIScale.SMALL else (0, 0) 55 ), 56 ), 57 transition=transition, 58 origin_widget=origin_widget, 59 ) 60 cancel_button = bui.buttonwidget( 61 parent=self._root_widget, 62 position=(35 + x_inset, self._height - 60 + yoffs), 63 scale=0.8, 64 size=(175, 60), 65 autoselect=True, 66 label=bui.Lstr(resource='cancelText'), 67 text_scale=1.2, 68 ) 69 save_button = btn = bui.buttonwidget( 70 parent=self._root_widget, 71 position=(self._width - (195 + x_inset), self._height - 60 + yoffs), 72 scale=0.8, 73 size=(190, 60), 74 autoselect=True, 75 left_widget=cancel_button, 76 label=bui.Lstr(resource='saveText'), 77 text_scale=1.2, 78 ) 79 80 bui.widget( 81 edit=btn, 82 right_widget=bui.get_special_widget('squad_button'), 83 ) 84 85 bui.widget( 86 edit=cancel_button, 87 left_widget=cancel_button, 88 right_widget=save_button, 89 ) 90 91 bui.textwidget( 92 parent=self._root_widget, 93 position=(-10, self._height - 50 + yoffs), 94 size=(self._width, 25), 95 text=bui.Lstr(resource=f'{self._r}.titleText'), 96 color=bui.app.ui_v1.title_color, 97 scale=1.05, 98 h_align='center', 99 v_align='center', 100 maxwidth=270, 101 ) 102 103 v = self._height - 115.0 + yoffs 104 105 self._scroll_width = self._width - (205 + 2 * x_inset) 106 107 bui.textwidget( 108 parent=self._root_widget, 109 text=bui.Lstr(resource=f'{self._r}.listNameText'), 110 position=(196 + x_inset, v + 31), 111 maxwidth=150, 112 color=(0.8, 0.8, 0.8, 0.5), 113 size=(0, 0), 114 scale=0.75, 115 h_align='right', 116 v_align='center', 117 ) 118 119 self._text_field = bui.textwidget( 120 parent=self._root_widget, 121 position=(210 + x_inset, v + 7), 122 size=(self._scroll_width - 53, 43), 123 text=self._editcontroller.getname(), 124 h_align='left', 125 v_align='center', 126 max_chars=40, 127 maxwidth=380, 128 autoselect=True, 129 color=(0.9, 0.9, 0.9, 1.0), 130 description=bui.Lstr(resource=f'{self._r}.listNameText'), 131 editable=True, 132 padding=4, 133 on_return_press_call=self._save_press_with_sound, 134 ) 135 bui.widget(edit=cancel_button, down_widget=self._text_field) 136 137 self._list_widgets: list[bui.Widget] = [] 138 139 h = 40 + x_inset 140 v = self._height - 172.0 + yoffs 141 142 b_color = (0.6, 0.53, 0.63) 143 b_textcolor = (0.75, 0.7, 0.8) 144 145 v -= 2.0 146 v += 63 147 148 scl = ( 149 1.03 150 if uiscale is bui.UIScale.SMALL 151 else 1.36 if uiscale is bui.UIScale.MEDIUM else 1.74 152 ) 153 v -= 63.0 * scl 154 155 add_game_button = bui.buttonwidget( 156 parent=self._root_widget, 157 position=(h, v), 158 size=(110, 61.0 * scl), 159 on_activate_call=self._add, 160 on_select_call=bui.Call(self._set_ui_selection, 'add_button'), 161 autoselect=True, 162 button_type='square', 163 color=b_color, 164 textcolor=b_textcolor, 165 text_scale=0.8, 166 label=bui.Lstr(resource=f'{self._r}.addGameText'), 167 ) 168 bui.widget(edit=add_game_button, up_widget=self._text_field) 169 v -= 63.0 * scl 170 171 self._edit_button = edit_game_button = bui.buttonwidget( 172 parent=self._root_widget, 173 position=(h, v), 174 size=(110, 61.0 * scl), 175 on_activate_call=self._edit, 176 on_select_call=bui.Call(self._set_ui_selection, 'editButton'), 177 autoselect=True, 178 button_type='square', 179 color=b_color, 180 textcolor=b_textcolor, 181 text_scale=0.8, 182 label=bui.Lstr(resource=f'{self._r}.editGameText'), 183 ) 184 v -= 63.0 * scl 185 186 remove_game_button = bui.buttonwidget( 187 parent=self._root_widget, 188 position=(h, v), 189 size=(110, 61.0 * scl), 190 text_scale=0.8, 191 on_activate_call=self._remove, 192 autoselect=True, 193 button_type='square', 194 color=b_color, 195 textcolor=b_textcolor, 196 label=bui.Lstr(resource=f'{self._r}.removeGameText'), 197 ) 198 v -= 40 199 h += 9 200 bui.buttonwidget( 201 parent=self._root_widget, 202 position=(h, v), 203 size=(42, 35), 204 on_activate_call=self._move_up, 205 label=bui.charstr(bui.SpecialChar.UP_ARROW), 206 button_type='square', 207 color=b_color, 208 textcolor=b_textcolor, 209 autoselect=True, 210 repeat=True, 211 ) 212 h += 52 213 bui.buttonwidget( 214 parent=self._root_widget, 215 position=(h, v), 216 size=(42, 35), 217 on_activate_call=self._move_down, 218 autoselect=True, 219 button_type='square', 220 color=b_color, 221 textcolor=b_textcolor, 222 label=bui.charstr(bui.SpecialChar.DOWN_ARROW), 223 repeat=True, 224 ) 225 226 v = self._height - 100 + yoffs 227 scroll_height = self._height - ( 228 250 if uiscale is bui.UIScale.SMALL else 155 229 ) 230 scrollwidget = bui.scrollwidget( 231 parent=self._root_widget, 232 position=(160 + x_inset, v - scroll_height), 233 highlight=False, 234 on_select_call=bui.Call(self._set_ui_selection, 'gameList'), 235 size=(self._scroll_width, (scroll_height - 15)), 236 ) 237 bui.widget( 238 edit=scrollwidget, 239 left_widget=add_game_button, 240 right_widget=scrollwidget, 241 ) 242 self._columnwidget = bui.columnwidget( 243 parent=scrollwidget, border=2, margin=0 244 ) 245 bui.widget(edit=self._columnwidget, up_widget=self._text_field) 246 247 for button in [add_game_button, edit_game_button, remove_game_button]: 248 bui.widget( 249 edit=button, left_widget=button, right_widget=scrollwidget 250 ) 251 252 self._refresh() 253 254 bui.buttonwidget(edit=cancel_button, on_activate_call=self._cancel) 255 bui.containerwidget( 256 edit=self._root_widget, 257 cancel_button=cancel_button, 258 selected_child=scrollwidget, 259 ) 260 261 bui.buttonwidget(edit=save_button, on_activate_call=self._save_press) 262 bui.containerwidget(edit=self._root_widget, start_button=save_button) 263 264 if prev_selection == 'add_button': 265 bui.containerwidget( 266 edit=self._root_widget, selected_child=add_game_button 267 ) 268 elif prev_selection == 'editButton': 269 bui.containerwidget( 270 edit=self._root_widget, selected_child=edit_game_button 271 ) 272 elif prev_selection == 'gameList': 273 bui.containerwidget( 274 edit=self._root_widget, selected_child=scrollwidget 275 ) 276 277 @override 278 def get_main_window_state(self) -> bui.MainWindowState: 279 # Support recreating our window for back/refresh purposes. 280 cls = type(self) 281 282 editcontroller = self._editcontroller 283 284 return bui.BasicMainWindowState( 285 create_call=lambda transition, origin_widget: cls( 286 transition=transition, 287 origin_widget=origin_widget, 288 editcontroller=editcontroller, 289 ) 290 ) 291 292 def _set_ui_selection(self, selection: str) -> None: 293 self._editcontroller.set_edit_ui_selection(selection) 294 295 def _cancel(self) -> None: 296 297 # no-op if our underlying widget is dead or on its way out. 298 if not self._root_widget or self._root_widget.transitioning_out: 299 return 300 301 bui.getsound('powerdown01').play() 302 self.main_window_back() 303 304 def _add(self) -> None: 305 # Store list name then tell the session to perform an add. 306 self._editcontroller.setname( 307 cast(str, bui.textwidget(query=self._text_field)) 308 ) 309 self._editcontroller.add_game_pressed(from_window=self) 310 311 def _edit(self) -> None: 312 # Store list name then tell the session to perform an add. 313 self._editcontroller.setname( 314 cast(str, bui.textwidget(query=self._text_field)) 315 ) 316 self._editcontroller.edit_game_pressed(from_window=self) 317 318 def _save_press(self) -> None: 319 320 # No-op if we're not in control. 321 if not self.main_window_has_control(): 322 return 323 324 # no-op if our underlying widget is dead or on its way out. 325 if not self._root_widget or self._root_widget.transitioning_out: 326 return 327 328 plus = bui.app.plus 329 assert plus is not None 330 331 new_name = cast(str, bui.textwidget(query=self._text_field)) 332 if ( 333 new_name != self._editcontroller.get_existing_playlist_name() 334 and new_name 335 in bui.app.config[ 336 self._editcontroller.get_config_name() + ' Playlists' 337 ] 338 ): 339 bui.screenmessage( 340 bui.Lstr(resource=f'{self._r}.cantSaveAlreadyExistsText') 341 ) 342 bui.getsound('error').play() 343 return 344 if not new_name: 345 bui.getsound('error').play() 346 return 347 if not self._editcontroller.get_playlist(): 348 bui.screenmessage( 349 bui.Lstr(resource=f'{self._r}.cantSaveEmptyListText') 350 ) 351 bui.getsound('error').play() 352 return 353 354 # We couldn't actually replace the default list anyway, but disallow 355 # using its exact name to avoid confusion. 356 if new_name == self._editcontroller.get_default_list_name().evaluate(): 357 bui.screenmessage( 358 bui.Lstr(resource=f'{self._r}.cantOverwriteDefaultText') 359 ) 360 bui.getsound('error').play() 361 return 362 363 # If we had an old one, delete it. 364 if self._editcontroller.get_existing_playlist_name() is not None: 365 plus.add_v1_account_transaction( 366 { 367 'type': 'REMOVE_PLAYLIST', 368 'playlistType': self._editcontroller.get_config_name(), 369 'playlistName': ( 370 self._editcontroller.get_existing_playlist_name() 371 ), 372 } 373 ) 374 375 plus.add_v1_account_transaction( 376 { 377 'type': 'ADD_PLAYLIST', 378 'playlistType': self._editcontroller.get_config_name(), 379 'playlistName': new_name, 380 'playlist': self._editcontroller.get_playlist(), 381 } 382 ) 383 plus.run_v1_account_transactions() 384 385 bui.getsound('gunCocking').play() 386 387 self.main_window_back() 388 389 def _save_press_with_sound(self) -> None: 390 bui.getsound('swish').play() 391 self._save_press() 392 393 def _select(self, index: int) -> None: 394 self._editcontroller.set_selected_index(index) 395 396 def _refresh(self) -> None: 397 # Need to grab this here as rebuilding the list will 398 # change it otherwise. 399 old_selection_index = self._editcontroller.get_selected_index() 400 401 while self._list_widgets: 402 self._list_widgets.pop().delete() 403 for index, pentry in enumerate(self._editcontroller.get_playlist()): 404 try: 405 cls = bui.getclass(pentry['type'], subclassof=bs.GameActivity) 406 desc = cls.get_settings_display_string(pentry) 407 except Exception: 408 logging.exception('Error in playlist refresh.') 409 desc = "(invalid: '" + pentry['type'] + "')" 410 411 txtw = bui.textwidget( 412 parent=self._columnwidget, 413 size=(self._width - 80, 30), 414 on_select_call=bui.Call(self._select, index), 415 always_highlight=True, 416 color=(0.8, 0.8, 0.8, 1.0), 417 padding=0, 418 maxwidth=self._scroll_width * 0.93, 419 text=desc, 420 on_activate_call=self._edit_button.activate, 421 v_align='center', 422 selectable=True, 423 ) 424 bui.widget(edit=txtw, show_buffer_top=50, show_buffer_bottom=50) 425 426 # Wanna be able to jump up to the text field from the top one. 427 if index == 0: 428 bui.widget(edit=txtw, up_widget=self._text_field) 429 self._list_widgets.append(txtw) 430 if old_selection_index == index: 431 bui.columnwidget( 432 edit=self._columnwidget, 433 selected_child=txtw, 434 visible_child=txtw, 435 ) 436 437 def _move_down(self) -> None: 438 playlist = self._editcontroller.get_playlist() 439 index = self._editcontroller.get_selected_index() 440 if index >= len(playlist) - 1: 441 return 442 tmp = playlist[index] 443 playlist[index] = playlist[index + 1] 444 playlist[index + 1] = tmp 445 index += 1 446 self._editcontroller.set_playlist(playlist) 447 self._editcontroller.set_selected_index(index) 448 self._refresh() 449 450 def _move_up(self) -> None: 451 playlist = self._editcontroller.get_playlist() 452 index = self._editcontroller.get_selected_index() 453 if index < 1: 454 return 455 tmp = playlist[index] 456 playlist[index] = playlist[index - 1] 457 playlist[index - 1] = tmp 458 index -= 1 459 self._editcontroller.set_playlist(playlist) 460 self._editcontroller.set_selected_index(index) 461 self._refresh() 462 463 def _remove(self) -> None: 464 playlist = self._editcontroller.get_playlist() 465 index = self._editcontroller.get_selected_index() 466 if not playlist: 467 return 468 del playlist[index] 469 if index >= len(playlist): 470 index = len(playlist) - 1 471 self._editcontroller.set_playlist(playlist) 472 self._editcontroller.set_selected_index(index) 473 bui.getsound('shieldDown').play() 474 self._refresh()
Window for editing an individual game playlist.
PlaylistEditWindow( editcontroller: bauiv1lib.playlist.editcontroller.PlaylistEditController, transition: str | None = 'in_right', origin_widget: _bauiv1.Widget | None = None)
21 def __init__( 22 self, 23 editcontroller: PlaylistEditController, 24 transition: str | None = 'in_right', 25 origin_widget: bui.Widget | None = None, 26 ): 27 # pylint: disable=too-many-statements 28 # pylint: disable=too-many-locals 29 prev_selection: str | None 30 self._editcontroller = editcontroller 31 self._r = 'editGameListWindow' 32 prev_selection = self._editcontroller.get_edit_ui_selection() 33 34 assert bui.app.classic is not None 35 uiscale = bui.app.ui_v1.uiscale 36 self._width = 870 if uiscale is bui.UIScale.SMALL else 670 37 x_inset = 100 if uiscale is bui.UIScale.SMALL else 0 38 self._height = ( 39 500 40 if uiscale is bui.UIScale.SMALL 41 else 470 if uiscale is bui.UIScale.MEDIUM else 540 42 ) 43 yoffs = -68 if uiscale is bui.UIScale.SMALL else 0 44 45 super().__init__( 46 root_widget=bui.containerwidget( 47 size=(self._width, self._height), 48 scale=( 49 1.76 50 if uiscale is bui.UIScale.SMALL 51 else 1.3 if uiscale is bui.UIScale.MEDIUM else 1.0 52 ), 53 stack_offset=( 54 (0, 0) if uiscale is bui.UIScale.SMALL else (0, 0) 55 ), 56 ), 57 transition=transition, 58 origin_widget=origin_widget, 59 ) 60 cancel_button = bui.buttonwidget( 61 parent=self._root_widget, 62 position=(35 + x_inset, self._height - 60 + yoffs), 63 scale=0.8, 64 size=(175, 60), 65 autoselect=True, 66 label=bui.Lstr(resource='cancelText'), 67 text_scale=1.2, 68 ) 69 save_button = btn = bui.buttonwidget( 70 parent=self._root_widget, 71 position=(self._width - (195 + x_inset), self._height - 60 + yoffs), 72 scale=0.8, 73 size=(190, 60), 74 autoselect=True, 75 left_widget=cancel_button, 76 label=bui.Lstr(resource='saveText'), 77 text_scale=1.2, 78 ) 79 80 bui.widget( 81 edit=btn, 82 right_widget=bui.get_special_widget('squad_button'), 83 ) 84 85 bui.widget( 86 edit=cancel_button, 87 left_widget=cancel_button, 88 right_widget=save_button, 89 ) 90 91 bui.textwidget( 92 parent=self._root_widget, 93 position=(-10, self._height - 50 + yoffs), 94 size=(self._width, 25), 95 text=bui.Lstr(resource=f'{self._r}.titleText'), 96 color=bui.app.ui_v1.title_color, 97 scale=1.05, 98 h_align='center', 99 v_align='center', 100 maxwidth=270, 101 ) 102 103 v = self._height - 115.0 + yoffs 104 105 self._scroll_width = self._width - (205 + 2 * x_inset) 106 107 bui.textwidget( 108 parent=self._root_widget, 109 text=bui.Lstr(resource=f'{self._r}.listNameText'), 110 position=(196 + x_inset, v + 31), 111 maxwidth=150, 112 color=(0.8, 0.8, 0.8, 0.5), 113 size=(0, 0), 114 scale=0.75, 115 h_align='right', 116 v_align='center', 117 ) 118 119 self._text_field = bui.textwidget( 120 parent=self._root_widget, 121 position=(210 + x_inset, v + 7), 122 size=(self._scroll_width - 53, 43), 123 text=self._editcontroller.getname(), 124 h_align='left', 125 v_align='center', 126 max_chars=40, 127 maxwidth=380, 128 autoselect=True, 129 color=(0.9, 0.9, 0.9, 1.0), 130 description=bui.Lstr(resource=f'{self._r}.listNameText'), 131 editable=True, 132 padding=4, 133 on_return_press_call=self._save_press_with_sound, 134 ) 135 bui.widget(edit=cancel_button, down_widget=self._text_field) 136 137 self._list_widgets: list[bui.Widget] = [] 138 139 h = 40 + x_inset 140 v = self._height - 172.0 + yoffs 141 142 b_color = (0.6, 0.53, 0.63) 143 b_textcolor = (0.75, 0.7, 0.8) 144 145 v -= 2.0 146 v += 63 147 148 scl = ( 149 1.03 150 if uiscale is bui.UIScale.SMALL 151 else 1.36 if uiscale is bui.UIScale.MEDIUM else 1.74 152 ) 153 v -= 63.0 * scl 154 155 add_game_button = bui.buttonwidget( 156 parent=self._root_widget, 157 position=(h, v), 158 size=(110, 61.0 * scl), 159 on_activate_call=self._add, 160 on_select_call=bui.Call(self._set_ui_selection, 'add_button'), 161 autoselect=True, 162 button_type='square', 163 color=b_color, 164 textcolor=b_textcolor, 165 text_scale=0.8, 166 label=bui.Lstr(resource=f'{self._r}.addGameText'), 167 ) 168 bui.widget(edit=add_game_button, up_widget=self._text_field) 169 v -= 63.0 * scl 170 171 self._edit_button = edit_game_button = bui.buttonwidget( 172 parent=self._root_widget, 173 position=(h, v), 174 size=(110, 61.0 * scl), 175 on_activate_call=self._edit, 176 on_select_call=bui.Call(self._set_ui_selection, 'editButton'), 177 autoselect=True, 178 button_type='square', 179 color=b_color, 180 textcolor=b_textcolor, 181 text_scale=0.8, 182 label=bui.Lstr(resource=f'{self._r}.editGameText'), 183 ) 184 v -= 63.0 * scl 185 186 remove_game_button = bui.buttonwidget( 187 parent=self._root_widget, 188 position=(h, v), 189 size=(110, 61.0 * scl), 190 text_scale=0.8, 191 on_activate_call=self._remove, 192 autoselect=True, 193 button_type='square', 194 color=b_color, 195 textcolor=b_textcolor, 196 label=bui.Lstr(resource=f'{self._r}.removeGameText'), 197 ) 198 v -= 40 199 h += 9 200 bui.buttonwidget( 201 parent=self._root_widget, 202 position=(h, v), 203 size=(42, 35), 204 on_activate_call=self._move_up, 205 label=bui.charstr(bui.SpecialChar.UP_ARROW), 206 button_type='square', 207 color=b_color, 208 textcolor=b_textcolor, 209 autoselect=True, 210 repeat=True, 211 ) 212 h += 52 213 bui.buttonwidget( 214 parent=self._root_widget, 215 position=(h, v), 216 size=(42, 35), 217 on_activate_call=self._move_down, 218 autoselect=True, 219 button_type='square', 220 color=b_color, 221 textcolor=b_textcolor, 222 label=bui.charstr(bui.SpecialChar.DOWN_ARROW), 223 repeat=True, 224 ) 225 226 v = self._height - 100 + yoffs 227 scroll_height = self._height - ( 228 250 if uiscale is bui.UIScale.SMALL else 155 229 ) 230 scrollwidget = bui.scrollwidget( 231 parent=self._root_widget, 232 position=(160 + x_inset, v - scroll_height), 233 highlight=False, 234 on_select_call=bui.Call(self._set_ui_selection, 'gameList'), 235 size=(self._scroll_width, (scroll_height - 15)), 236 ) 237 bui.widget( 238 edit=scrollwidget, 239 left_widget=add_game_button, 240 right_widget=scrollwidget, 241 ) 242 self._columnwidget = bui.columnwidget( 243 parent=scrollwidget, border=2, margin=0 244 ) 245 bui.widget(edit=self._columnwidget, up_widget=self._text_field) 246 247 for button in [add_game_button, edit_game_button, remove_game_button]: 248 bui.widget( 249 edit=button, left_widget=button, right_widget=scrollwidget 250 ) 251 252 self._refresh() 253 254 bui.buttonwidget(edit=cancel_button, on_activate_call=self._cancel) 255 bui.containerwidget( 256 edit=self._root_widget, 257 cancel_button=cancel_button, 258 selected_child=scrollwidget, 259 ) 260 261 bui.buttonwidget(edit=save_button, on_activate_call=self._save_press) 262 bui.containerwidget(edit=self._root_widget, start_button=save_button) 263 264 if prev_selection == 'add_button': 265 bui.containerwidget( 266 edit=self._root_widget, selected_child=add_game_button 267 ) 268 elif prev_selection == 'editButton': 269 bui.containerwidget( 270 edit=self._root_widget, selected_child=edit_game_button 271 ) 272 elif prev_selection == 'gameList': 273 bui.containerwidget( 274 edit=self._root_widget, selected_child=scrollwidget 275 )
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.
277 @override 278 def get_main_window_state(self) -> bui.MainWindowState: 279 # Support recreating our window for back/refresh purposes. 280 cls = type(self) 281 282 editcontroller = self._editcontroller 283 284 return bui.BasicMainWindowState( 285 create_call=lambda transition, origin_widget: cls( 286 transition=transition, 287 origin_widget=origin_widget, 288 editcontroller=editcontroller, 289 ) 290 )
Return a WindowState to recreate this window, if supported.