Update MsgView.axaml.cs

This commit is contained in:
xujie86 2025-09-25 06:36:59 +08:00 committed by GitHub
parent 1b9d7be553
commit f9f45d7c53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,156 +1,97 @@
using System.Collections.Concurrent; using System.Reactive.Disposables;
using System.Reactive.Linq; using Avalonia.Controls;
using System.Text; using Avalonia.Interactivity;
using System.Text.RegularExpressions; using Avalonia.ReactiveUI;
using Avalonia.Threading;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.Fody.Helpers; using v2rayN.Desktop.Common;
namespace ServiceLib.ViewModels; namespace v2rayN.Desktop.Views;
public class MsgViewModel : MyReactiveObject public partial class MsgView : ReactiveUserControl<MsgViewModel>
{ {
private readonly ConcurrentQueue<string> _queueMsg = new(); private readonly ScrollViewer _scrollViewer;
private readonly int _numMaxMsg = 500; private int _lastShownLength = 0;
private bool _lastMsgFilterNotAvailable;
private bool _blLockShow = false;
private readonly Queue<string> _window = new(); public MsgView()
private readonly int _maxLines = 350;
[Reactive]
public string MsgFilter { get; set; }
[Reactive]
public bool AutoRefresh { get; set; }
public MsgViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
{ {
_config = AppManager.Instance.Config; InitializeComponent();
_updateView = updateView; _scrollViewer = this.FindControl<ScrollViewer>("msgScrollViewer");
MsgFilter = _config.MsgUIItem.MainMsgFilter ?? string.Empty;
AutoRefresh = _config.MsgUIItem.AutoRefresh ?? true;
this.WhenAnyValue(x => x.MsgFilter) ViewModel = new MsgViewModel(UpdateViewHandler);
.Subscribe(_ => DoMsgFilter());
this.WhenAnyValue(x => x.AutoRefresh, y => y == true) this.WhenActivated(disposables =>
.Subscribe(_ => { _config.MsgUIItem.AutoRefresh = AutoRefresh; }); {
this.Bind(ViewModel, vm => vm.MsgFilter, v => v.cmbMsgFilter.Text).DisposeWith(disposables);
AppEvents.SendMsgViewRequested this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
.AsObservable() });
//.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(async content => await AppendQueueMsg(content));
} }
private async Task AppendQueueMsg(string msg) private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{ {
//if (msg == Global.CommandClearMsg) switch (action)
//{
// ClearMsg();
// return;
//}
if (AutoRefresh == false)
{ {
return; case EViewAction.DispatcherShowMsg:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ShowMsg(obj),
DispatcherPriority.ApplicationIdle);
break;
}
return await Task.FromResult(true);
} }
_ = EnqueueQueueMsg(msg); private void ShowMsg(object msg)
{
var newText = msg?.ToString() ?? string.Empty;
if (_blLockShow) if (txtMsg.Text is { } old && newText.Length >= _lastShownLength && newText.AsSpan(0, _lastShownLength).SequenceEqual(old))
{ {
return; var delta = newText.AsSpan(_lastShownLength);
} if (!delta.IsEmpty)
if (!_config.UiItem.ShowInTaskbar) txtMsg.Text += delta.ToString();
{
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 else
{ {
if (sb.Length > 0) txtMsg.Text = newText;
{
await _updateView?.Invoke(EViewAction.DispatcherShowMsg, sb.ToString());
}
} }
_blLockShow = false; _lastShownLength = txtMsg.Text.Length;
}
private async Task EnqueueQueueMsg(string msg) if (togScrollToEnd.IsChecked ?? true)
{ Avalonia.Threading.Dispatcher.UIThread.Post(
//filter msg () => _scrollViewer?.ScrollToEnd(),
if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable) 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() public void ClearMsg()
{ {
_queueMsg.Clear(); ViewModel?.ClearMsg();
_window.Clear(); txtMsg.Text = "";
} }
private void DoMsgFilter() private void menuMsgViewSelectAll_Click(object? sender, RoutedEventArgs e)
{ {
_config.MsgUIItem.MainMsgFilter = MsgFilter; txtMsg.Focus();
_lastMsgFilterNotAvailable = false; 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();
} }
} }