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.

def get_session_type(self) -> type[bascenev1.Session]:
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.

def edit_game_pressed(self, from_window: bauiv1.MainWindow) -> None:
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.