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 400 39 if uiscale is bui.UIScale.SMALL 40 else 470 if uiscale is bui.UIScale.MEDIUM else 540 41 ) 42 43 top_extra = 20 if uiscale is bui.UIScale.SMALL else 0 44 super().__init__( 45 root_widget=bui.containerwidget( 46 size=(self._width, self._height + top_extra), 47 scale=( 48 1.8 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, -16) 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), 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), 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), 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 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 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 226 scroll_height = self._height - 155 227 scrollwidget = bui.scrollwidget( 228 parent=self._root_widget, 229 position=(160 + x_inset, v - scroll_height), 230 highlight=False, 231 on_select_call=bui.Call(self._set_ui_selection, 'gameList'), 232 size=(self._scroll_width, (scroll_height - 15)), 233 ) 234 bui.widget( 235 edit=scrollwidget, 236 left_widget=add_game_button, 237 right_widget=scrollwidget, 238 ) 239 self._columnwidget = bui.columnwidget( 240 parent=scrollwidget, border=2, margin=0 241 ) 242 bui.widget(edit=self._columnwidget, up_widget=self._text_field) 243 244 for button in [add_game_button, edit_game_button, remove_game_button]: 245 bui.widget( 246 edit=button, left_widget=button, right_widget=scrollwidget 247 ) 248 249 self._refresh() 250 251 bui.buttonwidget(edit=cancel_button, on_activate_call=self._cancel) 252 bui.containerwidget( 253 edit=self._root_widget, 254 cancel_button=cancel_button, 255 selected_child=scrollwidget, 256 ) 257 258 bui.buttonwidget(edit=save_button, on_activate_call=self._save_press) 259 bui.containerwidget(edit=self._root_widget, start_button=save_button) 260 261 if prev_selection == 'add_button': 262 bui.containerwidget( 263 edit=self._root_widget, selected_child=add_game_button 264 ) 265 elif prev_selection == 'editButton': 266 bui.containerwidget( 267 edit=self._root_widget, selected_child=edit_game_button 268 ) 269 elif prev_selection == 'gameList': 270 bui.containerwidget( 271 edit=self._root_widget, selected_child=scrollwidget 272 ) 273 274 @override 275 def get_main_window_state(self) -> bui.MainWindowState: 276 # Support recreating our window for back/refresh purposes. 277 cls = type(self) 278 279 editcontroller = self._editcontroller 280 281 return bui.BasicMainWindowState( 282 create_call=lambda transition, origin_widget: cls( 283 transition=transition, 284 origin_widget=origin_widget, 285 editcontroller=editcontroller, 286 ) 287 ) 288 289 def _set_ui_selection(self, selection: str) -> None: 290 self._editcontroller.set_edit_ui_selection(selection) 291 292 def _cancel(self) -> None: 293 294 # no-op if our underlying widget is dead or on its way out. 295 if not self._root_widget or self._root_widget.transitioning_out: 296 return 297 298 bui.getsound('powerdown01').play() 299 self.main_window_back() 300 301 def _add(self) -> None: 302 # Store list name then tell the session to perform an add. 303 self._editcontroller.setname( 304 cast(str, bui.textwidget(query=self._text_field)) 305 ) 306 self._editcontroller.add_game_pressed(from_window=self) 307 308 def _edit(self) -> None: 309 # Store list name then tell the session to perform an add. 310 self._editcontroller.setname( 311 cast(str, bui.textwidget(query=self._text_field)) 312 ) 313 self._editcontroller.edit_game_pressed(from_window=self) 314 315 def _save_press(self) -> None: 316 317 # No-op if we're not in control. 318 if not self.main_window_has_control(): 319 return 320 321 # no-op if our underlying widget is dead or on its way out. 322 if not self._root_widget or self._root_widget.transitioning_out: 323 return 324 325 plus = bui.app.plus 326 assert plus is not None 327 328 new_name = cast(str, bui.textwidget(query=self._text_field)) 329 if ( 330 new_name != self._editcontroller.get_existing_playlist_name() 331 and new_name 332 in bui.app.config[ 333 self._editcontroller.get_config_name() + ' Playlists' 334 ] 335 ): 336 bui.screenmessage( 337 bui.Lstr(resource=f'{self._r}.cantSaveAlreadyExistsText') 338 ) 339 bui.getsound('error').play() 340 return 341 if not new_name: 342 bui.getsound('error').play() 343 return 344 if not self._editcontroller.get_playlist(): 345 bui.screenmessage( 346 bui.Lstr(resource=f'{self._r}.cantSaveEmptyListText') 347 ) 348 bui.getsound('error').play() 349 return 350 351 # We couldn't actually replace the default list anyway, but disallow 352 # using its exact name to avoid confusion. 353 if new_name == self._editcontroller.get_default_list_name().evaluate(): 354 bui.screenmessage( 355 bui.Lstr(resource=f'{self._r}.cantOverwriteDefaultText') 356 ) 357 bui.getsound('error').play() 358 return 359 360 # If we had an old one, delete it. 361 if self._editcontroller.get_existing_playlist_name() is not None: 362 plus.add_v1_account_transaction( 363 { 364 'type': 'REMOVE_PLAYLIST', 365 'playlistType': self._editcontroller.get_config_name(), 366 'playlistName': ( 367 self._editcontroller.get_existing_playlist_name() 368 ), 369 } 370 ) 371 372 plus.add_v1_account_transaction( 373 { 374 'type': 'ADD_PLAYLIST', 375 'playlistType': self._editcontroller.get_config_name(), 376 'playlistName': new_name, 377 'playlist': self._editcontroller.get_playlist(), 378 } 379 ) 380 plus.run_v1_account_transactions() 381 382 bui.getsound('gunCocking').play() 383 384 self.main_window_back() 385 386 def _save_press_with_sound(self) -> None: 387 bui.getsound('swish').play() 388 self._save_press() 389 390 def _select(self, index: int) -> None: 391 self._editcontroller.set_selected_index(index) 392 393 def _refresh(self) -> None: 394 # Need to grab this here as rebuilding the list will 395 # change it otherwise. 396 old_selection_index = self._editcontroller.get_selected_index() 397 398 while self._list_widgets: 399 self._list_widgets.pop().delete() 400 for index, pentry in enumerate(self._editcontroller.get_playlist()): 401 try: 402 cls = bui.getclass(pentry['type'], subclassof=bs.GameActivity) 403 desc = cls.get_settings_display_string(pentry) 404 except Exception: 405 logging.exception('Error in playlist refresh.') 406 desc = "(invalid: '" + pentry['type'] + "')" 407 408 txtw = bui.textwidget( 409 parent=self._columnwidget, 410 size=(self._width - 80, 30), 411 on_select_call=bui.Call(self._select, index), 412 always_highlight=True, 413 color=(0.8, 0.8, 0.8, 1.0), 414 padding=0, 415 maxwidth=self._scroll_width * 0.93, 416 text=desc, 417 on_activate_call=self._edit_button.activate, 418 v_align='center', 419 selectable=True, 420 ) 421 bui.widget(edit=txtw, show_buffer_top=50, show_buffer_bottom=50) 422 423 # Wanna be able to jump up to the text field from the top one. 424 if index == 0: 425 bui.widget(edit=txtw, up_widget=self._text_field) 426 self._list_widgets.append(txtw) 427 if old_selection_index == index: 428 bui.columnwidget( 429 edit=self._columnwidget, 430 selected_child=txtw, 431 visible_child=txtw, 432 ) 433 434 def _move_down(self) -> None: 435 playlist = self._editcontroller.get_playlist() 436 index = self._editcontroller.get_selected_index() 437 if index >= len(playlist) - 1: 438 return 439 tmp = playlist[index] 440 playlist[index] = playlist[index + 1] 441 playlist[index + 1] = tmp 442 index += 1 443 self._editcontroller.set_playlist(playlist) 444 self._editcontroller.set_selected_index(index) 445 self._refresh() 446 447 def _move_up(self) -> None: 448 playlist = self._editcontroller.get_playlist() 449 index = self._editcontroller.get_selected_index() 450 if index < 1: 451 return 452 tmp = playlist[index] 453 playlist[index] = playlist[index - 1] 454 playlist[index - 1] = tmp 455 index -= 1 456 self._editcontroller.set_playlist(playlist) 457 self._editcontroller.set_selected_index(index) 458 self._refresh() 459 460 def _remove(self) -> None: 461 playlist = self._editcontroller.get_playlist() 462 index = self._editcontroller.get_selected_index() 463 if not playlist: 464 return 465 del playlist[index] 466 if index >= len(playlist): 467 index = len(playlist) - 1 468 self._editcontroller.set_playlist(playlist) 469 self._editcontroller.set_selected_index(index) 470 bui.getsound('shieldDown').play() 471 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 400 40 if uiscale is bui.UIScale.SMALL 41 else 470 if uiscale is bui.UIScale.MEDIUM else 540 42 ) 43 44 top_extra = 20 if uiscale is bui.UIScale.SMALL else 0 45 super().__init__( 46 root_widget=bui.containerwidget( 47 size=(self._width, self._height + top_extra), 48 scale=( 49 1.8 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, -16) 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), 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), 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), 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 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 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 227 scroll_height = self._height - 155 228 scrollwidget = bui.scrollwidget( 229 parent=self._root_widget, 230 position=(160 + x_inset, v - scroll_height), 231 highlight=False, 232 on_select_call=bui.Call(self._set_ui_selection, 'gameList'), 233 size=(self._scroll_width, (scroll_height - 15)), 234 ) 235 bui.widget( 236 edit=scrollwidget, 237 left_widget=add_game_button, 238 right_widget=scrollwidget, 239 ) 240 self._columnwidget = bui.columnwidget( 241 parent=scrollwidget, border=2, margin=0 242 ) 243 bui.widget(edit=self._columnwidget, up_widget=self._text_field) 244 245 for button in [add_game_button, edit_game_button, remove_game_button]: 246 bui.widget( 247 edit=button, left_widget=button, right_widget=scrollwidget 248 ) 249 250 self._refresh() 251 252 bui.buttonwidget(edit=cancel_button, on_activate_call=self._cancel) 253 bui.containerwidget( 254 edit=self._root_widget, 255 cancel_button=cancel_button, 256 selected_child=scrollwidget, 257 ) 258 259 bui.buttonwidget(edit=save_button, on_activate_call=self._save_press) 260 bui.containerwidget(edit=self._root_widget, start_button=save_button) 261 262 if prev_selection == 'add_button': 263 bui.containerwidget( 264 edit=self._root_widget, selected_child=add_game_button 265 ) 266 elif prev_selection == 'editButton': 267 bui.containerwidget( 268 edit=self._root_widget, selected_child=edit_game_button 269 ) 270 elif prev_selection == 'gameList': 271 bui.containerwidget( 272 edit=self._root_widget, selected_child=scrollwidget 273 ) 274 275 @override 276 def get_main_window_state(self) -> bui.MainWindowState: 277 # Support recreating our window for back/refresh purposes. 278 cls = type(self) 279 280 editcontroller = self._editcontroller 281 282 return bui.BasicMainWindowState( 283 create_call=lambda transition, origin_widget: cls( 284 transition=transition, 285 origin_widget=origin_widget, 286 editcontroller=editcontroller, 287 ) 288 ) 289 290 def _set_ui_selection(self, selection: str) -> None: 291 self._editcontroller.set_edit_ui_selection(selection) 292 293 def _cancel(self) -> None: 294 295 # no-op if our underlying widget is dead or on its way out. 296 if not self._root_widget or self._root_widget.transitioning_out: 297 return 298 299 bui.getsound('powerdown01').play() 300 self.main_window_back() 301 302 def _add(self) -> None: 303 # Store list name then tell the session to perform an add. 304 self._editcontroller.setname( 305 cast(str, bui.textwidget(query=self._text_field)) 306 ) 307 self._editcontroller.add_game_pressed(from_window=self) 308 309 def _edit(self) -> None: 310 # Store list name then tell the session to perform an add. 311 self._editcontroller.setname( 312 cast(str, bui.textwidget(query=self._text_field)) 313 ) 314 self._editcontroller.edit_game_pressed(from_window=self) 315 316 def _save_press(self) -> None: 317 318 # No-op if we're not in control. 319 if not self.main_window_has_control(): 320 return 321 322 # no-op if our underlying widget is dead or on its way out. 323 if not self._root_widget or self._root_widget.transitioning_out: 324 return 325 326 plus = bui.app.plus 327 assert plus is not None 328 329 new_name = cast(str, bui.textwidget(query=self._text_field)) 330 if ( 331 new_name != self._editcontroller.get_existing_playlist_name() 332 and new_name 333 in bui.app.config[ 334 self._editcontroller.get_config_name() + ' Playlists' 335 ] 336 ): 337 bui.screenmessage( 338 bui.Lstr(resource=f'{self._r}.cantSaveAlreadyExistsText') 339 ) 340 bui.getsound('error').play() 341 return 342 if not new_name: 343 bui.getsound('error').play() 344 return 345 if not self._editcontroller.get_playlist(): 346 bui.screenmessage( 347 bui.Lstr(resource=f'{self._r}.cantSaveEmptyListText') 348 ) 349 bui.getsound('error').play() 350 return 351 352 # We couldn't actually replace the default list anyway, but disallow 353 # using its exact name to avoid confusion. 354 if new_name == self._editcontroller.get_default_list_name().evaluate(): 355 bui.screenmessage( 356 bui.Lstr(resource=f'{self._r}.cantOverwriteDefaultText') 357 ) 358 bui.getsound('error').play() 359 return 360 361 # If we had an old one, delete it. 362 if self._editcontroller.get_existing_playlist_name() is not None: 363 plus.add_v1_account_transaction( 364 { 365 'type': 'REMOVE_PLAYLIST', 366 'playlistType': self._editcontroller.get_config_name(), 367 'playlistName': ( 368 self._editcontroller.get_existing_playlist_name() 369 ), 370 } 371 ) 372 373 plus.add_v1_account_transaction( 374 { 375 'type': 'ADD_PLAYLIST', 376 'playlistType': self._editcontroller.get_config_name(), 377 'playlistName': new_name, 378 'playlist': self._editcontroller.get_playlist(), 379 } 380 ) 381 plus.run_v1_account_transactions() 382 383 bui.getsound('gunCocking').play() 384 385 self.main_window_back() 386 387 def _save_press_with_sound(self) -> None: 388 bui.getsound('swish').play() 389 self._save_press() 390 391 def _select(self, index: int) -> None: 392 self._editcontroller.set_selected_index(index) 393 394 def _refresh(self) -> None: 395 # Need to grab this here as rebuilding the list will 396 # change it otherwise. 397 old_selection_index = self._editcontroller.get_selected_index() 398 399 while self._list_widgets: 400 self._list_widgets.pop().delete() 401 for index, pentry in enumerate(self._editcontroller.get_playlist()): 402 try: 403 cls = bui.getclass(pentry['type'], subclassof=bs.GameActivity) 404 desc = cls.get_settings_display_string(pentry) 405 except Exception: 406 logging.exception('Error in playlist refresh.') 407 desc = "(invalid: '" + pentry['type'] + "')" 408 409 txtw = bui.textwidget( 410 parent=self._columnwidget, 411 size=(self._width - 80, 30), 412 on_select_call=bui.Call(self._select, index), 413 always_highlight=True, 414 color=(0.8, 0.8, 0.8, 1.0), 415 padding=0, 416 maxwidth=self._scroll_width * 0.93, 417 text=desc, 418 on_activate_call=self._edit_button.activate, 419 v_align='center', 420 selectable=True, 421 ) 422 bui.widget(edit=txtw, show_buffer_top=50, show_buffer_bottom=50) 423 424 # Wanna be able to jump up to the text field from the top one. 425 if index == 0: 426 bui.widget(edit=txtw, up_widget=self._text_field) 427 self._list_widgets.append(txtw) 428 if old_selection_index == index: 429 bui.columnwidget( 430 edit=self._columnwidget, 431 selected_child=txtw, 432 visible_child=txtw, 433 ) 434 435 def _move_down(self) -> None: 436 playlist = self._editcontroller.get_playlist() 437 index = self._editcontroller.get_selected_index() 438 if index >= len(playlist) - 1: 439 return 440 tmp = playlist[index] 441 playlist[index] = playlist[index + 1] 442 playlist[index + 1] = tmp 443 index += 1 444 self._editcontroller.set_playlist(playlist) 445 self._editcontroller.set_selected_index(index) 446 self._refresh() 447 448 def _move_up(self) -> None: 449 playlist = self._editcontroller.get_playlist() 450 index = self._editcontroller.get_selected_index() 451 if index < 1: 452 return 453 tmp = playlist[index] 454 playlist[index] = playlist[index - 1] 455 playlist[index - 1] = tmp 456 index -= 1 457 self._editcontroller.set_playlist(playlist) 458 self._editcontroller.set_selected_index(index) 459 self._refresh() 460 461 def _remove(self) -> None: 462 playlist = self._editcontroller.get_playlist() 463 index = self._editcontroller.get_selected_index() 464 if not playlist: 465 return 466 del playlist[index] 467 if index >= len(playlist): 468 index = len(playlist) - 1 469 self._editcontroller.set_playlist(playlist) 470 self._editcontroller.set_selected_index(index) 471 bui.getsound('shieldDown').play() 472 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 400 40 if uiscale is bui.UIScale.SMALL 41 else 470 if uiscale is bui.UIScale.MEDIUM else 540 42 ) 43 44 top_extra = 20 if uiscale is bui.UIScale.SMALL else 0 45 super().__init__( 46 root_widget=bui.containerwidget( 47 size=(self._width, self._height + top_extra), 48 scale=( 49 1.8 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, -16) 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), 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), 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), 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 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 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 227 scroll_height = self._height - 155 228 scrollwidget = bui.scrollwidget( 229 parent=self._root_widget, 230 position=(160 + x_inset, v - scroll_height), 231 highlight=False, 232 on_select_call=bui.Call(self._set_ui_selection, 'gameList'), 233 size=(self._scroll_width, (scroll_height - 15)), 234 ) 235 bui.widget( 236 edit=scrollwidget, 237 left_widget=add_game_button, 238 right_widget=scrollwidget, 239 ) 240 self._columnwidget = bui.columnwidget( 241 parent=scrollwidget, border=2, margin=0 242 ) 243 bui.widget(edit=self._columnwidget, up_widget=self._text_field) 244 245 for button in [add_game_button, edit_game_button, remove_game_button]: 246 bui.widget( 247 edit=button, left_widget=button, right_widget=scrollwidget 248 ) 249 250 self._refresh() 251 252 bui.buttonwidget(edit=cancel_button, on_activate_call=self._cancel) 253 bui.containerwidget( 254 edit=self._root_widget, 255 cancel_button=cancel_button, 256 selected_child=scrollwidget, 257 ) 258 259 bui.buttonwidget(edit=save_button, on_activate_call=self._save_press) 260 bui.containerwidget(edit=self._root_widget, start_button=save_button) 261 262 if prev_selection == 'add_button': 263 bui.containerwidget( 264 edit=self._root_widget, selected_child=add_game_button 265 ) 266 elif prev_selection == 'editButton': 267 bui.containerwidget( 268 edit=self._root_widget, selected_child=edit_game_button 269 ) 270 elif prev_selection == 'gameList': 271 bui.containerwidget( 272 edit=self._root_widget, selected_child=scrollwidget 273 )
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.
275 @override 276 def get_main_window_state(self) -> bui.MainWindowState: 277 # Support recreating our window for back/refresh purposes. 278 cls = type(self) 279 280 editcontroller = self._editcontroller 281 282 return bui.BasicMainWindowState( 283 create_call=lambda transition, origin_widget: cls( 284 transition=transition, 285 origin_widget=origin_widget, 286 editcontroller=editcontroller, 287 ) 288 )
Return a WindowState to recreate this window, if supported.
Inherited Members
- bauiv1._uitypes.MainWindow
- main_window_back_state
- main_window_is_top_level
- main_window_close
- main_window_has_control
- main_window_back
- main_window_replace
- on_main_window_close
- bauiv1._uitypes.Window
- get_root_widget