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 _tracks = new List(); private readonly Dictionary _activeItems = new Dictionary(); 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 DanMuClick; public event EventHandler DanMuAdded; public event EventHandler 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 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 ActiveItems { get; } = new List(); } } }