Bläddra i källkod

add loading status icon for playlist; add Playlist mode

Coder 7 månader sedan
förälder
incheckning
c3f8bd9a5f
2 ändrade filer med 213 tillägg och 28 borttagningar
  1. 212 28
      Bmp.WinForms/MainForm.cs
  2. 1 0
      Bmp.WinForms/SaveLoad/Models/SaveLoadState.cs

+ 212 - 28
Bmp.WinForms/MainForm.cs

@@ -1,5 +1,9 @@
 using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Diagnostics;
+using System.Diagnostics.Eventing.Reader;
 using System.Drawing.Drawing2D;
+using System.Reflection;
 using System.Text;
 using System.Threading.Channels;
 using Bmp.Core.Common.EventBus;
@@ -14,6 +18,7 @@ using Bmp.WinForms.SaveLoad;
 using Bmp.WinForms.SaveLoad.Models;
 using EnumsNET;
 using NAudio.Wave;
+using Newtonsoft.Json.Linq;
 using AsioOut = Bmp.Core.Lite.Playback.Outputs.NAudioASIO.AsioOut;
 
 namespace Bmp.WinForms
@@ -24,8 +29,10 @@ namespace Bmp.WinForms
         private const string EMOJI_WARN = "⚠";
         private const string EMOJI_PLAY_BIG = "▶";
         private const string EMOJI_PLAY_SMALL = "⏵";
+        private const string EMOJI_LOADING = "⏳";
 
         private static readonly IReadOnlyCollection<string> SupportedDropTypes = new[] { DataFormats.FileDrop, DataFormats.Text };
+        private static readonly Random Rng = new();
 
         private readonly Channel<string> _pendingAddToList = Channel.CreateUnbounded<string>();
 
@@ -58,6 +65,7 @@ namespace Bmp.WinForms
             Text = Const.AppTitle;
             var eIcon = IconExtractor.GetMainIcon();
             if (null != eIcon) Icon = eIcon;
+            UpdatePlaylistModeButton();
         }
 
         // -----------------  playback control -----------------
@@ -70,7 +78,9 @@ namespace Bmp.WinForms
             _playbackState = UIPlaybackState.Loading;
 
             _currentListViewItem = item;
-
+            _currentListViewItem.EnsureVisible();
+            _currentListViewItem.SubItems[StateColumnHeader.Index].Text = EMOJI_LOADING;
+            
             var finalState = EMOJI_PLAY_BIG;
 
             var balloonShow = false;
