From 1b9d7be5538832afecf4e0c7ead99f62e41c162a Mon Sep 17 00:00:00 2001 From: xujie86 <167618598+xujie86@users.noreply.github.com> Date: Thu, 25 Sep 2025 06:31:37 +0800 Subject: [PATCH] Update MsgView.axaml.cs --- v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs | 192 ++++++++++++------- 1 file changed, 123 insertions(+), 69 deletions(-) diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs index 19c75685..73e1c83b 100644 --- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs @@ -1,102 +1,156 @@ -using System.Reactive.Disposables; -using Avalonia.Controls; -using Avalonia.Interactivity; -using Avalonia.ReactiveUI; -using Avalonia.Threading; +using System.Collections.Concurrent; +using System.Reactive.Linq; +using System.Text; +using System.Text.RegularExpressions; using ReactiveUI; -using v2rayN.Desktop.Common; +using ReactiveUI.Fody.Helpers; -namespace v2rayN.Desktop.Views; +namespace ServiceLib.ViewModels; -public partial class MsgView : ReactiveUserControl +public class MsgViewModel : MyReactiveObject { - private readonly ScrollViewer _scrollViewer; - private int _lastShownLength = 0; + private readonly ConcurrentQueue _queueMsg = new(); + private readonly int _numMaxMsg = 500; + private bool _lastMsgFilterNotAvailable; + private bool _blLockShow = false; - public MsgView() + private readonly Queue _window = new(); + private readonly int _maxLines = 350; + + [Reactive] + public string MsgFilter { get; set; } + + [Reactive] + public bool AutoRefresh { get; set; } + + public MsgViewModel(Func>? updateView) { - InitializeComponent(); - _scrollViewer = this.FindControl("msgScrollViewer"); + _config = AppManager.Instance.Config; + _updateView = updateView; + MsgFilter = _config.MsgUIItem.MainMsgFilter ?? string.Empty; + AutoRefresh = _config.MsgUIItem.AutoRefresh ?? true; - ViewModel = new MsgViewModel(UpdateViewHandler); + this.WhenAnyValue(x => x.MsgFilter) + .Subscribe(_ => DoMsgFilter()); - this.WhenActivated(disposables => - { - this.Bind(ViewModel, vm => vm.MsgFilter, v => v.cmbMsgFilter.Text).DisposeWith(disposables); - this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables); - }); + this.WhenAnyValue(x => x.AutoRefresh, y => y == true) + .Subscribe(_ => { _config.MsgUIItem.AutoRefresh = AutoRefresh; }); + + AppEvents.SendMsgViewRequested + .AsObservable() + //.ObserveOn(RxApp.MainThreadScheduler) + .Subscribe(async content => await AppendQueueMsg(content)); } - private async Task UpdateViewHandler(EViewAction action, object? obj) + private async Task AppendQueueMsg(string msg) { - switch (action) + //if (msg == Global.CommandClearMsg) + //{ + // ClearMsg(); + // return; + //} + if (AutoRefresh == false) { - case EViewAction.DispatcherShowMsg: - if (obj is null) - return false; - - Dispatcher.UIThread.Post(() => - ShowMsg(obj), - DispatcherPriority.ApplicationIdle); - break; + return; } - return await Task.FromResult(true); - } - private void ShowMsg(object msg) - { - var newText = msg?.ToString() ?? string.Empty; + _ = EnqueueQueueMsg(msg); - if (txtMsg.Text is { } old && - newText.Length >= _lastShownLength && - newText.AsSpan(0, _lastShownLength).SequenceEqual(old)) + if (_blLockShow) { - var delta = newText.AsSpan(_lastShownLength); - if (!delta.IsEmpty) - txtMsg.Text += delta.ToString(); + return; + } + if (!_config.UiItem.ShowInTaskbar) + { + return; + } + + _blLockShow = true; + + await Task.Delay(500); + + var sb = new StringBuilder(); + var needRebuild = false; + + while (_queueMsg.TryDequeue(out var line)) + { + _window.Enqueue(line); + if (_window.Count > _maxLines) + { + _window.Dequeue(); + needRebuild = true; + } + if (!needRebuild) + { + sb.Append(line); + } + } + + if (needRebuild) + { + var sbAll = new StringBuilder(); + foreach (var s in _window) + { + sbAll.Append(s); + } + await _updateView?.Invoke(EViewAction.DispatcherShowMsg, sbAll.ToString()); } else { - txtMsg.Text = newText; + if (sb.Length > 0) + { + await _updateView?.Invoke(EViewAction.DispatcherShowMsg, sb.ToString()); + } } - _lastShownLength = txtMsg.Text.Length; + _blLockShow = false; + } - if (togScrollToEnd.IsChecked ?? true) + private async Task EnqueueQueueMsg(string msg) + { + //filter msg + if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable) { - Avalonia.Threading.Dispatcher.UIThread.Post( - () => _scrollViewer?.ScrollToEnd(), - Avalonia.Threading.DispatcherPriority.Render); + try + { + if (!Regex.IsMatch(msg, MsgFilter)) + { + return; + } + } + catch (Exception ex) + { + _queueMsg.Enqueue(ex.Message); + _lastMsgFilterNotAvailable = true; + } } + + // 限制待处理队列规模,避免短时洪峰占用 + if (_queueMsg.Count > _numMaxMsg) + { + for (int k = 0; k < _queueMsg.Count - _numMaxMsg; k++) + { + _queueMsg.TryDequeue(out _); + } + } + + _queueMsg.Enqueue(msg); + if (!msg.EndsWith(Environment.NewLine)) + { + _queueMsg.Enqueue(Environment.NewLine); + } + await Task.CompletedTask; } public void ClearMsg() { - ViewModel?.ClearMsg(); - txtMsg.Text = ""; - _lastShownLength = 0; + _queueMsg.Clear(); + _window.Clear(); } - private void menuMsgViewSelectAll_Click(object? sender, RoutedEventArgs e) + private void DoMsgFilter() { - txtMsg.Focus(); - txtMsg.SelectAll(); - } - - private async void menuMsgViewCopy_Click(object? sender, RoutedEventArgs e) - { - var data = txtMsg.SelectedText.TrimEx(); - await AvaUtils.SetClipboardData(this, data); - } - - private async void menuMsgViewCopyAll_Click(object? sender, RoutedEventArgs e) - { - var data = txtMsg.Text.TrimEx(); - await AvaUtils.SetClipboardData(this, data); - } - - private void menuMsgViewClear_Click(object? sender, RoutedEventArgs e) - { - ClearMsg(); + _config.MsgUIItem.MainMsgFilter = MsgFilter; + _lastMsgFilterNotAvailable = false; } }