Source code for bauiv1lib.playlist.editcontroller

# Released under the MIT License. See LICENSE for details.
#
"""Defines a controller for wrangling playlist edit UIs."""

from __future__ import annotations

import copy
from typing import TYPE_CHECKING

import bascenev1 as bs
import bauiv1 as bui

if TYPE_CHECKING:
    from typing import Any, Callable


[docs] class PlaylistEditController: """Coordinates various UIs involved in playlist editing.""" def __init__( self, sessiontype: type[bs.Session], from_window: bui.MainWindow, *, existing_playlist_name: str | None = None, playlist: list[dict[str, Any]] | None = None, playlist_name: str | None = None, ): from bascenev1 import filter_playlist from bauiv1lib.playlist import PlaylistTypeVars from bauiv1lib.playlist.edit import PlaylistEditWindow appconfig = bui.app.config # Since we may be showing our map list momentarily, # lets go ahead and preload all map preview textures. if bui.app.classic is not None: bui.app.classic.preload_map_preview_media() self._sessiontype = sessiontype self._editing_game = False self._editing_game_type: type[bs.GameActivity] | None = None self._pvars = PlaylistTypeVars(sessiontype) self._existing_playlist_name = existing_playlist_name self._config_name_full = self._pvars.config_name + ' Playlists' self._pre_game_add_state: bui.MainWindowState | None = None self._pre_game_edit_state: bui.MainWindowState | None = None # Make sure config exists. if self._config_name_full not in appconfig: appconfig[self._config_name_full] = {} self._selected_index = 0 if existing_playlist_name: self._name = existing_playlist_name # Filter out invalid games. self._playlist = filter_playlist( appconfig[self._pvars.config_name + ' Playlists'][ existing_playlist_name ], sessiontype=sessiontype, remove_unowned=False, name=existing_playlist_name, ) self._edit_ui_selection = None else: if playlist is not None: self._playlist = playlist else: self._playlist = [] if playlist_name is not None: self._name = playlist_name else: # Find a good unused name. i = 1 while True: self._name = ( self._pvars.default_new_list_name.evaluate() + ((' ' + str(i)) if i > 1 else '') ) if ( self._name not in appconfig[self._pvars.config_name + ' Playlists'] ): break i += 1 # Also we want it to start with 'add' highlighted since its empty # and that's all they can do. self._edit_ui_selection = 'add_button' editwindow = PlaylistEditWindow(editcontroller=self) from_window.main_window_replace(editwindow) # Once we've set our start window, store the back state. We'll # skip back to there once we're fully done. self._back_state = editwindow.main_window_back_state
[docs] def get_config_name(self) -> str: """(internal)""" return self._pvars.config_name
[docs] def get_existing_playlist_name(self) -> str | None: """(internal)""" return self._existing_playlist_name
[docs] def get_edit_ui_selection(self) -> str | None: """(internal)""" return self._edit_ui_selection
[docs] def set_edit_ui_selection(self, selection: str) -> None: """(internal)""" self._edit_ui_selection = selection
[docs] def getname(self) -> str: """(internal)""" return self._name
[docs] def setname(self, name: str) -> None: """(internal)""" self._name = name
[docs] def get_playlist(self) -> list[dict[str, Any]]: """Return the current state of the edited playlist.""" return copy.deepcopy(self._playlist)
[docs] def set_playlist(self, playlist: list[dict[str, Any]]) -> None: """Set the playlist contents.""" self._playlist = copy.deepcopy(playlist)
[docs] def get_session_type(self) -> type[bs.Session]: """Return the bascenev1.Session type for this edit-session.""" return self._sessiontype
[docs] def get_selected_index(self) -> int: """Return the index of the selected playlist.""" return self._selected_index
[docs] def get_default_list_name(self) -> bui.Lstr: """(internal)""" return self._pvars.default_list_name
[docs] def set_selected_index(self, index: int) -> None: """Sets the selected playlist index.""" self._selected_index = index
[docs] def add_game_pressed(self, from_window: bui.MainWindow) -> None: """(internal)""" from bauiv1lib.playlist.addgame import PlaylistAddGameWindow # assert bui.app.classic is not None # No op if we're not in control. if not from_window.main_window_has_control(): return addwindow = PlaylistAddGameWindow(editcontroller=self) from_window.main_window_replace(addwindow) # Once we're there, store the back state. We'll use that to jump # back to our current location once the edit is done. assert self._pre_game_add_state is None self._pre_game_add_state = addwindow.main_window_back_state
[docs] def edit_game_pressed(self, from_window: bui.MainWindow) -> None: """Should be called by supplemental UIs when a game is to be edited.""" if not self._playlist: return self._show_edit_ui( gametype=bui.getclass( self._playlist[self._selected_index]['type'], subclassof=bs.GameActivity, ), settings=self._playlist[self._selected_index], from_window=from_window, )
def _show_edit_ui( self, gametype: type[bs.GameActivity], settings: dict[str, Any] | None, from_window: bui.MainWindow, ) -> None: # pylint: disable=cyclic-import from bauiv1lib.playlist.editgame import PlaylistEditGameWindow if not from_window.main_window_has_control(): return self._editing_game = settings is not None self._editing_game_type = gametype assert self._sessiontype is not None # Jump into an edit window. editwindow = PlaylistEditGameWindow( gametype, self._sessiontype, copy.deepcopy(settings), completion_call=self._edit_game_done, ) from_window.main_window_replace(editwindow) # Once we're there, store the back state. We'll use that to jump # back to our current location once the edit is done. assert self._pre_game_edit_state is None self._pre_game_edit_state = editwindow.main_window_back_state
[docs] def add_game_type_selected( self, gametype: type[bs.GameActivity], from_window: bui.MainWindow ) -> None: """(internal)""" self._show_edit_ui( gametype=gametype, settings=None, from_window=from_window )
def _edit_game_done( self, config: dict[str, Any] | None, from_window: bui.MainWindow ) -> None: # No-op if provided window isn't in charge. if not from_window.main_window_has_control(): return assert bui.app.classic is not None if config is None: bui.getsound('powerdown01').play() else: # Make sure type is in there. assert self._editing_game_type is not None config['type'] = bui.get_type_name(self._editing_game_type) if self._editing_game: self._playlist[self._selected_index] = copy.deepcopy(config) else: # Add a new entry to the playlist. insert_index = min( len(self._playlist), self._selected_index + 1 ) self._playlist.insert(insert_index, copy.deepcopy(config)) self._selected_index = insert_index bui.getsound('gunCocking').play() # If we're adding, jump to before the add started. # Otherwise jump to before the edit started. assert ( self._pre_game_edit_state is not None or self._pre_game_add_state is not None ) if self._pre_game_add_state is not None: from_window.main_window_back_state = self._pre_game_add_state elif self._pre_game_edit_state is not None: from_window.main_window_back_state = self._pre_game_edit_state from_window.main_window_back() self._pre_game_edit_state = None self._pre_game_add_state = None
# Docs-generation hack; import some stuff that we likely only forward-declared # in our actual source code so that docs tools can find it. from typing import (Coroutine, Any, Literal, Callable, Generator, Awaitable, Sequence, Self) import asyncio from concurrent.futures import Future from pathlib import Path from enum import Enum