@@ -380,19 +390,142 @@ namespace Bmp.WinForms
 
         private async Task TrackPrevAsync()
         {
-            //TODO: 上一曲(按播放模式)
-            if (_currentListViewItem is not { ListView: { } }) return;
-            var nextIndex = _currentListViewItem.Index - 1;
-            if (nextIndex > 0) await LoadItemAsync(MainListView.Items[nextIndex]);
+            var itemsCount = MainListView.Items.Count;
+
+            if (itemsCount == 0)
+            {
+                //列表为空 下一个鬼,直接停止
+                await StopAsync();
+                return;
+            }
+
+            var playlistMode = _saveLoadService.State.PlaylistMode;
+
+            int? nextIndex; // 下一项,null表示停止
+
+            //当前项已消失时
+            //   随机 go random brr
+            //   其他 回到第一项
+            if (_currentListViewItem?.ListView == null)
+            {
+                if (itemsCount > 2 && playlistMode == PlaylistMode.Random)
+                    nextIndex = Rng.Next(itemsCount);
+                else
+                    nextIndex = 1;
+            }
+            else
+            {
+                var currentIndex = _currentListViewItem.Index;
+                switch (playlistMode)
+                {
+                    default:
+                    case PlaylistMode.Normal:
+                        nextIndex = currentIndex - 1;
+                        // 在第一项往前,无反应
+                        if (nextIndex.Value < 0) return;
+                        break;
+
+                    case PlaylistMode.LoopList:
+                        nextIndex = currentIndex - 1;
+                        // 最后一项播放完,跳到最后一项
+                        if (nextIndex.Value < 0) nextIndex = itemsCount - 1;
+                        break;
+
+                    case PlaylistMode.LoopTrack:
+                        //保持不变
+                        nextIndex = currentIndex;
+                        break;
+
+                    case PlaylistMode.Random:
+                        //go random brr
+                        nextIndex = Rng.Next(itemsCount);
+                        break;
+
+                    case PlaylistMode.Once:
+                        //无条件停止
+                        nextIndex = null;
+                        break;
+                }
+            }
+
+            if (nextIndex.HasValue)
+            {
+                await LoadItemAsync(MainListView.Items[nextIndex.Value]);
+            }
+            else
+            {
+                await StopAsync();
+            }
         }
 
         private async Task TrackNextAsync()
         {
-            //TODO: 下一曲(按播放模式)
-            if (_currentListViewItem?.ListView == null) return;
-            var nextIndex = _currentListViewItem.Index + 1;
-            if (MainListView.Items.Count > nextIndex) await LoadItemAsync(MainListView.Items[nextIndex]);
-            else _playbackState = UIPlaybackState.Stopped; //列表最后一项时
+            var itemsCount = MainListView.Items.Count;
+
+            if (itemsCount == 0)
+            {
+                //列表为空 下一个鬼,直接停止
+                await StopAsync();
+                return;
+            }
+
+            int? nextIndex; // 下一项,null表示停止
+
+            var playlistMode = _saveLoadService.State.PlaylistMode;
+
+            //当前项已消失时
+            //   随机 go random brr
+            //   其他 回到第一项
+            if (_currentListViewItem?.ListView == null)
+            {
+                if (itemsCount > 2 && playlistMode == PlaylistMode.Random)
+                    nextIndex = Rng.Next(itemsCount);
+                else
+                    nextIndex = 1;
+            }
+            else
+            {
+                var currentIndex = _currentListViewItem.Index;
+                switch (playlistMode)
+                {
+                    default:
+                    case PlaylistMode.Normal:
+                        nextIndex = currentIndex + 1;
+                        // 最后一项播放完,停止
+                        if (nextIndex.Value > itemsCount - 1) nextIndex = null;
+                        break;
+
+                    case PlaylistMode.LoopList:
+                        nextIndex = currentIndex + 1;
+                        // 最后一项播放完,回到第一项
+                        if (nextIndex.Value > itemsCount - 1) nextIndex = 0;
+                        break;
+
+                    case PlaylistMode.LoopTrack:
+                        //保持不变
+                        nextIndex = currentIndex;
+                        break;
+
+                    case PlaylistMode.Random:
+                        //go random brr
+                        nextIndex = Rng.Next(itemsCount);
+                        break;
+
+                    case PlaylistMode.Once:
+                        //无条件停止
+                        nextIndex = null;
+                        break;
+                }
+            }
+
+            if (nextIndex.HasValue)
+            {
+                await LoadItemAsync(MainListView.Items[nextIndex.Value]);
+            }
+            else
+            {
+                await StopAsync();
+            }
         }
 
         // -----------------  playback event -----------------
@@ -409,7 +542,6 @@ namespace Bmp.WinForms
 
             if (e.Exception == null)
             {
-                //TODO: 正常播放结束 下一曲(按播放模式)
                 await TrackNextAsync();
             }
             else
@@ -644,10 +776,22 @@ namespace Bmp.WinForms
 #endif
         }
 
