mirror of
https://github.com/2dust/v2rayN.git
synced 2025-10-14 04:19:12 +00:00
Update MsgView.axaml.cs
This commit is contained in:
parent
1b9d7be553
commit
f9f45d7c53
1 changed files with 66 additions and 125 deletions
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue