#region license // This file is part of Vocaluxe. // // Vocaluxe is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Vocaluxe is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Vocaluxe. If not, see . #endregion using System; using System.Diagnostics; using System.Xml.Serialization; using VocaluxeLib.Draw; using VocaluxeLib.PartyModes; namespace VocaluxeLib.Menu.SongMenu { [XmlType("SongMenu")] public struct SThemeSongMenu { [XmlAttribute(AttributeName = "Name")] public string Name; public string CoverBackground; public string CoverBigBackground; public string DuetIcon; public string RapIcon; public string VideoIcon; public string MedleyCalcIcon; public string MedleyTagIcon; public string InstrumentalIcon; public string VocalsIcon; public SThemeColor Color; //public SThemeSongMenuBook songMenuBook; //public SThemeSongMenuDreidel songMenuDreidel; public SThemeSongMenuList SongMenuList; public SThemeSongMenuTileBoard SongMenuTileBoard; } public struct SThemeSongMenuList { /// /// Number of visible songs in list /// public int ListLength; /// /// Space between tiles horizontal /// public float SpaceW; /// /// Space between tiles vertical /// public float SpaceH; public SRectF TileRect; public SRectF TileRectSmall; public SThemeText TextArtist; public SThemeText TextTitle; public SThemeText TextSongLength; public SThemeText TextSongYear; public SThemeStatic StaticCoverBig; public SThemeStatic StaticTextBG; public SThemeStatic StaticDuetIcon; public SThemeStatic StaticRapIcon; public SThemeStatic StaticInstrumentalIcon; public SThemeStatic StaticVocalsIcon; public SThemeStatic StaticVideoIcon; public SThemeStatic StaticMedleyCalcIcon; public SThemeStatic StaticMedleyTagIcon; public SThemeStatic? StaticScrollBar; public SThemeStatic? StaticScrollBarThumb; } public struct SThemeSongMenuTileBoard { /// /// Number of tiles horizontal /// public int NumW; /// /// Number of tiles vertical /// public int NumH; /// /// Number of tiles horizontal in small-modus /// public int NumWsmall; /// /// Number of tiles vertical in small-modus /// public int NumHsmall; /// /// Space between tiles horizontal /// public float SpaceW; /// /// Space between tiles vertical /// public float SpaceH; public SRectF TileRect; public SRectF TileRectSmall; public SThemeText TextArtist; public SThemeText TextTitle; public SThemeText TextSongLength; public SThemeText TextSongYear; public SThemeStatic StaticCoverBig; public SThemeStatic StaticTextBG; public SThemeStatic StaticDuetIcon; public SThemeStatic StaticRapIcon; public SThemeStatic StaticInstrumentalIcon; public SThemeStatic StaticVocalsIcon; public SThemeStatic StaticVideoIcon; public SThemeStatic StaticMedleyCalcIcon; public SThemeStatic StaticMedleyTagIcon; public SThemeStatic? StaticScrollBar; public SThemeStatic? StaticScrollBarThumb; public SThemeStatic? StaticTileCaptionBG; public SThemeText? TextTileArtist; public SThemeText? TextTileTitle; } public abstract class CSongMenuFramework : CMenuElementBase, ISongMenu { protected readonly int _PartyModeId; protected SThemeSongMenu _Theme; public bool ThemeLoaded { get; private set; } public bool Selectable { get { return Visible; } } protected bool _Initialized; private int _PreviewNrInternal = -1; protected virtual int _PreviewNr { get { return _PreviewNrInternal; } set { if (_PreviewNrInternal == value) { return; } if (CBase.Songs.IsInCategory()) { if (value >= CBase.Songs.GetNumSongsVisible()) { value = -1; } _PlaySong(value); } else if (value >= CBase.Songs.GetNumCategories()) { value = -1; CBase.BackgroundMusic.SetPlayingPreview(false); } _PreviewNrInternal = value; } } protected int _AutoplayDelayinMs = CBase.Config.GetAutoplayPreviewDelay(); protected System.Timers.Timer _AutoplayTimer = new System.Timers.Timer(); protected volatile bool _AutoplayPending = false; private const float _ScrollDuration = 150f; private readonly Stopwatch _ScrollTimer = new Stopwatch(); private float _ScrollStart; private int _ScrollTarget; private SColorF _ColorInternal; protected SColorF _Color { get { return _ColorInternal; } } public int GetPreviewSong() { return _PreviewNr; } public virtual bool SmallView { get; set; } public bool PreviewVisible { get; set; } = true; public abstract float SelectedTileZoomFactor { get; } // This is the nr of the current selection (song or category) protected virtual int _SelectionNr { get; set; } protected CSongMenuFramework(int partyModeId) { Visible = true; _PartyModeId = partyModeId; } protected CSongMenuFramework(SThemeSongMenu theme, int partyModeId) { Visible = true; _PartyModeId = partyModeId; _Theme = theme; ThemeLoaded = true; } #region Theme public string GetThemeName() { return _Theme.Name; } public virtual object GetTheme() { return _Theme; } #endregion Theme public virtual void Init() { _ResetPreview(false); _InitializeAutoplayTimer(); _Initialized = true; } public virtual void Update(SScreenSongOptions songOptions) { if (_AutoplayPending) { _AutoplayPending = false; _PreviewSelectedSong(); } } public abstract void OnShow(); public virtual void OnHide() { _AutoplayTimer.Stop(); _AutoplayPending = false; var check = CBase.Graphics.GetNextScreenType(); if (CBase.Graphics.GetNextScreenType() == EScreen.Sing) { _ResetPreview(false); } else if ((CBase.Graphics.GetNextScreenType() != EScreen.Names && CBase.Graphics.GetNextScreenType() != EScreen.Highscore) || CBase.Config.GetBackgroundMusicStatus() == EBackgroundMusicOffOn.TR_CONFIG_OFF) { _ResetPreview(); } } public abstract bool HandleInput(ref SKeyEvent keyEvent, SScreenSongOptions options); public abstract bool HandleMouse(ref SMouseEvent mouseEvent, SScreenSongOptions songOptions); public virtual void Draw() { if (!_Initialized || !Visible) { return; } if (CBase.BackgroundMusic.IsPlaying()) { CBase.Drawing.DrawTexture(CBase.BackgroundMusic.GetVideoTexture(), new SRectF(0, 0, 1920, 1080, 0)); } } public virtual bool IsMouseOverSelectedSong(SMouseEvent mEvent) { var selCov = GetSelectedSongCover(); return selCov != null && CHelper.IsInBounds(selCov.Rect.Scale(SelectedTileZoomFactor), mEvent); } public int GetPreviewSongNr() { if (CBase.Songs.IsInCategory()) { return _PreviewNr; } return -1; } public int GetSelectedSongNr() { if (CBase.Songs.IsInCategory()) { return _SelectionNr; } return -1; } public abstract CStatic GetSelectedSongCover(); public int GetSelectedCategory() { if (!CBase.Songs.IsInCategory()) { return _SelectionNr; } return -1; } public void SetSelectedSong(int visibleSongNr) { Debug.Assert(CBase.Songs.IsInCategory()); if (visibleSongNr >= 0 && visibleSongNr < CBase.Songs.GetNumSongsVisible()) { _SelectionNr = visibleSongNr; } else { _SelectionNr = -1; } _PreviewNr = _SelectionNr; } public void SetSelectedCategory(int categoryNr) { Debug.Assert(!CBase.Songs.IsInCategory()); if (categoryNr >= 0 && categoryNr < CBase.Songs.GetNumCategories()) { _SelectionNr = categoryNr; } else { _SelectionNr = -1; } _PreviewNr = _SelectionNr; } public void LeaveSelectedCategory() { if (!CBase.Songs.IsInCategory()) { return; } var oldCategory = CBase.Songs.GetCurrentCategoryIndex(); _LeaveCategory(); if (oldCategory >= 0 && oldCategory < CBase.Songs.GetNumCategories()) { SetSelectedCategory(oldCategory); } else if (CBase.Songs.GetNumCategories() > 0) { SetSelectedCategory(0); } } public virtual void UnloadSkin() { } public virtual void LoadSkin() { Init(); _Theme.Color.Get(_PartyModeId, out _ColorInternal); } public void ReloadSkin() { UnloadSkin(); LoadSkin(); } public bool EnterSelectedCategory() { if (!_Initialized) { return false; } if (CBase.Songs.IsInCategory() || GetSelectedCategory() < 0 || GetSelectedCategory() >= CBase.Songs.GetNumCategories()) { return false; } _EnterCategory(GetSelectedCategory()); return true; } protected virtual void _EnterCategory(int categoryNr) { _AutoplayTimer.Stop(); _AutoplayPending = false; if (!_Initialized) { return; } if (categoryNr >= CBase.Songs.GetNumCategories()) { return; } _ResetPreview(false); CBase.Songs.SetCategory(categoryNr); } protected virtual void _LeaveCategory() { _AutoplayTimer.Stop(); _AutoplayPending = false; if (!_Initialized) { return; } if (!CBase.Songs.IsInCategory()) { return; } _ResetPreview(); CBase.Songs.SetCategory(-1); } private void _PlaySong(int nr) { _PreviewNrInternal = -1; var song = CBase.Songs.GetVisibleSong(nr); if (song == null) { return; } CBase.BackgroundMusic.LoadPreview(song, song.Preview.StartTime); } protected void _ResetPreview(bool playBGagain = true) { _AutoplayTimer.Stop(); _AutoplayPending = false; if (_PreviewNrInternal == -1) { return; } CBase.BackgroundMusic.StopPreview(); CBase.Sound.SetGlobalVolume(CBase.Config.GetMusicVolume(EMusicType.Background)); if (playBGagain) { CBase.BackgroundMusic.Play(); } //Make sure we don't have a preview here otherwise a change won't be recognized //(e.g. leave a category with one song and set preview to 0 --> previewOld=previewNew=0 --> No change --> Old data shown //Use internal nr because this function gets called from withing setPreviewNr _PreviewNrInternal = -1; } protected void _PreviewSelectedSong() { _PreviewNr = _SelectionNr; } protected void _InitializeAutoplayTimer() { _AutoplayTimer.Stop(); _AutoplayPending = false; _AutoplayTimer.Interval = _AutoplayDelayinMs; _AutoplayTimer.AutoReset = false; _AutoplayTimer.Elapsed -= _OnAutoplayTimerElapsed; _AutoplayTimer.Elapsed += _OnAutoplayTimerElapsed; } private void _OnAutoplayTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) { _AutoplayPending = true; } protected void _AutoplayPreviewIfEnabled() { if (CBase.Config.GetAutoplayPreviews() == EOffOn.TR_CONFIG_ON) { _PlayPreviewAfterDelay(); } } protected void _PlayPreviewAfterDelay() { _AutoplayPending = false; _AutoplayTimer.Stop(); _AutoplayTimer.Start(); } protected void _SetScrollTarget(int target, bool animate, int maxDistance) { if (!animate) { _ScrollTimer.Reset(); _ScrollTarget = target; return; } if (target == _ScrollTarget) { return; } var start = _GetScrollPosition(); _ScrollStart = start.Clamp((float)(target - maxDistance), target + maxDistance); _ScrollTarget = target; _ScrollTimer.Restart(); } protected float _GetScrollPosition() { if (!_ScrollTimer.IsRunning) { return _ScrollTarget; } var progress = _ScrollTimer.ElapsedMilliseconds / _ScrollDuration; if (progress >= 1f) { _ScrollTimer.Reset(); return _ScrollTarget; } var eased = 1f - (1f - progress) * (1f - progress); return _ScrollStart + (_ScrollTarget - _ScrollStart) * eased; } protected bool _IsScrolling() { return Math.Abs(_GetScrollPosition() - _ScrollTarget) > 0.001f; } protected void _DrawClipped(CTextureRef texture, SRectF rect, SColorF color, EAspect aspect) { var top = Math.Max(rect.Y, Rect.Y); var bottom = Math.Min(rect.Bottom, Rect.Bottom); if (bottom <= top) { return; } var bounds = new SRectF(rect.X, top, rect.W, bottom - top, rect.Z); if (texture == null) { CBase.Drawing.DrawRect(color, bounds); return; } CBase.Drawing.DrawTexture(texture, CHelper.FitInBounds(rect, texture.OrigAspect, aspect), color, bounds); } protected float _GetVisibleFraction(SRectF rect) { if (rect.H <= 0) { return 0f; } var visible = Math.Min(rect.Bottom, Rect.Bottom) - Math.Max(rect.Y, Rect.Y); return (visible / rect.H).Clamp(0f, 1f); } protected void _DrawScrollBar(CStatic scrollBar, CStatic scrollBarThumb, SRectF normalRect, float position, int numVisible, int numTotal) { if (scrollBar == null || numTotal <= numVisible) { return; } var barTheme = (SThemeStatic)scrollBar.GetTheme(); var track = barTheme.Rect; track.X += Rect.Right - normalRect.Right; track.Y += Rect.Y - normalRect.Y; track.H += Rect.H - normalRect.H; scrollBar.MaxRect = track; scrollBar.Draw(EAspect.Stretch); if (scrollBarThumb == null) { return; } var thumbTheme = (SThemeStatic)scrollBarThumb.GetTheme(); var thumbH = Math.Min(track.H, Math.Max(track.H * numVisible / numTotal, thumbTheme.Rect.W * 3f)); var progress = (position / (numTotal - numVisible)).Clamp(0f, 1f); scrollBarThumb.MaxRect = new SRectF(track.X + thumbTheme.Rect.X - barTheme.Rect.X, track.Y + (track.H - thumbH) * progress, thumbTheme.Rect.W, thumbH, thumbTheme.Rect.Z); scrollBarThumb.Draw(EAspect.Stretch); } #region ThemeEdit public void MoveElement(int stepX, int stepY) { X += stepX; Y += stepY; Init(); } public void ResizeElement(int stepW, int stepH) { W += stepW; if (W < 100) { W = 100; } H += stepH; if (H < 100) { H = 100; } Init(); } #endregion ThemeEdit } }