mirror of https://github.com/2dust/v2rayN
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>pull/8042/head
parent
d86003df55
commit
21a773f400
|
@ -22,7 +22,7 @@ jobs:
|
|||
matrix:
|
||||
configuration: [Release]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.6" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.6" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.6" />
|
||||
|
@ -19,6 +20,7 @@
|
|||
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||
<PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" />
|
||||
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.10" />
|
||||
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
|
||||
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.10" />
|
||||
<PackageVersion Include="NLog" Version="6.0.4" />
|
||||
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
||||
|
@ -27,4 +29,4 @@
|
|||
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
|
||||
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
|
@ -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<string> _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()
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
RequestedThemeVariant="Default">
|
||||
<Application.Styles>
|
||||
<semi:SemiTheme />
|
||||
<semi:AvaloniaEditSemiTheme />
|
||||
<StyleInclude Source="Assets/GlobalStyles.axaml" />
|
||||
<StyleInclude Source="avares://Semi.Avalonia.DataGrid/Index.axaml" />
|
||||
<dialogHost:DialogHostStyles />
|
||||
|
|
|
@ -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<ContextMenu>(),
|
||||
x.OfType<DataGridRow>(),
|
||||
x.OfType<ListBoxItem>(),
|
||||
x.OfType<HeaderedContentControl>()
|
||||
x.OfType<HeaderedContentControl>(),
|
||||
x.OfType<TextEditor>()
|
||||
));
|
||||
style.Add(new Setter()
|
||||
{
|
||||
|
@ -153,7 +155,8 @@ public class ThemeSettingViewModel : MyReactiveObject
|
|||
x.OfType<DataGridRow>(),
|
||||
x.OfType<ListBoxItem>(),
|
||||
x.OfType<HeaderedContentControl>(),
|
||||
x.OfType<WindowNotificationManager>()
|
||||
x.OfType<WindowNotificationManager>(),
|
||||
x.OfType<TextEditor>()
|
||||
));
|
||||
style.Add(new Setter()
|
||||
{
|
||||
|
|
|
@ -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}" />
|
||||
</WrapPanel>
|
||||
|
||||
<ScrollViewer x:Name="msgScrollViewer" VerticalScrollBarVisibility="Auto">
|
||||
<SelectableTextBlock
|
||||
Name="txtMsg"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="Wrap">
|
||||
<SelectableTextBlock.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewSelectAll"
|
||||
Click="menuMsgViewSelectAll_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewSelectAll}" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewCopy"
|
||||
Click="menuMsgViewCopy_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewCopy}" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewCopyAll"
|
||||
Click="menuMsgViewCopyAll_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewCopyAll}" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewClear"
|
||||
Click="menuMsgViewClear_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewClear}" />
|
||||
</ContextMenu>
|
||||
</SelectableTextBlock.ContextMenu>
|
||||
</SelectableTextBlock>
|
||||
</ScrollViewer>
|
||||
<avaloniaEdit:TextEditor
|
||||
Name="txtMsg"
|
||||
Margin="{StaticResource Margin8}"
|
||||
IsReadOnly="True"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
WordWrap="True">
|
||||
<avaloniaEdit:TextEditor.Options>
|
||||
<avaloniaEdit:TextEditorOptions AllowScrollBelowDocument="False"/>
|
||||
</avaloniaEdit:TextEditor.Options>
|
||||
<avaloniaEdit:TextEditor.ContextFlyout>
|
||||
<MenuFlyout>
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewSelectAll"
|
||||
Click="menuMsgViewSelectAll_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewSelectAll}" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewCopy"
|
||||
Click="menuMsgViewCopy_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewCopy}" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewCopyAll"
|
||||
Click="menuMsgViewCopyAll_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewCopyAll}" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewClear"
|
||||
Click="menuMsgViewClear_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewClear}" />
|
||||
</MenuFlyout>
|
||||
</avaloniaEdit:TextEditor.ContextFlyout>
|
||||
</avaloniaEdit:TextEditor>
|
||||
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
|
|
|
@ -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<MsgViewModel>
|
||||
{
|
||||
private readonly ScrollViewer _scrollViewer;
|
||||
private const int MaxLines = 350;
|
||||
private const int KeepLines = 320;
|
||||
|
||||
public MsgView()
|
||||
{
|
||||
InitializeComponent();
|
||||
_scrollViewer = this.FindControl<ScrollViewer>("msgScrollViewer");
|
||||
|
||||
ViewModel = new MsgViewModel(UpdateViewHandler);
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
|
@ -34,9 +32,8 @@ public partial class MsgView : ReactiveUserControl<MsgViewModel>
|
|||
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<MsgViewModel>
|
|||
|
||||
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)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia.AvaloniaEdit" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
|
@ -17,6 +18,7 @@
|
|||
<PackageReference Include="Avalonia.ReactiveUI" />
|
||||
<PackageReference Include="MessageBox.Avalonia" />
|
||||
<PackageReference Include="Semi.Avalonia" />
|
||||
<PackageReference Include="Semi.Avalonia.AvaloniaEdit" />
|
||||
<PackageReference Include="Semi.Avalonia.DataGrid">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue