# Released under the MIT License. See LICENSE for details.
#
"""Provides UI for selecting soundtrack entry types."""
from __future__ import annotations
import copy
from typing import TYPE_CHECKING, override
import bauiv1 as bui
if TYPE_CHECKING:
from typing import Any, Callable
[docs]
class SoundtrackEntryTypeSelectWindow(bui.MainWindow):
"""Window for selecting a soundtrack entry type."""
def __init__(
self,
callback: Callable[[Any], Any],
current_entry: Any,
selection_target_name: str,
*,
transition: str | None = 'in_right',
origin_widget: bui.Widget | None = None,
):
# pylint: disable=too-many-locals
assert bui.app.classic is not None
music = bui.app.classic.music
self._r = 'editSoundtrackWindow'
self._selection_target_name = selection_target_name
self._callback = callback
self._current_entry = copy.deepcopy(current_entry)
self._width = 580
self._height = 220
spacing = 80
# FIXME: Generalize this so new custom soundtrack types can add
# themselves here.
do_default = True
do_mac_music_app_playlist = music.supports_soundtrack_entry_type(
'iTunesPlaylist'
)
do_music_file = music.supports_soundtrack_entry_type('musicFile')
do_music_folder = music.supports_soundtrack_entry_type('musicFolder')
if do_mac_music_app_playlist:
self._height += spacing
if do_music_file:
self._height += spacing
if do_music_folder:
self._height += spacing
uiscale = bui.app.ui_v1.uiscale
# NOTE: When something is selected, we close our UI and kick off
# another window which then calls us back when its done, so the
# standard UI-cleanup-check complains that something is holding
# on to our instance after its ui is gone. Should restructure in
# a cleaner way, but just disabling that check for now.
super().__init__(
root_widget=bui.containerwidget(
size=(self._width, self._height),
scale=(
1.7
if uiscale is bui.UIScale.SMALL
else 1.4 if uiscale is bui.UIScale.MEDIUM else 1.0
),
),
cleanupcheck=False,
transition=transition,
origin_widget=origin_widget,
)
btn = bui.buttonwidget(
parent=self._root_widget,
position=(35, self._height - 65),
size=(160, 60),
scale=0.8,
text_scale=1.2,
label=bui.Lstr(resource='cancelText'),
on_activate_call=self._on_cancel_press,
)
bui.containerwidget(edit=self._root_widget, cancel_button=btn)
bui.textwidget(
parent=self._root_widget,
position=(self._width * 0.5, self._height - 32),
size=(0, 0),
text=bui.Lstr(resource=f'{self._r}.selectASourceText'),
color=bui.app.ui_v1.title_color,
maxwidth=230,
h_align='center',
v_align='center',
)
bui.textwidget(
parent=self._root_widget,
position=(self._width * 0.5, self._height - 56),
size=(0, 0),
text=selection_target_name,
color=bui.app.ui_v1.infotextcolor,
scale=0.7,
maxwidth=230,
h_align='center',
v_align='center',
)
v = self._height - 155
current_entry_type = music.get_soundtrack_entry_type(current_entry)
if do_default:
btn = bui.buttonwidget(
parent=self._root_widget,
size=(self._width - 100, 60),
position=(50, v),
label=bui.Lstr(resource=f'{self._r}.useDefaultGameMusicText'),
on_activate_call=self._on_default_press,
)
if current_entry_type == 'default':
bui.containerwidget(edit=self._root_widget, selected_child=btn)
v -= spacing
if do_mac_music_app_playlist:
btn = bui.buttonwidget(
parent=self._root_widget,
size=(self._width - 100, 60),
position=(50, v),
label=bui.Lstr(resource=f'{self._r}.useITunesPlaylistText'),
on_activate_call=self._on_mac_music_app_playlist_press,
icon=None,
)
if current_entry_type == 'iTunesPlaylist':
bui.containerwidget(edit=self._root_widget, selected_child=btn)
v -= spacing
if do_music_file:
btn = bui.buttonwidget(
parent=self._root_widget,
size=(self._width - 100, 60),
position=(50, v),
label=bui.Lstr(resource=f'{self._r}.useMusicFileText'),
on_activate_call=self._on_music_file_press,
icon=bui.gettexture('file'),
)
if current_entry_type == 'musicFile':
bui.containerwidget(edit=self._root_widget, selected_child=btn)
v -= spacing
if do_music_folder:
btn = bui.buttonwidget(
parent=self._root_widget,
size=(self._width - 100, 60),
position=(50, v),
label=bui.Lstr(resource=f'{self._r}.useMusicFolderText'),
on_activate_call=self._on_music_folder_press,
icon=bui.gettexture('folder'),
icon_color=(1.1, 0.8, 0.2),
)
if current_entry_type == 'musicFolder':
bui.containerwidget(edit=self._root_widget, selected_child=btn)
v -= spacing
[docs]
@override
def get_main_window_state(self) -> bui.MainWindowState:
# Support recreating our window for back/refresh purposes.
cls = type(self)
# Pull these out of self here; if we reference self in the
# lambda we'll keep our window alive which is bad.
current_entry = self._current_entry
callback = self._callback
selection_target_name = self._selection_target_name
return bui.BasicMainWindowState(
create_call=lambda transition, origin_widget: cls(
transition=transition,
origin_widget=origin_widget,
current_entry=current_entry,
callback=callback,
selection_target_name=selection_target_name,
)
)
def _on_mac_music_app_playlist_press(self) -> None:
assert bui.app.classic is not None
music = bui.app.classic.music
from bauiv1lib.soundtrack.macmusicapp import (
MacMusicAppPlaylistSelectWindow,
)
# no-op if we're not in control.
if not self.main_window_has_control():
return
current_playlist_entry: str | None
if (
music.get_soundtrack_entry_type(self._current_entry)
== 'iTunesPlaylist'
):
current_playlist_entry = music.get_soundtrack_entry_name(
self._current_entry
)
else:
current_playlist_entry = None
self.main_window_replace(
MacMusicAppPlaylistSelectWindow(
self._callback, current_playlist_entry, self._current_entry
)
)
def _on_music_file_press(self) -> None:
from babase import android_get_external_files_dir
from baclassic.osmusic import OSMusicPlayer
from bauiv1lib.fileselector import FileSelectorWindow
# no-op if we're not in control.
if not self.main_window_has_control():
return
base_path = android_get_external_files_dir()
assert bui.app.classic is not None
self.main_window_replace(
FileSelectorWindow(
base_path,
callback=self._music_file_selector_cb,
show_base_path=False,
valid_file_extensions=(
OSMusicPlayer.get_valid_music_file_extensions()
),
allow_folders=False,
),
)
def _on_music_folder_press(self) -> None:
from bauiv1lib.fileselector import FileSelectorWindow
from babase import android_get_external_files_dir
# no-op if we're not in control.
if not self.main_window_has_control():
return
base_path = android_get_external_files_dir()
assert bui.app.classic is not None
self.main_window_replace(
FileSelectorWindow(
base_path,
callback=self._music_folder_selector_cb,
show_base_path=False,
valid_file_extensions=[],
allow_folders=True,
),
)
def _music_file_selector_cb(self, result: str | None) -> None:
if result is None:
self._callback(self._current_entry)
else:
self._callback({'type': 'musicFile', 'name': result})
def _music_folder_selector_cb(self, result: str | None) -> None:
if result is None:
self._callback(self._current_entry)
else:
self._callback({'type': 'musicFolder', 'name': result})
def _on_default_press(self) -> None:
self.main_window_back()
self._callback(None)
def _on_cancel_press(self) -> None:
self.main_window_back()
self._callback(self._current_entry)
# 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