401 lines
12 KiB
C#
401 lines
12 KiB
C#
using System.Linq;
|
||
using Content.Client.UserInterface.Controls;
|
||
using Content.Shared._Horizon.MediaPlayer;
|
||
using Robust.Client.Audio;
|
||
using Robust.Client.AutoGenerated;
|
||
using Robust.Client.UserInterface.Controls;
|
||
using Robust.Client.UserInterface.XAML;
|
||
using Robust.Shared.Audio.Components;
|
||
using Robust.Shared.Timing;
|
||
|
||
namespace Content.Client._Horizon.MediaPlayer;
|
||
|
||
[GenerateTypedNameReferences]
|
||
public sealed partial class MediaPlayerMenu : FancyWindow
|
||
{
|
||
[Dependency] private readonly IEntityManager _entManager = null!;
|
||
private readonly AudioSystem _audioSystem;
|
||
// events
|
||
public event Action<MediaSelectedMessage>? OnFileSelected;
|
||
public event Action<MediaRepeatMessage>? OnRepeatPressed;
|
||
public event Action<MediaVolumeMessage>? OnVolumePressed;
|
||
public event Action<MediaStopMessage>? OnStopPressed;
|
||
public event Action<bool>? OnPlayPressed;
|
||
public event Action<float>? SetTime;
|
||
|
||
// counters and stuff
|
||
public EntityUid? Audio;
|
||
private sbyte _repeatCounter;
|
||
private sbyte _volumeCounter;
|
||
private float _volume = -2;
|
||
private MediaFile? _currentMediaFile;
|
||
private readonly string _waitText = Loc.GetString("media-wait-message");
|
||
|
||
|
||
public MediaPlayerMenu()
|
||
{
|
||
RobustXamlLoader.Load(this);
|
||
IoCManager.InjectDependencies(this);
|
||
_audioSystem = _entManager.System<AudioSystem>();
|
||
|
||
DisableButtons();
|
||
CurrentPlaying.Text = _waitText;
|
||
RepeatButton.OnPressed += _ => Repeat();
|
||
VolumeButton.OnPressed += _ => Volume();
|
||
StopButton.OnPressed += _ => Switch(_currentMediaFile);
|
||
PlaybackSlider.OnReleased += _ => SetTime?.Invoke(PlaybackSlider.Value);
|
||
PlayButton.OnPressed += _ =>
|
||
{
|
||
PlayPause(PlayButton.Pressed);
|
||
OnPlayPressed?.Invoke(PlayButton.Pressed);
|
||
};
|
||
Search.OnTextChanged += _ =>
|
||
{
|
||
if (MediaList.ChildCount == 0)
|
||
return;
|
||
|
||
foreach (var control in MediaList.Children.ToArray())
|
||
{
|
||
if (control is not MediaFile mediaFile)
|
||
continue;
|
||
|
||
var songName = $"{mediaFile.MusicAuthor.Text} {mediaFile.MusicName.Text}";
|
||
mediaFile.Visible = songName.Contains(Search.Text, StringComparison.CurrentCultureIgnoreCase);
|
||
}
|
||
};
|
||
|
||
// UpDown
|
||
StopButton.OnButtonDown += OnDown;
|
||
StopButton.OnButtonUp += OnUp;
|
||
|
||
PlayButton.OnButtonDown += OnDown;
|
||
PlayButton.OnButtonUp += OnUp;
|
||
|
||
VolumeButton.OnButtonDown += OnDown;
|
||
VolumeButton.OnButtonUp += OnUp;
|
||
}
|
||
|
||
#region Buttons events
|
||
private static void OnDown(BaseButton.ButtonEventArgs args)
|
||
{
|
||
args.Button.Modulate = Color.Gray;
|
||
}
|
||
private static void OnUp(BaseButton.ButtonEventArgs args)
|
||
{
|
||
args.Button.Modulate = Color.White;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Called when the song field is selected.
|
||
/// </summary>
|
||
/// <param name="args">A field that stores data about a song</param>
|
||
private void OnSelect(MediaFile args)
|
||
{
|
||
// Если выбраная панель уже была выбрана - останавливаем музыку.
|
||
if (_currentMediaFile == args)
|
||
{
|
||
Switch(_currentMediaFile);
|
||
return;
|
||
}
|
||
// Если файл не совпадает с предидущим - останавливаем музыку
|
||
if (_currentMediaFile != null)
|
||
{
|
||
Switch(_currentMediaFile, args);
|
||
return;
|
||
}
|
||
|
||
EnableButtons();
|
||
SetTitle(newMedia: args);
|
||
PlayPause(true);
|
||
_currentMediaFile = args;
|
||
OnFileSelected?.Invoke(new MediaSelectedMessage(_volume, args.SongId, args.SongPath));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Switch that turns music on/off
|
||
/// customizing the UI depending on the switch state
|
||
/// </summary>
|
||
/// <param name="oldMedia">Media that should be turned off and replaced with new one.</param>
|
||
/// <param name="newMedia">Media that should be turned on and shown in the UI with music</param>
|
||
private void Switch(MediaFile? oldMedia = null, MediaFile? newMedia = null)
|
||
{
|
||
if (oldMedia is null)
|
||
return;
|
||
|
||
SetTitle(oldMedia, newMedia);
|
||
|
||
if (newMedia is not null)
|
||
{
|
||
PlayPause(true);
|
||
EnableButtons();
|
||
_currentMediaFile = newMedia;
|
||
OnFileSelected?.Invoke(new MediaSelectedMessage(_volume, newMedia.SongId, newMedia.SongPath));
|
||
}
|
||
else
|
||
{
|
||
PlayPause(false);
|
||
DisableButtons();
|
||
_currentMediaFile = null;
|
||
OnStopPressed?.Invoke(new MediaStopMessage(false));
|
||
}
|
||
}
|
||
|
||
private void PlayPause(bool state)
|
||
{
|
||
if (state)
|
||
{
|
||
PlayButton.TexturePath = "/Textures/_Horizon/Interface/pause.png";
|
||
PlayButton.Pressed = state;
|
||
return;
|
||
}
|
||
|
||
PlayButton.TexturePath = "/Textures/_Horizon/Interface/play.png";
|
||
PlayButton.Pressed = state;
|
||
}
|
||
|
||
private void Volume()
|
||
{
|
||
switch (_volumeCounter)
|
||
{
|
||
case 0:
|
||
VolumeButton.TexturePath = "/Textures/_Horizon/Interface/volume_2.png";
|
||
_volume = -5;
|
||
break;
|
||
|
||
case 1:
|
||
VolumeButton.TexturePath = "/Textures/_Horizon/Interface/volume_3.png";
|
||
_volume = 0;
|
||
break;
|
||
|
||
case 2:
|
||
VolumeButton.TexturePath = "/Textures/_Horizon/Interface/volume_1.png";
|
||
_volume = -10;
|
||
_volumeCounter = -1;
|
||
break;
|
||
}
|
||
OnVolumePressed?.Invoke(new MediaVolumeMessage(_volume));
|
||
_volumeCounter++;
|
||
}
|
||
|
||
private void Repeat()
|
||
{
|
||
var type = RepeatType.None;
|
||
switch (_repeatCounter)
|
||
{
|
||
case 0:
|
||
RepeatButton.Modulate = Color.Gray;
|
||
type = RepeatType.Playlist;
|
||
break;
|
||
|
||
case 1:
|
||
RepeatButton.TexturePath = "/Textures/_Horizon/Interface/repeat_one.png";
|
||
type = RepeatType.Single;
|
||
break;
|
||
|
||
case 2:
|
||
RepeatButton.Modulate = Color.White;
|
||
RepeatButton.TexturePath = "/Textures/_Horizon/Interface/repeat.png";
|
||
type = RepeatType.None;
|
||
_repeatCounter = -1;
|
||
break;
|
||
}
|
||
OnRepeatPressed?.Invoke(new MediaRepeatMessage(type));
|
||
_repeatCounter++;
|
||
}
|
||
|
||
#endregion Buttons events
|
||
|
||
#region Events
|
||
protected override void FrameUpdate(FrameEventArgs args)
|
||
{
|
||
base.FrameUpdate(args);
|
||
|
||
if (Audio is null || PlaybackSlider.Disabled)
|
||
return;
|
||
|
||
DurationLabel.Text = _entManager.TryGetComponent(Audio, out AudioComponent? audioComp)
|
||
? $@"{TimeSpan.FromSeconds(audioComp.PlaybackPosition):mm\:ss} / {TimeSpan.FromSeconds(PlaybackSlider.MaxValue):mm\:ss}"
|
||
: $"00:00 / 00:00";
|
||
|
||
if (PlaybackSlider.Grabbed)
|
||
return;
|
||
|
||
if (audioComp != null || _entManager.TryGetComponent(Audio, out audioComp))
|
||
{
|
||
PlaybackSlider.SetValueWithoutEvent(audioComp.PlaybackPosition);
|
||
}
|
||
else if (PlayButton.Pressed)
|
||
{
|
||
PlaybackSlider.SetValueWithoutEvent(0f);
|
||
if (_repeatCounter == 0)
|
||
Switch(_currentMediaFile);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Adds new tracks to the list
|
||
/// </summary>
|
||
/// <param name="files">Files we wish to add</param>
|
||
public void PopulateMediaList(List<MediaFilePrototype> files)
|
||
{
|
||
MediaList.RemoveAllChildren();
|
||
foreach (var file in files)
|
||
{
|
||
var mediaFile = new MediaFile()
|
||
{
|
||
SongId = file.ID,
|
||
MusicAuthor = { Text = $"{file.Author}" },
|
||
MusicName = { Text = $"{file.SongName}" },
|
||
MusicTimer = { Text = $@"{_audioSystem.GetAudioLength(_audioSystem.ResolveSound(file.SoundPath)):mm\:ss}" },
|
||
FullSongName = $"{Loc.GetString("media-now-playing-message")}:\n{file.Author} - {file.SongName}",
|
||
SongPath = file.SoundPath,
|
||
};
|
||
mediaFile.OnPanelClicked += OnSelect;
|
||
MediaList.AddChild(mediaFile);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Set selected song, and length for slider
|
||
/// </summary>
|
||
/// <param name="length">Song length in seconds</param>
|
||
public void SetSliderLength(float length)
|
||
{
|
||
PlaybackSlider.MaxValue = length;
|
||
PlaybackSlider.SetValueWithoutEvent(0);
|
||
}
|
||
|
||
public void UpdateState(string id, RepeatType type, float volume)
|
||
{
|
||
// Setup old state of UI
|
||
SetupRepeatButton(type);
|
||
SetupVolumeButton(volume);
|
||
|
||
foreach (var control in MediaList.Children.ToArray())
|
||
{
|
||
if (control is not MediaFile mediaFile)
|
||
continue;
|
||
|
||
if (mediaFile.SongId != id)
|
||
continue;
|
||
|
||
SetTitle(newMedia: mediaFile);
|
||
mediaFile.MusicTitle.Modulate = Color.Green;
|
||
mediaFile.TitlePressed = true;
|
||
_currentMediaFile = mediaFile;
|
||
|
||
if (!_entManager.TryGetComponent(Audio, out AudioComponent? comp))
|
||
return;
|
||
|
||
EnableButtons();
|
||
PlayPause(comp.Playing);
|
||
|
||
}
|
||
}
|
||
|
||
private void EnableButtons()
|
||
{
|
||
// Enable
|
||
PlayButton.Disabled = false;
|
||
StopButton.Disabled = false;
|
||
PlaybackSlider.Disabled = false;
|
||
}
|
||
|
||
private void DisableButtons()
|
||
{
|
||
// Disable
|
||
PlayButton.Disabled = true;
|
||
StopButton.Disabled = true;
|
||
PlaybackSlider.Disabled = true;
|
||
}
|
||
|
||
private void SetupRepeatButton(RepeatType type)
|
||
{
|
||
switch (type)
|
||
{
|
||
case RepeatType.Playlist:
|
||
RepeatButton.Modulate = Color.Gray;
|
||
_repeatCounter = 1;
|
||
return;
|
||
|
||
case RepeatType.Single:
|
||
RepeatButton.Modulate = Color.Gray;
|
||
RepeatButton.TexturePath = "/Textures/_Horizon/Interface/repeat_one.png";
|
||
_repeatCounter = 2;
|
||
return;
|
||
|
||
case RepeatType.None:
|
||
RepeatButton.TexturePath = "/Textures/_Horizon/Interface/repeat.png";
|
||
_repeatCounter = 0;
|
||
return;
|
||
}
|
||
}
|
||
|
||
private void SetupVolumeButton(float volume)
|
||
{
|
||
switch (volume)
|
||
{
|
||
case -5:
|
||
VolumeButton.TexturePath = "/Textures/_Horizon/Interface/volume_2.png";
|
||
_volume = volume;
|
||
_volumeCounter = 1;
|
||
return;
|
||
|
||
case 0:
|
||
VolumeButton.TexturePath = "/Textures/_Horizon/Interface/volume_3.png";
|
||
_volume = volume;
|
||
_volumeCounter = 2;
|
||
return;
|
||
|
||
case -10:
|
||
VolumeButton.TexturePath = "/Textures/_Horizon/Interface/volume_1.png";
|
||
_volume = volume;
|
||
_volumeCounter = 0;
|
||
return;
|
||
}
|
||
}
|
||
|
||
public void ChangeTitle(string id)
|
||
{
|
||
foreach (var control in MediaList.Children.ToArray())
|
||
{
|
||
if (control is not MediaFile mediaFile || id != mediaFile.SongId)
|
||
continue;
|
||
|
||
EnableButtons();
|
||
PlayPause(true);
|
||
if (_currentMediaFile == mediaFile)
|
||
return;
|
||
|
||
mediaFile.TitlePressed = true;
|
||
mediaFile.MusicTitle.Modulate = Color.Green;
|
||
SetTitle(_currentMediaFile, mediaFile);
|
||
_currentMediaFile = mediaFile;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Sets labels based on input data
|
||
/// </summary>
|
||
/// <param name="oldMedia">Media that should be disabled and removed from display in the UI</param>
|
||
/// <param name="newMedia">Media that should be enabled and add to display in the UI</param>
|
||
private void SetTitle(MediaFile? oldMedia = null, MediaFile? newMedia = null)
|
||
{
|
||
if (oldMedia is not null && oldMedia.TitlePressed)
|
||
{
|
||
oldMedia.TitlePressed = !oldMedia.TitlePressed;
|
||
oldMedia.MusicTitle.Modulate = Color.White;
|
||
}
|
||
|
||
if (newMedia is not null)
|
||
{
|
||
CurrentPlaying.Text = newMedia.FullSongName;
|
||
return;
|
||
}
|
||
|
||
CurrentPlaying.Text = _waitText;
|
||
DurationLabel.Text = "00:00 / 00:00";
|
||
}
|
||
#endregion
|
||
}
|