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? OnFileSelected; public event Action? OnRepeatPressed; public event Action? OnVolumePressed; public event Action? OnStopPressed; public event Action? OnPlayPressed; public event Action? 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(); 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; } /// /// Called when the song field is selected. /// /// A field that stores data about a song 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)); } /// /// Switch that turns music on/off /// customizing the UI depending on the switch state /// /// Media that should be turned off and replaced with new one. /// Media that should be turned on and shown in the UI with music 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); } } /// /// Adds new tracks to the list /// /// Files we wish to add public void PopulateMediaList(List 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); } } /// /// Set selected song, and length for slider /// /// Song length in seconds 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; } } /// /// Sets labels based on input data /// /// Media that should be disabled and removed from display in the UI /// Media that should be enabled and add to display in the UI 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 }