Improve song selection screen (#932)

* Redesign song selection screen

* Fix SonarCloud issues

* Add TileCaption to SConfig test file

---------

Co-authored-by: Florian Ostertag <flokuep@users.noreply.github.com>
This commit is contained in:
McSchneidarandFlorian Ostertag authored and GitHub committed 2026-10-21 10:25:40 +02:00
1 parent 6601f41aac
commit 74653ab977
19 files changed
+1710 -345

No files matched your search

@@ -15,8 +15,10 @@
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
using System.Diagnostics;
using System.Xml.Serialization;
using VocaluxeLib.Draw;
using VocaluxeLib.PartyModes;
namespace VocaluxeLib.Menu.SongMenu
@@ -78,6 +80,9 @@ namespace VocaluxeLib.Menu.SongMenu
public SThemeStatic StaticVideoIcon;
public SThemeStatic StaticMedleyCalcIcon;
public SThemeStatic StaticMedleyTagIcon;
public SThemeStatic? StaticScrollBar;
public SThemeStatic? StaticScrollBarThumb;
}
public struct SThemeSongMenuTileBoard
@@ -129,6 +134,12 @@ namespace VocaluxeLib.Menu.SongMenu
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
@@ -179,6 +190,11 @@ namespace VocaluxeLib.Menu.SongMenu
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
{
@@ -191,6 +207,7 @@ namespace VocaluxeLib.Menu.SongMenu
}
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; }
@@ -502,6 +519,107 @@ namespace VocaluxeLib.Menu.SongMenu
_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)
{
+283 -85
View File
@@ -17,6 +17,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using VocaluxeLib.Draw;
@@ -30,6 +31,7 @@ namespace VocaluxeLib.Menu.SongMenu
private SRectF _ScrollRect;
private List<CStatic> _Tiles;
private List<CText> _Texts;
private List<CText> _SubTexts;
private List<CStatic> _RowBackgrounds;
private readonly CStatic _CoverBig;
private readonly CStatic _TextBG;
@@ -49,11 +51,21 @@ namespace VocaluxeLib.Menu.SongMenu
private readonly CText _SongLength;
private readonly CText _SongYear;
private readonly CStatic _ScrollBar;
private readonly CStatic _ScrollBarThumb;
private CText _ScrollText;
private CText _ScrollSubText;
private float _SpaceW;
private float _SpaceH;
private int _TileW;
private int _TileH;
private float _TileH;
private const float _SelectedRowScale = 1.3f;
private const float _ExpandDuration = 120f;
private readonly Stopwatch _ExpandTimer = new Stopwatch();
private int _ExpandedNr = -1;
private int _PrevExpandedNr = -1;
private int _ListLength;
@@ -70,7 +82,7 @@ namespace VocaluxeLib.Menu.SongMenu
public override float SelectedTileZoomFactor
{
get { return 1.2f; }
get { return 1f; }
}
protected override int _SelectionNr
@@ -118,6 +130,7 @@ namespace VocaluxeLib.Menu.SongMenu
base._PreviewNr = value;
_UpdatePreview();
_UpdateExpandedRow();
}
}
@@ -141,6 +154,16 @@ namespace VocaluxeLib.Menu.SongMenu
_VocalsIcon = new CStatic(_Theme.SongMenuList.StaticVocalsIcon, _PartyModeId);
_SubElements.AddRange(new IMenuElement[]
{ _Artist, _Title, _SongLength, _SongYear, _DuetIcon, _RapIcon, _VideoIcon, _MedleyCalcIcon, _MedleyTagIcon, _InstrumentalIcon, _VocalsIcon });
if (_Theme.SongMenuList.StaticScrollBar.HasValue)
{
_ScrollBar = new CStatic(_Theme.SongMenuList.StaticScrollBar.Value, _PartyModeId);
}
if (_Theme.SongMenuList.StaticScrollBarThumb.HasValue)
{
_ScrollBarThumb = new CStatic(_Theme.SongMenuList.StaticScrollBarThumb.Value, _PartyModeId);
}
}
private void _ReadSubTheme()
@@ -158,6 +181,16 @@ namespace VocaluxeLib.Menu.SongMenu
_Theme.SongMenuList.StaticVocalsIcon = (SThemeStatic)_VocalsIcon.GetTheme();
_Theme.SongMenuList.StaticTextBG = (SThemeStatic)_TextBG.GetTheme();
_Theme.SongMenuList.StaticVideoIcon = (SThemeStatic)_VideoIcon.GetTheme();
if (_ScrollBar != null)
{
_Theme.SongMenuList.StaticScrollBar = (SThemeStatic)_ScrollBar.GetTheme();
}
if (_ScrollBarThumb != null)
{
_Theme.SongMenuList.StaticScrollBarThumb = (SThemeStatic)_ScrollBarThumb.GetTheme();
}
}
public override object GetTheme()
@@ -178,10 +211,7 @@ namespace VocaluxeLib.Menu.SongMenu
{
MaxRect = SmallView ? _Theme.SongMenuList.TileRectSmall : _Theme.SongMenuList.TileRect;
_TileW = (int)((MaxRect.H - _SpaceH * (_ListLength - 1)) / _ListLength);
_TileH = _TileW;
var _ListTextWidth = MaxRect.W - (_TileW + _SpaceW);
_TileH = (MaxRect.H - _SpaceH * (_ListLength - 1)) / (_ListLength - 1 + _SelectedRowScale);
_CoverBGTexture = CBase.Themes.GetSkinTexture(_Theme.CoverBackground, _PartyModeId);
_CoverBigBGTexture = CBase.Themes.GetSkinTexture(_Theme.CoverBigBackground, _PartyModeId);
@@ -190,58 +220,151 @@ namespace VocaluxeLib.Menu.SongMenu
_RowBackgrounds = new List<CStatic>();
_Tiles = new List<CStatic>();
_Texts = new List<CText>();
_SubTexts = new List<CText>();
for (var i = 0; i < _ListLength; i++)
{
//Create Cover
var y = Rect.Y + i * (_TileH + _SpaceH);
var bgHeight = _TileH + 8f;
var rowRect = new SRectF(Rect.X, y - 4f, Rect.W, bgHeight, Rect.Z + 0.05f);
var rowBackground = new CStatic(_PartyModeId, _CoverBGTexture, _Color, rowRect);
var rowBackground = new CStatic(_PartyModeId, _CoverBGTexture, _Color, Rect);
rowBackground.Color = new SColorF(1f, 1f, 1f, 0f);
_RowBackgrounds.Add(rowBackground);
var rect = new SRectF(Rect.X, y, _TileW, _TileH, Rect.Z);
var rect = new SRectF(Rect.X, Rect.Y, _TileH, _TileH, Rect.Z);
var tile = new CStatic(_PartyModeId, _CoverBGTexture, _Color, rect);
_Tiles.Add(tile);
//Create text
var textRect = new SRectF(MaxRect.X + (_TileW + _SpaceW), y, _ListTextWidth, _TileH, Rect.Z);
var text = new CText(textRect.X, textRect.Y, textRect.Z, textRect.H, textRect.W, EAlignment.Left, EStyle.Normal, "Normal", _Artist.Color, "");
text.MaxRect = new SRectF(text.MaxRect.X, text.MaxRect.Y, MaxRect.W + MaxRect.X - text.Rect.X - 5f, text.MaxRect.H, text.MaxRect.Z);
text.ResizeAlign = EHAlignment.Center;
_Texts.Add(text);
_Texts.Add(_CreateRowText(1f));
_SubTexts.Add(_CreateRowText(0.7f));
}
_ScrollText = _CreateRowText(1f);
_ScrollSubText = _CreateRowText(0.7f);
_ScrollRect = CBase.Settings.GetRenderRect();
_ExpandTimer.Reset();
_PrevExpandedNr = -1;
}
private CText _CreateRowText(float alpha)
{
var color = new SColorF(_Artist.Color.R, _Artist.Color.G, _Artist.Color.B, _Artist.Color.A * alpha);
var text = new CText(Rect.X, Rect.Y, Rect.Z, _TileH, Rect.W, EAlignment.Left, EStyle.Normal, "Normal", color, "");
text.ResizeAlign = EHAlignment.Center;
return text;
}
private float _GetExpandProgress()
{
if (!_ExpandTimer.IsRunning)
{
return 1f;
}
var progress = _ExpandTimer.ElapsedMilliseconds / _ExpandDuration;
if (progress >= 1f)
{
_ExpandTimer.Reset();
_PrevExpandedNr = -1;
return 1f;
}
return 1f - (1f - progress) * (1f - progress);
}
private float _GetRowHeight(int index, float progress)
{
var extra = _TileH * (_SelectedRowScale - 1f);
if (index == _ExpandedNr)
{
return _TileH + extra * progress;
}
if (index == _PrevExpandedNr)
{
return _TileH + extra * (1f - progress);
}
return _TileH;
}
private SRectF _GetRowRect(int index, float position, float progress)
{
var y = Rect.Y + (index - position) * (_TileH + _SpaceH);
for (var i = (int)Math.Floor(position); i < index; i++)
{
y += _GetRowHeight(i, progress) - _TileH;
}
return new SRectF(Rect.X, y, Rect.W, _GetRowHeight(index, progress), Rect.Z);
}
private void _LayoutRowTexts(CText title, CText artist, SRectF row, int index, bool isInCategory)
{
var x = row.X + row.H + _SpaceW;
var w = Rect.Right - x - 5f;
var itemCount = isInCategory ? CBase.Songs.GetNumSongsVisible() : CBase.Songs.GetNumCategories();
if (index >= itemCount)
{
title.Text = "";
artist.Text = "";
return;
}
title.Font.Style = index == _ExpandedNr ? EStyle.Bold : EStyle.Normal;
title.Font.Height = row.H * 0.56f;
title.MaxRect = new SRectF(x, row.Y, w, title.Font.Height, Rect.Z);
title.Text = _GetItemTitle(index, isInCategory);
artist.Font.Height = row.H * 0.42f;
artist.MaxRect = new SRectF(x, row.Y + row.H * 0.56f, w, artist.Font.Height, Rect.Z);
artist.Text = _GetItemSubTitle(index, isInCategory);
}
private void _UpdateRowLayout()
{
var progress = _GetExpandProgress();
var isInCategory = CBase.Songs.IsInCategory();
for (var i = 0; i < _Tiles.Count; i++)
{
var index = i + _Offset;
var row = _GetRowRect(index, _Offset, progress);
_Tiles[i].MaxRect = new SRectF(row.X, row.Y, row.H, row.H, row.Z);
_RowBackgrounds[i].MaxRect = new SRectF(row.X, row.Y - 4f, row.W, row.H + 8f, row.Z + 0.05f);
_RowBackgrounds[i].Color = new SColorF(1f, 1f, 1f, index == _ExpandedNr ? 0.25f : 0f);
_LayoutRowTexts(_Texts[i], _SubTexts[i], row, index, isInCategory);
}
}
private void _UpdateExpandedRow()
{
var expandedNr = _SelectionNr >= 0 ? _SelectionNr : _PreviewNr;
if (expandedNr == _ExpandedNr)
{
return;
}
_PrevExpandedNr = _ExpandedNr;
_ExpandedNr = expandedNr;
_ExpandTimer.Restart();
}
private void _UpdateTileSelection()
{
for (var i = 0; i < _Tiles.Count; i++)
foreach (var tile in _Tiles)
{
_Tiles[i].Selected = false;
if (i < _RowBackgrounds.Count)
{
_RowBackgrounds[i].Color = new SColorF(1f, 1f, 1f, 0f);
}
tile.Selected = false;
}
var tileNr = _SelectionNr - _Offset;
if (tileNr >= 0 && tileNr < _Tiles.Count)
{
_Tiles[tileNr].Selected = true;
if (tileNr < _RowBackgrounds.Count)
{
_RowBackgrounds[tileNr].Color = new SColorF(1f, 1f, 1f, 0.25f);
}
}
_UpdateExpandedRow();
}
public override void Update(SScreenSongOptions songOptions)
@@ -297,14 +420,14 @@ namespace VocaluxeLib.Menu.SongMenu
}
_CoverBig.Texture = category.CoverTextureBig;
_Artist.Text = category.Name;
_Title.Text = category.Name;
return;
}
_CoverBig.Texture = song.CoverTextureBig;
_Artist.Text = song.Artist;
_Title.Text = song.Title;
_SongYear.Text = song.Year;
_SongYear.Text = string.IsNullOrEmpty(song.Year) ? "-" : song.Year;
_DuetIcon.Visible = song.IsDuet;
_RapIcon.Visible = song.IsRap;
_VideoIcon.Visible = song.Video != "";
@@ -325,11 +448,11 @@ namespace VocaluxeLib.Menu.SongMenu
}
_CoverBig.Texture = category.CoverTextureBig;
_Artist.Text = category.Name;
_Title.Text = category.Name;
var num = category.GetNumSongsNotSung();
var songOrSongs = num == 1 ? "TR_SCREENSONG_NUMSONG" : "TR_SCREENSONG_NUMSONGS";
_Title.Text = CBase.Language.Translate(songOrSongs).Replace("%v", num.ToString());
_Artist.Text = CBase.Language.Translate(songOrSongs).Replace("%v", num.ToString());
}
}
@@ -651,43 +774,39 @@ namespace VocaluxeLib.Menu.SongMenu
public override void Draw()
{
foreach (var rowBackground in _RowBackgrounds)
if (_IsScrolling())
{
rowBackground.Draw(EAspect.Stretch);
_DrawScrollingList();
}
foreach (var tile in _Tiles)
else
{
if (tile.Selected)
_UpdateRowLayout();
foreach (var rowBackground in _RowBackgrounds)
{
tile.Draw(EAspect.Crop, SelectedTileZoomFactor, -0.1f);
rowBackground.Draw(EAspect.Stretch);
}
else
foreach (var tile in _Tiles)
{
var aspect = tile.Texture != _CoverBGTexture ? EAspect.Crop : EAspect.Stretch;
tile.Draw(aspect);
}
//highlight the text of the selected song
for (var i = 0; i < _Texts.Count; i++)
{
_Texts[i].Draw();
_SubTexts[i].Draw();
}
}
//highlight the text of the selected song
var i = 0;
foreach (var text in _Texts)
{
if (i < _Tiles.Count && _Tiles[i].Selected)
{
text.Font.Style = EStyle.Bold;
}
else if (i < _Tiles.Count)
{
text.Font.Style = EStyle.Normal;
}
else
{
text.Text = "";
}
var itemCount = CBase.Songs.IsInCategory() ? CBase.Songs.GetNumSongsVisible() : CBase.Songs.GetNumCategories();
_DrawScrollBar(_ScrollBar, _ScrollBarThumb, _Theme.SongMenuList.TileRect, _GetScrollPosition(), _ListLength, itemCount);
text.Draw();
i++;
if (!PreviewVisible)
{
return;
}
_TextBG.Draw();
@@ -720,6 +839,93 @@ namespace VocaluxeLib.Menu.SongMenu
}
}
private void _DrawScrollingList()
{
var position = _GetScrollPosition();
var progress = _GetExpandProgress();
var isInCategory = CBase.Songs.IsInCategory();
var itemCount = isInCategory ? CBase.Songs.GetNumSongsVisible() : CBase.Songs.GetNumCategories();
for (var index = Math.Max((int)Math.Floor(position), 0); index < itemCount; index++)
{
var row = _GetRowRect(index, position, progress);
if (row.Y >= Rect.Bottom)
{
break;
}
if (index == _ExpandedNr)
{
_DrawClipped(_CoverBGTexture, new SRectF(row.X, row.Y - 4f, row.W, row.H + 8f, row.Z + 0.05f), new SColorF(1f, 1f, 1f, 0.25f), EAspect.Stretch);
}
var rect = new SRectF(row.X, row.Y, row.H, row.H, row.Z);
_DrawClipped(_GetCoverTexture(index, isInCategory), rect, new SColorF(1f, 1f, 1f, 1f), EAspect.Crop);
_LayoutRowTexts(_ScrollText, _ScrollSubText, row, index, isInCategory);
var visibleFraction = _GetVisibleFraction(rect);
_ScrollText.Alpha = visibleFraction;
_ScrollText.Draw();
_ScrollSubText.Alpha = visibleFraction;
_ScrollSubText.Draw();
}
}
private static CTextureRef _GetCoverTexture(int index, bool isInCategory)
{
return isInCategory ? CBase.Songs.GetVisibleSong(index).CoverTextureSmall : CBase.Songs.GetCategory(index).CoverTextureSmall;
}
private static string _GetItemText(int index, bool isInCategory)
{
if (isInCategory)
{
var song = CBase.Songs.GetVisibleSong(index);
if (CBase.Config.GetSongSorting() == ESongSorting.TR_CONFIG_TITLE_LETTER)
{
return song.Title + " - " + song.Artist;
}
return song.Artist + " - " + song.Title;
}
var category = CBase.Songs.GetCategory(index);
var num = category.GetNumSongsNotSung();
var songOrSongs = num == 1 ? "TR_SCREENSONG_NUMSONG" : "TR_SCREENSONG_NUMSONGS";
return category.Name + " " + CBase.Language.Translate(songOrSongs).Replace("%v", num.ToString());
}
private static bool _IsSortedByArtistLetter()
{
return CBase.Config.GetSongSorting() == ESongSorting.TR_CONFIG_ARTIST_LETTER;
}
private static string _GetItemTitle(int index, bool isInCategory)
{
if (!isInCategory)
{
return CBase.Songs.GetCategory(index).Name;
}
var song = CBase.Songs.GetVisibleSong(index);
return _IsSortedByArtistLetter() ? song.Artist : song.Title;
}
private static string _GetItemSubTitle(int index, bool isInCategory)
{
if (isInCategory)
{
var song = CBase.Songs.GetVisibleSong(index);
return _IsSortedByArtistLetter() ? song.Title : song.Artist;
}
var num = CBase.Songs.GetCategory(index).GetNumSongsNotSung();
var songOrSongs = num == 1 ? "TR_SCREENSONG_NUMSONG" : "TR_SCREENSONG_NUMSONGS";
return CBase.Language.Translate(songOrSongs).Replace("%v", num.ToString());
}
public override CStatic GetSelectedSongCover()
{
return _Tiles.FirstOrDefault(tile => tile.Selected);
@@ -734,7 +940,8 @@ namespace VocaluxeLib.Menu.SongMenu
continue;
}
return CHelper.IsInBounds(_Tiles[i].Rect, mEvent) || CHelper.IsInBounds(_Texts[i].Rect, mEvent);
var tileRect = _Tiles[i].Rect;
return CHelper.IsInBounds(new SRectF(tileRect.X, tileRect.Y, Rect.W, tileRect.H, tileRect.Z), mEvent);
}
return false;
@@ -836,42 +1043,31 @@ namespace VocaluxeLib.Menu.SongMenu
return;
}
if (force)
{
_ExpandTimer.Reset();
_ExpandedNr = -1;
_PrevExpandedNr = -1;
}
for (var i = 0; i < _Tiles.Count; i++)
{
if (i + offset < itemCount)
{
_Tiles[i].Color = new SColorF(1f, 1f, 1f, 1f);
if (isInCategory)
{
var currentSong = CBase.Songs.GetVisibleSong(i + offset);
_Tiles[i].Texture = currentSong.CoverTextureSmall;
if (CBase.Config.GetSongSorting() == ESongSorting.TR_CONFIG_TITLE_LETTER)
{
_Texts[i].Text = currentSong.Title + " - " + currentSong.Artist;
}
else
{
_Texts[i].Text = currentSong.Artist + " - " + currentSong.Title;
}
}
else
{
var currentCat = CBase.Songs.GetCategory(i + offset);
_Tiles[i].Texture = currentCat.CoverTextureSmall;
var num = currentCat.GetNumSongsNotSung();
var songOrSongs = num == 1 ? "TR_SCREENSONG_NUMSONG" : "TR_SCREENSONG_NUMSONGS";
_Texts[i].Text = currentCat.Name + " " + CBase.Language.Translate(songOrSongs).Replace("%v", num.ToString());
}
_Tiles[i].Texture = _GetCoverTexture(i + offset, isInCategory);
_Texts[i].Text = _GetItemText(i + offset, isInCategory);
}
else
{
_Tiles[i].Texture = _CoverBGTexture;
_Tiles[i].Color.A = 0;
_Texts[i].Text = "";
_SubTexts[i].Text = "";
}
}
_SetScrollTarget(offset, !force && offset != _Offset, _ListLength);
_Offset = offset;
_UpdateTileSelection();
}
@@ -886,6 +1082,8 @@ namespace VocaluxeLib.Menu.SongMenu
// Those are drawn seperately so they are not in the above list
_CoverBig.LoadSkin();
_TextBG.LoadSkin();
_ScrollBar?.LoadSkin();
_ScrollBarThumb?.LoadSkin();
base.LoadSkin();
}
+209 -13
View File
@@ -47,6 +47,12 @@ namespace VocaluxeLib.Menu.SongMenu
private CText _SongLength;
private CText _SongYear;
private readonly CStatic _ScrollBar;
private readonly CStatic _ScrollBarThumb;
private readonly CStatic _TileCaptionBG;
private readonly CText _TileArtist;
private readonly CText _TileTitle;
private int _NumW;
private int _NumH;
@@ -62,6 +68,8 @@ namespace VocaluxeLib.Menu.SongMenu
private bool _MouseWasInRect;
private const float _UnfocusedAlpha = 0.85f;
private readonly List<IMenuElement> _SubElements = new List<IMenuElement>();
public override float SelectedTileZoomFactor
@@ -136,6 +144,31 @@ namespace VocaluxeLib.Menu.SongMenu
_VocalsIcon = new CStatic(_Theme.SongMenuTileBoard.StaticVocalsIcon, _PartyModeId);
_SubElements.AddRange(new IMenuElement[]
{ _Artist, _Title, _SongLength, _SongYear, _DuetIcon, _RapIcon, _VideoIcon, _MedleyCalcIcon, _MedleyTagIcon, _InstrumentalIcon, _VocalsIcon });
if (_Theme.SongMenuTileBoard.StaticScrollBar.HasValue)
{
_ScrollBar = new CStatic(_Theme.SongMenuTileBoard.StaticScrollBar.Value, _PartyModeId);
}
if (_Theme.SongMenuTileBoard.StaticScrollBarThumb.HasValue)
{
_ScrollBarThumb = new CStatic(_Theme.SongMenuTileBoard.StaticScrollBarThumb.Value, _PartyModeId);
}
if (_Theme.SongMenuTileBoard.StaticTileCaptionBG.HasValue)
{
_TileCaptionBG = new CStatic(_Theme.SongMenuTileBoard.StaticTileCaptionBG.Value, _PartyModeId);
}
if (_Theme.SongMenuTileBoard.TextTileArtist.HasValue)
{
_TileArtist = new CText(_Theme.SongMenuTileBoard.TextTileArtist.Value, _PartyModeId);
}
if (_Theme.SongMenuTileBoard.TextTileTitle.HasValue)
{
_TileTitle = new CText(_Theme.SongMenuTileBoard.TextTileTitle.Value, _PartyModeId);
}
}
private void _UpdateTileSelection()
@@ -167,6 +200,31 @@ namespace VocaluxeLib.Menu.SongMenu
_Theme.SongMenuTileBoard.StaticVideoIcon = (SThemeStatic)_VideoIcon.GetTheme();
_Theme.SongMenuTileBoard.StaticInstrumentalIcon = (SThemeStatic)_InstrumentalIcon.GetTheme();
_Theme.SongMenuTileBoard.StaticVocalsIcon = (SThemeStatic)_VocalsIcon.GetTheme();
if (_ScrollBar != null)
{
_Theme.SongMenuTileBoard.StaticScrollBar = (SThemeStatic)_ScrollBar.GetTheme();
}
if (_ScrollBarThumb != null)
{
_Theme.SongMenuTileBoard.StaticScrollBarThumb = (SThemeStatic)_ScrollBarThumb.GetTheme();
}
if (_TileCaptionBG != null)
{
_Theme.SongMenuTileBoard.StaticTileCaptionBG = (SThemeStatic)_TileCaptionBG.GetTheme();
}
if (_TileArtist != null)
{
_Theme.SongMenuTileBoard.TextTileArtist = (SThemeText)_TileArtist.GetTheme();
}
if (_TileTitle != null)
{
_Theme.SongMenuTileBoard.TextTileTitle = (SThemeText)_TileTitle.GetTheme();
}
}
public override object GetTheme()
@@ -271,14 +329,14 @@ namespace VocaluxeLib.Menu.SongMenu
}
_CoverBig.Texture = category.CoverTextureBig;
_Artist.Text = category.Name;
_Title.Text = category.Name;
return;
}
_CoverBig.Texture = song.CoverTextureBig;
_Artist.Text = song.Artist;
_Title.Text = song.Title;
_SongYear.Text = song.Year;
_SongYear.Text = string.IsNullOrEmpty(song.Year) ? "-" : song.Year;
_DuetIcon.Visible = song.IsDuet;
_RapIcon.Visible = song.IsRap;
_VideoIcon.Visible = song.Video != "";
@@ -299,11 +357,11 @@ namespace VocaluxeLib.Menu.SongMenu
}
_CoverBig.Texture = category.CoverTextureBig;
_Artist.Text = category.Name;
_Title.Text = category.Name;
var num = category.GetNumSongsNotSung();
var songOrSongs = num == 1 ? "TR_SCREENSONG_NUMSONG" : "TR_SCREENSONG_NUMSONGS";
_Title.Text = CBase.Language.Translate(songOrSongs).Replace("%v", num.ToString());
_Artist.Text = CBase.Language.Translate(songOrSongs).Replace("%v", num.ToString());
}
}
@@ -623,17 +681,42 @@ namespace VocaluxeLib.Menu.SongMenu
public override void Draw()
{
foreach (var tile in _Tiles)
if (_IsScrolling())
{
if (tile.Selected)
_DrawScrollingTiles();
}
else
{
var focusNr = _GetFocusNr();
var dimTiles = focusNr >= _Offset && focusNr < _Offset + _Tiles.Count;
for (var i = 0; i < _Tiles.Count; i++)
{
tile.Draw(EAspect.Crop, SelectedTileZoomFactor, -0.1f);
}
else
{
var aspect = tile.Texture != _CoverBGTexture ? EAspect.Crop : EAspect.Stretch;
tile.Draw(aspect);
var tile = _Tiles[i];
tile.Alpha = dimTiles && i + _Offset != focusNr ? _UnfocusedAlpha : 1f;
if (tile.Selected)
{
var frame = tile.Rect.Scale(SelectedTileZoomFactor);
CBase.Drawing.DrawRect(new SColorF(1f, 1f, 1f, 0.9f), new SRectF(frame.X - 3f, frame.Y - 3f, frame.W + 6f, frame.H + 6f, frame.Z - 0.05f));
tile.Draw(EAspect.Crop, SelectedTileZoomFactor, -0.1f);
}
else
{
var aspect = tile.Texture != _CoverBGTexture ? EAspect.Crop : EAspect.Stretch;
tile.Draw(aspect);
}
}
_DrawTileCaption();
}
var itemCount = CBase.Songs.IsInCategory() ? CBase.Songs.GetNumSongsVisible() : CBase.Songs.GetNumCategories();
_DrawScrollBar(_ScrollBar, _ScrollBarThumb, _Theme.SongMenuTileBoard.TileRect, _GetScrollPosition(), _NumH, (itemCount + _NumW - 1) / _NumW);
if (!PreviewVisible)
{
return;
}
_TextBG.Draw();
@@ -666,6 +749,114 @@ namespace VocaluxeLib.Menu.SongMenu
}
}
private void _DrawScrollingTiles()
{
var position = _GetScrollPosition();
var firstRow = (int)Math.Floor(position);
var isInCategory = CBase.Songs.IsInCategory();
var itemCount = isInCategory ? CBase.Songs.GetNumSongsVisible() : CBase.Songs.GetNumCategories();
var focusNr = _GetFocusNr();
for (var row = Math.Max(firstRow, 0); row <= firstRow + _NumH; row++)
{
var y = Rect.Y + (row - position) * (_TileH + _Theme.SongMenuTileBoard.SpaceH);
for (var col = 0; col < _NumW; col++)
{
var index = row * _NumW + col;
var rect = new SRectF(Rect.X + col * (_TileW + _Theme.SongMenuTileBoard.SpaceW), y, _TileW, _TileH, Rect.Z);
if (index < itemCount)
{
var alpha = focusNr >= 0 && index != focusNr ? _UnfocusedAlpha : 1f;
_DrawClipped(_GetCoverTexture(index, isInCategory), rect, new SColorF(1f, 1f, 1f, alpha), EAspect.Crop);
}
else
{
_DrawClipped(_CoverBGTexture, rect, _Color, EAspect.Stretch);
}
}
}
}
private int _GetFocusNr()
{
return _SelectionNr >= 0 ? _SelectionNr : _PreviewNr;
}
private void _DrawTileCaption()
{
if ((_TileCaptionBG == null && _TileArtist == null && _TileTitle == null) || CBase.Config.GetTileCaption() == EOffOn.TR_CONFIG_OFF)
{
return;
}
var tile = GetSelectedSongCover();
if (tile == null)
{
return;
}
string artist;
string title;
if (CBase.Songs.IsInCategory())
{
var song = CBase.Songs.GetVisibleSong(_SelectionNr);
if (song == null)
{
return;
}
artist = song.Artist;
title = song.Title;
}
else
{
var category = CBase.Songs.GetCategory(_SelectionNr);
if (category == null)
{
return;
}
var num = category.GetNumSongsNotSung();
artist = category.Name;
title = CBase.Language.Translate(num == 1 ? "TR_SCREENSONG_NUMSONG" : "TR_SCREENSONG_NUMSONGS").Replace("%v", num.ToString());
}
var rect = tile.Rect;
CBase.Drawing.PushTransform(SelectedTileZoomFactor, rect.X + rect.W / 2f, rect.Y + rect.H / 2f, 0f, 1f);
if (_TileCaptionBG != null)
{
var bgRect = ((SThemeStatic)_TileCaptionBG.GetTheme()).Rect;
_TileCaptionBG.MaxRect = new SRectF(rect.X + bgRect.X, rect.Y + bgRect.Y, Math.Min(bgRect.W, rect.W - bgRect.X), bgRect.H, rect.Z - 0.2f);
_TileCaptionBG.Draw(EAspect.Stretch);
}
_DrawTileCaptionText(_TileArtist, artist, rect);
_DrawTileCaptionText(_TileTitle, title, rect);
CBase.Drawing.PopTransform();
}
private static void _DrawTileCaptionText(CText text, string value, SRectF tileRect)
{
if (text == null)
{
return;
}
var theme = (SThemeText)text.GetTheme();
text.X = tileRect.X + theme.X;
text.Y = tileRect.Y + theme.Y;
text.Z = tileRect.Z - 0.3f;
text.Text = value;
text.Draw();
}
private static CTextureRef _GetCoverTexture(int index, bool isInCategory)
{
return isInCategory ? CBase.Songs.GetVisibleSong(index).CoverTextureSmall : CBase.Songs.GetCategory(index).CoverTextureSmall;
}
public override CStatic GetSelectedSongCover()
{
return _Tiles.FirstOrDefault(tile => tile.Selected);
@@ -757,7 +948,7 @@ namespace VocaluxeLib.Menu.SongMenu
{
if (itemCount > i + offset)
{
_Tiles[i].Texture = isInCategory ? CBase.Songs.GetVisibleSong(i + offset).CoverTextureSmall : CBase.Songs.GetCategory(i + offset).CoverTextureSmall;
_Tiles[i].Texture = _GetCoverTexture(i + offset, isInCategory);
_Tiles[i].Color = new SColorF(1f, 1f, 1f, 1f);
}
else
@@ -767,6 +958,7 @@ namespace VocaluxeLib.Menu.SongMenu
}
}
_SetScrollTarget(offset / _NumW, !force && offset != _Offset, _NumH);
_Offset = offset;
}
@@ -781,6 +973,10 @@ namespace VocaluxeLib.Menu.SongMenu
// Those are drawn seperately so they are not in the above list
_CoverBig.LoadSkin();
_TextBG.LoadSkin();
foreach (var themeable in new IThemeable[] { _ScrollBar, _ScrollBarThumb, _TileCaptionBG, _TileArtist, _TileTitle }.Where(t => t != null))
{
themeable.LoadSkin();
}
Init();
}
+1
View File
@@ -46,5 +46,6 @@ namespace VocaluxeLib.Menu.SongMenu
int GetPreviewSong();
bool SmallView { get; set; }
bool PreviewVisible { get; set; }
}
}