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

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        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
def get_playlist(self) -> list[dict[str, typing.Any]]:
126    def get_playlist(self) -> list[dict[str, Any]]:
127        """Return the current state of the edited playlist."""
128        return copy.deepcopy(self._playlist)

Return the current state of the edited playlist.

def set_playlist(self, playlist: list[dict[str, typing.Any]]) -> None:
130    def set_playlist(self, playlist: list[dict[str, Any]]) -> None:
131        """Set the playlist contents."""
132        self._playlist = copy.deepcopy(playlist)

Set the playlist contents.

def get_session_type(self) -> type[bascenev1.Session]:
134    def get_session_type(self) -> type[bs.Session]:
135        """Return the bascenev1.Session type for this edit-session."""
136        return self._sessiontype

Return the bascenev1.Session type for this edit-session.

def get_selected_index(self) -> int:
138    def get_selected_index(self) -> int:
139        """Return the index of the selected playlist."""
140        return self._selected_index

Return the index of the selected playlist.

def set_selected_index(self, index: int) -> None:
146    def set_selected_index(self, index: int) -> None:
147        """Sets the selected playlist index."""
148        self._selected_index = index

Sets the selected playlist index.

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

Should be called by supplemental UIs when a game is to be edited.