bauiv1lib.playlist.editcontroller
Defines a controller for wrangling playlist edit UIs.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Defines a controller for wrangling playlist edit UIs.""" 4 5from __future__ import annotations 6 7import copy 8from typing import TYPE_CHECKING 9 10import bascenev1 as bs 11import bauiv1 as bui 12 13if TYPE_CHECKING: 14 from typing import Any, Callable 15 16 17class PlaylistEditController: 18 """Coordinates various UIs involved in playlist editing.""" 19 20 def __init__( 21 self, 22 sessiontype: type[bs.Session], 23 from_window: bui.MainWindow, 24 *, 25 existing_playlist_name: str | None = None, 26 playlist: list[dict[str, Any]] | None = None, 27 playlist_name: str | None = None, 28 ): 29 from bascenev1 import filter_playlist 30 from bauiv1lib.playlist import PlaylistTypeVars 31 from bauiv1lib.playlist.edit import PlaylistEditWindow 32 33 appconfig = bui.app.config 34 35 # Since we may be showing our map list momentarily, 36 # lets go ahead and preload all map preview textures. 37 if bui.app.classic is not None: 38 bui.app.classic.preload_map_preview_media() 39 40 self._sessiontype = sessiontype 41 42 self._editing_game = False 43 self._editing_game_type: type[bs.GameActivity] | None = None 44 self._pvars = PlaylistTypeVars(sessiontype) 45 self._existing_playlist_name = existing_playlist_name 46 self._config_name_full = self._pvars.config_name + ' Playlists' 47 48 self._pre_game_add_state: bui.MainWindowState | None = None 49 self._pre_game_edit_state: bui.MainWindowState | None = None 50 51 # Make sure config exists. 52 if self._config_name_full not in appconfig: 53 appconfig[self._config_name_full] = {} 54 55 self._selected_index = 0 56 if existing_playlist_name: 57 self._name = existing_playlist_name 58 59 # Filter out invalid games. 60 self._playlist = filter_playlist( 61 appconfig[self._pvars.config_name + ' Playlists'][ 62 existing_playlist_name 63 ], 64 sessiontype=sessiontype, 65 remove_unowned=False, 66 name=existing_playlist_name, 67 ) 68 self._edit_ui_selection = None 69 else: 70 if playlist is not None: 71 self._playlist = playlist 72 else: 73 self._playlist = [] 74 if playlist_name is not None: 75 self._name = playlist_name 76 else: 77 # Find a good unused name. 78 i = 1 79 while True: 80 self._name = ( 81 self._pvars.default_new_list_name.evaluate() 82 + ((' ' + str(i)) if i > 1 else '') 83 ) 84 if ( 85 self._name 86 not in appconfig[self._pvars.config_name + ' Playlists'] 87 ): 88 break 89 i += 1 90 91 # Also we want it to start with 'add' highlighted since its empty 92 # and that's all they can do. 93 self._edit_ui_selection = 'add_button' 94 95 editwindow = PlaylistEditWindow(editcontroller=self) 96 from_window.main_window_replace(editwindow) 97 98 # Once we've set our start window, store the back state. We'll 99 # skip back to there once we're fully done. 100 self._back_state = editwindow.main_window_back_state 101 102 def get_config_name(self) -> str: 103 """(internal)""" 104 return self._pvars.config_name 105 106 def get_existing_playlist_name(self) -> str | None: 107 """(internal)""" 108 return self._existing_playlist_name 109 110 def get_edit_ui_selection(self) -> str | None: 111 """(internal)""" 112 return self._edit_ui_selection 113 114 def set_edit_ui_selection(self, selection: str) -> None: 115 """(internal)""" 116 self._edit_ui_selection = selection 117 118 def getname(self) -> str: 119 """(internal)""" 120 return self._name 121 122 def setname(self, name: str) -> None: 123 """(internal)""" 124 self._name = name 125 126 def get_playlist(self) -> list[dict[str, Any]]: 127 """Return the current state of the edited playlist.""" 128 return copy.deepcopy(self._playlist) 129 130 def set_playlist(self, playlist: list[dict[str, Any]]) -> None: 131 """Set the playlist contents.""" 132 self._playlist = copy.deepcopy(playlist) 133 134 def get_session_type(self) -> type[bs.Session]: 135 """Return the bascenev1.Session type for this edit-session.""" 136 return self._sessiontype 137 138 def get_selected_index(self) -> int: 139 """Return the index of the selected playlist.""" 140 return self._selected_index 141 142 def get_default_list_name(self) -> bui.Lstr: 143 """(internal)""" 144 return self._pvars.default_list_name 145 146 def set_selected_index(self, index: int) -> None: 147 """Sets the selected playlist index.""" 148 self._selected_index = index 149 150 def add_game_pressed(self, from_window: bui.MainWindow) -> None: 151 """(internal)""" 152 from bauiv1lib.playlist.addgame import PlaylistAddGameWindow 153 154 # assert bui.app.classic is not None 155 156 # No op if we're not in control. 157 if not from_window.main_window_has_control(): 158 return 159 160 addwindow = PlaylistAddGameWindow(editcontroller=self) 161 from_window.main_window_replace(addwindow) 162 163 # Once we're there, store the back state. We'll use that to jump 164 # back to our current location once the edit is done. 165 assert self._pre_game_add_state is None 166 self._pre_game_add_state = addwindow.main_window_back_state 167 168 def edit_game_pressed(self, from_window: bui.MainWindow) -> None: 169 """Should be called by supplemental UIs when a game is to be edited.""" 170 171 if not self._playlist: 172 return 173 174 self._show_edit_ui( 175 gametype=bui.getclass( 176 self._playlist[self._selected_index]['type'], 177 subclassof=bs.GameActivity, 178 ), 179 settings=self._playlist[self._selected_index], 180 from_window=from_window, 181 ) 182 183 def _show_edit_ui( 184 self, 185 gametype: type[bs.GameActivity], 186 settings: dict[str, Any] | None, 187 from_window: bui.MainWindow, 188 ) -> None: 189 # pylint: disable=cyclic-import 190 from bauiv1lib.playlist.editgame import PlaylistEditGameWindow 191 192 if not from_window.main_window_has_control(): 193 return 194 195 self._editing_game = settings is not None 196 self._editing_game_type = gametype 197 assert self._sessiontype is not None 198 199 # Jump into an edit window. 200 editwindow = PlaylistEditGameWindow( 201 gametype, 202 self._sessiontype, 203 copy.deepcopy(settings), 204 completion_call=self._edit_game_done, 205 ) 206 from_window.main_window_replace(editwindow) 207 208 # Once we're there, store the back state. We'll use that to jump 209 # back to our current location once the edit is done. 210 assert self._pre_game_edit_state is None 211 self._pre_game_edit_state = editwindow.main_window_back_state 212 213 def add_game_type_selected( 214 self, gametype: type[bs.GameActivity], from_window: bui.MainWindow 215 ) -> None: 216 """(internal)""" 217 self._show_edit_ui( 218 gametype=gametype, settings=None, from_window=from_window 219 ) 220 221 def _edit_game_done( 222 self, config: dict[str, Any] | None, from_window: bui.MainWindow 223 ) -> None: 224 225 # No-op if provided window isn't in charge. 226 if not from_window.main_window_has_control(): 227 return 228 229 assert bui.app.classic is not None 230 if config is None: 231 bui.getsound('powerdown01').play() 232 else: 233 # Make sure type is in there. 234 assert self._editing_game_type is not None 235 config['type'] = bui.get_type_name(self._editing_game_type) 236 237 if self._editing_game: 238 self._playlist[self._selected_index] = copy.deepcopy(config) 239 else: 240 # Add a new entry to the playlist. 241 insert_index = min( 242 len(self._playlist), self._selected_index + 1 243 ) 244 self._playlist.insert(insert_index, copy.deepcopy(config)) 245 self._selected_index = insert_index 246 247 bui.getsound('gunCocking').play() 248 249 # If we're adding, jump to before the add started. 250 # Otherwise jump to before the edit started. 251 assert ( 252 self._pre_game_edit_state is not None 253 or self._pre_game_add_state is not None 254 ) 255 if self._pre_game_add_state is not None: 256 from_window.main_window_back_state = self._pre_game_add_state 257 elif self._pre_game_edit_state is not None: 258 from_window.main_window_back_state = self._pre_game_edit_state 259 from_window.main_window_back() 260 self._pre_game_edit_state = None 261 self._pre_game_add_state = None
class
PlaylistEditController:
18class PlaylistEditController: 19 """Coordinates various UIs involved in playlist editing.""" 20 21 def __init__( 22 self, 23 sessiontype: type[bs.Session], 24 from_window: bui.MainWindow, 25 *, 26 existing_playlist_name: str | None = None, 27 playlist: list[dict[str, Any]] | None = None, 28 playlist_name: str | None = None, 29 ): 30 from bascenev1 import filter_playlist 31 from bauiv1lib.playlist import PlaylistTypeVars 32 from bauiv1lib.playlist.edit import PlaylistEditWindow 33 34 appconfig = bui.app.config 35 36 # Since we may be showing our map list momentarily, 37 # lets go ahead and preload all map preview textures. 38 if bui.app.classic is not None: 39 bui.app.classic.preload_map_preview_media() 40 41 self._sessiontype = sessiontype 42 43 self._editing_game = False 44 self._editing_game_type: type[bs.GameActivity] | None = None 45 self._pvars = PlaylistTypeVars(sessiontype) 46 self._existing_playlist_name = existing_playlist_name 47 self._config_name_full = self._pvars.config_name + ' Playlists' 48 49 self._pre_game_add_state: bui.MainWindowState | None = None 50 self._pre_game_edit_state: bui.MainWindowState | None = None 51 52 # Make sure config exists. 53 if self._config_name_full not in appconfig: 54 appconfig[self._config_name_full] = {} 55 56 self._selected_index = 0 57 if existing_playlist_name: 58 self._name = existing_playlist_name 59 60 # Filter out invalid games. 61 self._playlist = filter_playlist( 62 appconfig[self._pvars.config_name + ' Playlists'][ 63 existing_playlist_name 64 ], 65 sessiontype=sessiontype, 66 remove_unowned=False, 67 name=existing_playlist_name, 68 ) 69 self._edit_ui_selection = None 70 else: 71 if playlist is not None: 72 self._playlist = playlist 73 else: 74 self._playlist = [] 75 if playlist_name is not None: 76 self._name = playlist_name 77 else: 78 # Find a good unused name. 79 i = 1 80 while True: 81 self._name = ( 82 self._pvars.default_new_list_name.evaluate() 83 + ((' ' + str(i)) if i > 1 else '') 84 ) 85 if ( 86 self._name 87 not in appconfig[self._pvars.config_name + ' Playlists'] 88 ): 89 break 90 i += 1 91 92 # Also we want it to start with 'add' highlighted since its empty 93 # and that's all they can do. 94 self._edit_ui_selection = 'add_button' 95 96 editwindow = PlaylistEditWindow(editcontroller=self) 97 from_window.main_window_replace(editwindow) 98 99 # Once we've set our start window, store the back state. We'll 100 # skip back to there once we're fully done. 101 self._back_state = editwindow.main_window_back_state 102 103 def get_config_name(self) -> str: 104 """(internal)""" 105 return self._pvars.config_name 106 107 def get_existing_playlist_name(self) -> str | None: 108 """(internal)""" 109 return self._existing_playlist_name 110 111 def get_edit_ui_selection(self) -> str | None: 112 """(internal)""" 113 return self._edit_ui_selection 114 115 def set_edit_ui_selection(self, selection: str) -> None: 116 """(internal)""" 117 self._edit_ui_selection = selection 118 119 def getname(self) -> str: 120 """(internal)""" 121 return self._name 122 123 def setname(self, name: str) -> None: 124 """(internal)""" 125 self._name = name 126 127 def get_playlist(self) -> list[dict[str, Any]]: 128 """Return the current state of the edited playlist.""" 129 return copy.deepcopy(self._playlist) 130 131 def set_playlist(self, playlist: list[dict[str, Any]]) -> None: 132 """Set the playlist contents.""" 133 self._playlist = copy.deepcopy(playlist) 134 135 def get_session_type(self) -> type[bs.Session]: 136 """Return the bascenev1.Session type for this edit-session.""" 137 return self._sessiontype 138 139 def get_selected_index(self) -> int: 140 """Return the index of the selected playlist.""" 141 return self._selected_index 142 143 def get_default_list_name(self) -> bui.Lstr: 144 """(internal)""" 145 return self._pvars.default_list_name 146 147 def set_selected_index(self, index: int) -> None: 148 """Sets the selected playlist index.""" 149 self._selected_index = index 150 151 def add_game_pressed(self, from_window: bui.MainWindow) -> None: 152 """(internal)""" 153 from bauiv1lib.playlist.addgame import PlaylistAddGameWindow 154 155 # assert bui.app.classic is not None 156 157 # No op if we're not in control. 158 if not from_window.main_window_has_control(): 159 return 160 161 addwindow = PlaylistAddGameWindow(editcontroller=self) 162 from_window.main_window_replace(addwindow) 163 164 # Once we're there, store the back state. We'll use that to jump 165 # back to our current location once the edit is done. 166 assert self._pre_game_add_state is None 167 self._pre_game_add_state = addwindow.main_window_back_state 168 169 def edit_game_pressed(self, from_window: bui.MainWindow) -> None: 170 """Should be called by supplemental UIs when a game is to be edited.""" 171 172 if not self._playlist: 173 return 174 175 self._show_edit_ui( 176 gametype=bui.getclass( 177 self._playlist[self._selected_index]['type'], 178 subclassof=bs.GameActivity, 179 ), 180 settings=self._playlist[self._selected_index], 181 from_window=from_window, 182 ) 183 184 def _show_edit_ui( 185 self, 186 gametype: type[bs.GameActivity], 187 settings: dict[str, Any] | None, 188 from_window: bui.MainWindow, 189 ) -> None: 190 # pylint: disable=cyclic-import 191 from bauiv1lib.playlist.editgame import PlaylistEditGameWindow 192 193 if not from_window.main_window_has_control(): 194 return 195 196 self._editing_game = settings is not None 197 self._editing_game_type = gametype 198 assert self._sessiontype is not None 199 200 # Jump into an edit window. 201 editwindow = PlaylistEditGameWindow( 202 gametype, 203 self._sessiontype, 204 copy.deepcopy(settings), 205 completion_call=self._edit_game_done, 206 ) 207 from_window.main_window_replace(editwindow) 208 209 # Once we're there, store the back state. We'll use that to jump 210 # back to our current location once the edit is done. 211 assert self._pre_game_edit_state is None 212 self._pre_game_edit_state = editwindow.main_window_back_state 213 214 def add_game_type_selected( 215 self, gametype: type[bs.GameActivity], from_window: bui.MainWindow 216 ) -> None: 217 """(internal)""" 218 self._show_edit_ui( 219 gametype=gametype, settings=None, from_window=from_window 220 ) 221 222 def _edit_game_done( 223 self, config: dict[str, Any] | None, from_window: bui.MainWindow 224 ) -> None: 225 226 # No-op if provided window isn't in charge. 227 if not from_window.main_window_has_control(): 228 return 229 230 assert bui.app.classic is not None 231 if config is None: 232 bui.getsound('powerdown01').play() 233 else: 234 # Make sure type is in there. 235 assert self._editing_game_type is not None 236 config['type'] = bui.get_type_name(self._editing_game_type) 237 238 if self._editing_game: 239 self._playlist[self._selected_index] = copy.deepcopy(config) 240 else: 241 # Add a new entry to the playlist. 242 insert_index = min( 243 len(self._playlist), self._selected_index + 1 244 ) 245 self._playlist.insert(insert_index, copy.deepcopy(config)) 246 self._selected_index = insert_index 247 248 bui.getsound('gunCocking').play() 249 250 # If we're adding, jump to before the add started. 251 # Otherwise jump to before the edit started. 252 assert ( 253 self._pre_game_edit_state is not None 254 or self._pre_game_add_state is not None 255 ) 256 if self._pre_game_add_state is not None: 257 from_window.main_window_back_state = self._pre_game_add_state 258 elif self._pre_game_edit_state is not None: 259 from_window.main_window_back_state = self._pre_game_edit_state 260 from_window.main_window_back() 261 self._pre_game_edit_state = None 262 self._pre_game_add_state = None
Coordinates various UIs involved in playlist editing.
PlaylistEditController( sessiontype: type[bascenev1.Session], from_window: bauiv1.MainWindow, *, existing_playlist_name: str | None = None, playlist: list[dict[str, typing.Any]] | None = None, playlist_name: str | None = None)
21 def __init__( 22 self, 23 sessiontype: type[bs.Session], 24 from_window: bui.MainWindow, 25 *, 26 existing_playlist_name: str | None = None, 27 playlist: list[dict[str, Any]] | None = None, 28 playlist_name: str | None = None, 29 ): 30 from bascenev1 import filter_playlist 31 from bauiv1lib.playlist import PlaylistTypeVars 32 from bauiv1lib.playlist.edit import PlaylistEditWindow 33 34 appconfig = bui.app.config 35 36 # Since we may be showing our map list momentarily, 37 # lets go ahead and preload all map preview textures. 38 if bui.app.classic is not None: 39 bui.app.classic.preload_map_preview_media() 40 41 self._sessiontype = sessiontype 42 43 self._editing_game = False 44 self._editing_game_type: type[bs.GameActivity] | None = None 45 self._pvars = PlaylistTypeVars(sessiontype) 46 self._existing_playlist_name = existing_playlist_name 47 self._config_name_full = self._pvars.config_name + ' Playlists' 48 49 self._pre_game_add_state: bui.MainWindowState | None = None 50 self._pre_game_edit_state: bui.MainWindowState | None = None 51 52 # Make sure config exists. 53 if self._config_name_full not in appconfig: 54 appconfig[self._config_name_full] = {} 55 56 self._selected_index = 0 57 if existing_playlist_name: 58 self._name = existing_playlist_name 59 60 # Filter out invalid games. 61 self._playlist = filter_playlist( 62 appconfig[self._pvars.config_name + ' Playlists'][ 63 existing_playlist_name 64 ], 65 sessiontype=sessiontype, 66 remove_unowned=False, 67 name=existing_playlist_name, 68 ) 69 self._edit_ui_selection = None 70 else: 71 if playlist is not None: 72 self._playlist = playlist 73 else: 74 self._playlist = [] 75 if playlist_name is not None: 76 self._name = playlist_name 77 else: 78 # Find a good unused name. 79 i = 1 80 while True: 81 self._name = ( 82 self._pvars.default_new_list_name.evaluate() 83 + ((' ' + str(i)) if i > 1 else '') 84 ) 85 if ( 86 self._name 87 not in appconfig[self._pvars.config_name + ' Playlists'] 88 ): 89 break 90 i += 1 91 92 # Also we want it to start with 'add' highlighted since its empty 93 # and that's all they can do. 94 self._edit_ui_selection = 'add_button' 95 96 editwindow = PlaylistEditWindow(editcontroller=self) 97 from_window.main_window_replace(editwindow) 98 99 # Once we've set our start window, store the back state. We'll 100 # skip back to there once we're fully done. 101 self._back_state = editwindow.main_window_back_state
def
get_playlist(self) -> list[dict[str, typing.Any]]:
127 def get_playlist(self) -> list[dict[str, Any]]: 128 """Return the current state of the edited playlist.""" 129 return copy.deepcopy(self._playlist)
Return the current state of the edited playlist.
def
set_playlist(self, playlist: list[dict[str, typing.Any]]) -> None:
131 def set_playlist(self, playlist: list[dict[str, Any]]) -> None: 132 """Set the playlist contents.""" 133 self._playlist = copy.deepcopy(playlist)
Set the playlist contents.
135 def get_session_type(self) -> type[bs.Session]: 136 """Return the bascenev1.Session type for this edit-session.""" 137 return self._sessiontype
Return the bascenev1.Session type for this edit-session.
def
get_selected_index(self) -> int:
139 def get_selected_index(self) -> int: 140 """Return the index of the selected playlist.""" 141 return self._selected_index
Return the index of the selected playlist.
def
set_selected_index(self, index: int) -> None:
147 def set_selected_index(self, index: int) -> None: 148 """Sets the selected playlist index.""" 149 self._selected_index = index
Sets the selected playlist index.
169 def edit_game_pressed(self, from_window: bui.MainWindow) -> None: 170 """Should be called by supplemental UIs when a game is to be edited.""" 171 172 if not self._playlist: 173 return 174 175 self._show_edit_ui( 176 gametype=bui.getclass( 177 self._playlist[self._selected_index]['type'], 178 subclassof=bs.GameActivity, 179 ), 180 settings=self._playlist[self._selected_index], 181 from_window=from_window, 182 )
Should be called by supplemental UIs when a game is to be edited.