+        private void UpdatePlaylistModeButton()
+        {
+            var value = _saveLoadService.State.PlaylistMode;
+
+            var type = value.GetType();
+            var name = Enum.GetName(type, value);
+            var d = type.GetField(name)?.GetCustomAttribute<DisplayAttribute>();
+
+            PlaylistModeButton.Text = d?.ShortName;
+        }
+
         // -----------------  UI event: Form -----------------
 
         private void MainForm_Shown(object sender, EventArgs e)
         {
+            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
             //var lvi = new ListViewItem();
 
             //AlbumImageList.Images.Add(this.Icon);
@@ -948,6 +1092,49 @@ namespace Bmp.WinForms
 
         private async void NextButton_Click(object sender, EventArgs e) => await TrackNextAsync();
 
+        private void PlaylistModeButton_Paint(object sender, PaintEventArgs e)
+        {
+            // Draw the arrow glyph on the right side of the button
+            var arrowX = PlaylistModeButton.ClientRectangle.Width - 15;
+            var arrowY = PlaylistModeButton.ClientRectangle.Height / 2 + 12;
+
+            var arrowBrush = PlaylistModeButton.Enabled ? SystemBrushes.ControlText : SystemBrushes.ButtonShadow;
+            var arrows = new[]
+            {
+                new PointF(arrowX, arrowY),
+                new PointF(arrowX + 10, arrowY),
+                new PointF(arrowX + 5 , arrowY +5 )
+            };
+            e.Graphics.FillPolygon(arrowBrush, arrows);
+        }
+
+        private void PlaylistModeButton_Click(object sender, EventArgs e)
+        {
+            var cm = new ContextMenuStrip { ShowItemToolTips = true };
+            foreach (var item in typeof(PlaylistMode).GetFields(BindingFlags.Public | BindingFlags.Static))
+            {
+                var attr = item.GetCustomAttribute<DisplayAttribute>();
+                if (attr == null) continue;
+                var mi = new ToolStripMenuItem
+                {
+                    Text = attr.Name,
+                    ToolTipText = attr.Description
+                };
+
+                var value = (PlaylistMode)item.GetRawConstantValue()!;
+                if (_saveLoadService.State.PlaylistMode == value) mi.CheckState = CheckState.Indeterminate;
+
+                mi.Click += delegate
+                {
+                    _saveLoadService.State.PlaylistMode = value;
+                    UpdatePlaylistModeButton();
+                };
+
+                cm.Items.Add(mi);
+            }
+            cm.Show(PlaylistModeButton, 0, PlaylistModeButton.Height);
+        }
+
         // -----------------  UI event: Others -----------------
 
         private void UpdateTimer_Tick(object sender, EventArgs e)
@@ -1114,27 +1301,24 @@ namespace Bmp.WinForms
             MainPanel.Enabled = true;
             _trackBarHolding = false;
         }
+    }
 
-        private void PlaylistModeButton_Paint(object sender, PaintEventArgs e)
-        {
-            // Draw the arrow glyph on the right side of the button
-            var arrowX = PlaylistModeButton.ClientRectangle.Width - 24;
-            var arrowY = PlaylistModeButton.ClientRectangle.Height / 2 + 12;
+    internal enum PlaylistMode
+    {
+        [Display(ShortName = "⇶", Name = "正常", Description = "播放完当前项自动播放下一项,直到列表最后一项播放完,停止")]
+        Normal = 0,
 
-            var arrowBrush = PlaylistModeButton.Enabled ? SystemBrushes.ControlText : SystemBrushes.ButtonShadow;
-            var arrows = new[]
-            {
-                new PointF(arrowX, arrowY),
-                new PointF(arrowX + 18, arrowY),
-                new PointF(arrowX + 9 , arrowY + 9 )
-            };
-            e.Graphics.FillPolygon(arrowBrush, arrows);
-        }
+        [Display(ShortName = "🔁", Name = "列表循环", Description = "无限循环整个播放列表")]
+        LoopList = 1,
 
-        private void PlaylistModeButton_Click(object sender, EventArgs e)
-        {
+        [Display(ShortName = "🔂", Name = "单曲循环", Description = "无限循环当前项")]
+        LoopTrack = 2,
 
-        }
+        [Display(ShortName = "🔀", Name = "随机", Description = "无限随机整个播放列表")]
+        Random = 3,
+
+        [Display(ShortName = "⤞", Name = "一次", Description = "播放完当前项,停止")]
+        Once = 4,
     }
 
     internal enum UIPlaybackState

+ 1 - 0
Bmp.WinForms/SaveLoad/Models/SaveLoadState.cs

@@ -13,4 +13,5 @@ internal class SaveLoadState
     public bool DecodeDsdToPcm { get; set; }
 
     public IReadOnlyCollection<SaveLoadPlaylistItem>? Playlist { get; set; }
+    public PlaylistMode PlaylistMode { get; set; }
 }