Files
YwxAppWpfBarrage/YwxAppWpfDanMu/Controls/DanMuControl.xaml.cs

215 lines
6.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using YwxAppWpfDanMu.Models;
using YwxAppWpfDanMu.Services;
using YwxAppWpfDanMu.Utils;
namespace YwxAppWpfDanMu.Controls
{
public partial class DanMuControl : UserControl
{
private readonly DanMuPool _danMuPool;
private readonly DanMuDispatcher _dispatcher;
private readonly DanMuQueueProcessor _queueProcessor;
private readonly RateLimiter _rateLimiter;
private readonly DanMuPerformanceMonitor _performanceMonitor;
internal List<DanMuTrack> _tracks = new List<DanMuTrack>();
private readonly Dictionary<DanMuItem, DanMuTrack> _activeItems = new Dictionary<DanMuItem, DanMuTrack>();
public DanMuControl()
{
InitializeComponent();
_danMuPool = new DanMuPool(this);
_dispatcher = new DanMuDispatcher(Dispatcher);
_queueProcessor = new DanMuQueueProcessor(_dispatcher);
_rateLimiter = new RateLimiter(50); // 限制每秒50条
_performanceMonitor = new DanMuPerformanceMonitor();
Loaded += OnLoaded;
Unloaded += OnUnloaded;
SizeChanged += OnSizeChanged;
}
#region Dependency Properties
public static readonly DependencyProperty LineCountProperty = DependencyProperty.Register(
"LineCount", typeof(int), typeof(DanMuControl), new PropertyMetadata(5, OnLineCountChanged));
public int LineCount
{
get => (int)GetValue(LineCountProperty);
set => SetValue(LineCountProperty, value);
}
public static readonly DependencyProperty IsPausedProperty = DependencyProperty.Register(
"IsPaused", typeof(bool), typeof(DanMuControl), new PropertyMetadata(false));
public bool IsPaused
{
get => (bool)GetValue(IsPausedProperty);
set => SetValue(IsPausedProperty, value);
}
#endregion
#region Events
public event EventHandler<DanMuEventArgs> DanMuClick;
public event EventHandler<DanMuEventArgs> DanMuAdded;
public event EventHandler<DanMuEventArgs> DanMuRemoved;
#endregion
#region Public Methods
public void AddDanMu(DanMuMessage message)
{
if (!_rateLimiter.TryAcquire())
{
// 超出速率限制,可以记录日志或采取其他措施
return;
}
_queueProcessor.Enqueue(() =>
{
try
{
if (DanMuFilter.ShouldFilter(message))
return;
var args = new DanMuEventArgs(message);
DanMuAdded?.Invoke(this, args);
if (!args.Handled)
{
_danMuPool.AddDanMu(message);
}
_performanceMonitor.RecordAddedDanMu();
}
catch (Exception ex)
{
// 记录错误日志
_performanceMonitor.RecordError(ex);
}
});
}
public void AddDanMuBatch(IEnumerable<DanMuMessage> messages)
{
_queueProcessor.EnqueueBatch(() =>
{
foreach (var message in messages)
{
if (!_rateLimiter.TryAcquire())
continue;
try
{
if (DanMuFilter.ShouldFilter(message))
continue;
var args = new DanMuEventArgs(message);
DanMuAdded?.Invoke(this, args);
if (!args.Handled)
{
_danMuPool.AddDanMu(message);
}
_performanceMonitor.RecordAddedDanMu();
}
catch (Exception ex)
{
_performanceMonitor.RecordError(ex);
}
}
});
}
public void ClearAll()
{
_queueProcessor.Enqueue(() =>
{
_danMuPool.ClearAll();
_performanceMonitor.RecordClear();
});
}
#endregion
#region Private Methods
private void OnLoaded(object sender, RoutedEventArgs e)
{
InitializeTracks();
_danMuPool.Start();
_performanceMonitor.Start();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
_danMuPool.Stop();
_performanceMonitor.Stop();
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
InitializeTracks();
}
private void InitializeTracks()
{
_tracks.Clear();
double trackHeight = ActualHeight / LineCount;
for (int i = 0; i < LineCount; i++)
{
_tracks.Add(new DanMuTrack
{
Top = i * trackHeight,
Height = trackHeight,
Width = ActualWidth
});
}
}
private static void OnLineCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is DanMuControl control)
{
control.InitializeTracks();
}
}
internal void OnDanMuItemClick(DanMuItem item)
{
var args = new DanMuEventArgs(item.Message);
DanMuClick?.Invoke(this, args);
}
internal void OnDanMuItemRemoved(DanMuItem item)
{
var args = new DanMuEventArgs(item.Message);
DanMuRemoved?.Invoke(this, args);
}
#endregion
internal class DanMuTrack
{
public double Top { get; set; }
public double Height { get; set; }
public double Width { get; set; }
public double AvailablePosition { get; set; }
public List<DanMuItem> ActiveItems { get; } = new List<DanMuItem>();
}
}
}