From 21a773f40076cb4df47c6d7068560f10c85d1921 Mon Sep 17 00:00:00 2001 From: JieXu Date: Fri, 26 Sep 2025 13:55:35 +0800 Subject: [PATCH] Update MsgView.axaml.cs Plan C (#8035) * Add avaloniaEdit for test * Adjust avaloniaEdit * Optimize and improve message function * Update build-linux.yml * Update MsgView.axaml * Update MsgView.axaml.cs --------- Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com> --- .github/workflows/build-linux.yml | 2 +- v2rayN/Directory.Packages.props | 4 +- v2rayN/ServiceLib/ViewModels/MsgViewModel.cs | 66 +++++++++---------- v2rayN/v2rayN.Desktop/App.axaml | 1 + .../ViewModels/ThemeSettingViewModel.cs | 7 +- v2rayN/v2rayN.Desktop/Views/MsgView.axaml | 62 ++++++++--------- v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs | 35 ++++++---- v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj | 2 + v2rayN/v2rayN/Views/MsgView.xaml.cs | 11 +++- 9 files changed, 107 insertions(+), 83 deletions(-) diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 40e9c953..d5f9b1c5 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -22,7 +22,7 @@ jobs: matrix: configuration: [Release] - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Checkout diff --git a/v2rayN/Directory.Packages.props b/v2rayN/Directory.Packages.props index 24484112..9dcfbe61 100644 --- a/v2rayN/Directory.Packages.props +++ b/v2rayN/Directory.Packages.props @@ -5,6 +5,7 @@ false + @@ -19,6 +20,7 @@ + @@ -27,4 +29,4 @@ - + \ No newline at end of file diff --git a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs index 8fc62dfe..8b3467bd 100644 --- a/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs +++ b/v2rayN/ServiceLib/ViewModels/MsgViewModel.cs @@ -1,5 +1,6 @@ using System.Collections.Concurrent; using System.Reactive.Linq; +using System.Text; using System.Text.RegularExpressions; using ReactiveUI; using ReactiveUI.Fody.Helpers; @@ -9,9 +10,9 @@ namespace ServiceLib.ViewModels; public class MsgViewModel : MyReactiveObject { private readonly ConcurrentQueue _queueMsg = new(); - private readonly int _numMaxMsg = 500; - private bool _lastMsgFilterNotAvailable; - private bool _blLockShow = false; + private volatile bool _lastMsgFilterNotAvailable; + private int _showLock = 0; // 0 = unlocked, 1 = locked + public int NumMaxMsg { get; } = 50; [Reactive] public string MsgFilter { get; set; } @@ -33,46 +34,52 @@ public class MsgViewModel : MyReactiveObject this.WhenAnyValue( x => x.AutoRefresh, y => y == true) - .Subscribe(c => { _config.MsgUIItem.AutoRefresh = AutoRefresh; }); + .Subscribe(c => _config.MsgUIItem.AutoRefresh = AutoRefresh); AppEvents.SendMsgViewRequested .AsObservable() //.ObserveOn(RxApp.MainThreadScheduler) - .Subscribe(async content => await AppendQueueMsg(content)); + .Subscribe(content => _ = AppendQueueMsg(content)); } private async Task AppendQueueMsg(string msg) { - //if (msg == Global.CommandClearMsg) - //{ - // ClearMsg(); - // return; - //} if (AutoRefresh == false) { return; } - _ = EnqueueQueueMsg(msg); - if (_blLockShow) - { - return; - } + EnqueueQueueMsg(msg); + if (!_config.UiItem.ShowInTaskbar) { return; } - _blLockShow = true; + if (Interlocked.CompareExchange(ref _showLock, 1, 0) != 0) + { + return; + } - await Task.Delay(500); - var txt = string.Join("", _queueMsg.ToArray()); - await _updateView?.Invoke(EViewAction.DispatcherShowMsg, txt); + try + { + await Task.Delay(500).ConfigureAwait(false); - _blLockShow = false; + var sb = new StringBuilder(); + while (_queueMsg.TryDequeue(out var line)) + { + sb.Append(line); + } + + await _updateView?.Invoke(EViewAction.DispatcherShowMsg, sb.ToString()); + } + finally + { + Interlocked.Exchange(ref _showLock, 0); + } } - private async Task EnqueueQueueMsg(string msg) + private void EnqueueQueueMsg(string msg) { //filter msg if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable) @@ -91,26 +98,17 @@ public class MsgViewModel : MyReactiveObject } } - //Enqueue - 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() - { - _queueMsg.Clear(); - } + //public void ClearMsg() + //{ + // _queueMsg.Clear(); + //} private void DoMsgFilter() { diff --git a/v2rayN/v2rayN.Desktop/App.axaml b/v2rayN/v2rayN.Desktop/App.axaml index 8b61125f..222ab488 100644 --- a/v2rayN/v2rayN.Desktop/App.axaml +++ b/v2rayN/v2rayN.Desktop/App.axaml @@ -11,6 +11,7 @@ RequestedThemeVariant="Default"> + diff --git a/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs b/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs index 3473cf23..ae12fa07 100644 --- a/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs +++ b/v2rayN/v2rayN.Desktop/ViewModels/ThemeSettingViewModel.cs @@ -5,6 +5,7 @@ using Avalonia.Controls.Notifications; using Avalonia.Controls.Primitives; using Avalonia.Media; using Avalonia.Styling; +using AvaloniaEdit; using ReactiveUI; using ReactiveUI.Fody.Helpers; using Semi.Avalonia; @@ -112,7 +113,8 @@ public class ThemeSettingViewModel : MyReactiveObject x.OfType(), x.OfType(), x.OfType(), - x.OfType() + x.OfType(), + x.OfType() )); style.Add(new Setter() { @@ -153,7 +155,8 @@ public class ThemeSettingViewModel : MyReactiveObject x.OfType(), x.OfType(), x.OfType(), - x.OfType() + x.OfType(), + x.OfType() )); style.Add(new Setter() { diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml index b6ca81a1..2f7ad807 100644 --- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml +++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml @@ -2,6 +2,7 @@ x:Class="v2rayN.Desktop.Views.MsgView" xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:avaloniaEdit="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib" @@ -70,35 +71,36 @@ Theme="{DynamicResource SimpleToggleSwitch}" /> - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs index e2d21101..f7b2f9b9 100644 --- a/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs +++ b/v2rayN/v2rayN.Desktop/Views/MsgView.axaml.cs @@ -1,5 +1,4 @@ using System.Reactive.Disposables; -using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.ReactiveUI; using Avalonia.Threading; @@ -10,13 +9,12 @@ namespace v2rayN.Desktop.Views; public partial class MsgView : ReactiveUserControl { - private readonly ScrollViewer _scrollViewer; + private const int MaxLines = 350; + private const int KeepLines = 320; public MsgView() { InitializeComponent(); - _scrollViewer = this.FindControl("msgScrollViewer"); - ViewModel = new MsgViewModel(UpdateViewHandler); this.WhenActivated(disposables => @@ -34,9 +32,8 @@ public partial class MsgView : ReactiveUserControl if (obj is null) return false; - Dispatcher.UIThread.Post(() => - ShowMsg(obj), - DispatcherPriority.ApplicationIdle); + Dispatcher.UIThread.Post(() => ShowMsg(obj), + DispatcherPriority.ApplicationIdle); break; } return await Task.FromResult(true); @@ -44,23 +41,35 @@ public partial class MsgView : ReactiveUserControl private void ShowMsg(object msg) { - txtMsg.Text = msg.ToString(); + txtMsg.AppendText(msg.ToString()); + + if (txtMsg.Document.LineCount > MaxLines) + { + var lc = txtMsg.Document.LineCount; + var cutLineNumber = lc - KeepLines; + var cutLine = txtMsg.Document.GetLineByNumber(cutLineNumber); + txtMsg.Document.Remove(0, cutLine.Offset); + } + if (togScrollToEnd.IsChecked ?? true) { - _scrollViewer?.ScrollToEnd(); + txtMsg.ScrollToEnd(); } } public void ClearMsg() { - ViewModel?.ClearMsg(); - txtMsg.Text = ""; + txtMsg.Text = string.Empty; + txtMsg.AppendText("----- Message cleared -----\n"); } private void menuMsgViewSelectAll_Click(object? sender, RoutedEventArgs e) { - txtMsg.Focus(); - txtMsg.SelectAll(); + Dispatcher.UIThread.Post(() => + { + txtMsg.TextArea.Focus(); + txtMsg.SelectAll(); + }, DispatcherPriority.Render); } private async void menuMsgViewCopy_Click(object? sender, RoutedEventArgs e) diff --git a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj index 8c274a69..f6ff5735 100644 --- a/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj +++ b/v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj @@ -9,6 +9,7 @@ + true @@ -17,6 +18,7 @@ + true diff --git a/v2rayN/v2rayN/Views/MsgView.xaml.cs b/v2rayN/v2rayN/Views/MsgView.xaml.cs index 3cc1fdfa..af8eb5f4 100644 --- a/v2rayN/v2rayN/Views/MsgView.xaml.cs +++ b/v2rayN/v2rayN/Views/MsgView.xaml.cs @@ -48,18 +48,25 @@ public partial class MsgView private void ShowMsg(object msg) { txtMsg.BeginChange(); - txtMsg.Text = msg.ToString(); + + if (txtMsg.LineCount > ViewModel?.NumMaxMsg) + { + ClearMsg(); + } + + txtMsg.AppendText(msg.ToString()); if (togScrollToEnd.IsChecked ?? true) { txtMsg.ScrollToEnd(); } + txtMsg.EndChange(); } public void ClearMsg() { - ViewModel?.ClearMsg(); txtMsg.Clear(); + txtMsg.AppendText("----- Message cleared -----\n"); } private void menuMsgViewSelectAll_Click(object sender, System.Windows.RoutedEventArgs e)