#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
}
}