mirror of
https://github.com/2dust/v2rayN.git
synced 2025-10-13 11:59:13 +00:00
Compare commits
44 commits
c88bf796f4
...
4bf86665b7
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4bf86665b7 | ||
![]() |
ae46b39110 | ||
![]() |
836217266e | ||
![]() |
4e3c2991f9 | ||
![]() |
cdab7a097f | ||
![]() |
1d8b8491ea | ||
![]() |
04dae54654 | ||
![]() |
63bf68376f | ||
![]() |
826f9f833a | ||
![]() |
9317c73084 | ||
![]() |
2d5a5465df | ||
![]() |
af62d9e0f4 | ||
![]() |
2ab044b4fc | ||
![]() |
c76d125ce1 | ||
![]() |
183be53153 | ||
![]() |
52d1eb1e2b | ||
![]() |
521dca33d5 | ||
![]() |
a323484ee3 | ||
![]() |
9a43003c47 | ||
![]() |
e97b98f4bf | ||
![]() |
bd3a733057 | ||
![]() |
0cde086448 | ||
![]() |
8561311a45 | ||
![]() |
57fd56fc05 | ||
![]() |
5d6c5da9d9 | ||
![]() |
ade2db3903 | ||
![]() |
7f07279a4c | ||
![]() |
b25d4d57bd | ||
![]() |
46edd8f9a4 | ||
![]() |
ebb95b5ee8 | ||
![]() |
dc4611a258 | ||
![]() |
03d5b7a05b | ||
![]() |
a652fd879b | ||
![]() |
326bf334e7 | ||
![]() |
21a773f400 | ||
![]() |
d86003df55 | ||
![]() |
faff8e4ea2 | ||
![]() |
6b85aa0b03 | ||
![]() |
671678724b | ||
![]() |
e96a4818c4 | ||
![]() |
0377e7ce19 | ||
![]() |
6929886b3e | ||
![]() |
721d70c8c7 | ||
![]() |
27b45aee83 |
44 changed files with 690 additions and 464 deletions
2
.github/workflows/build-linux.yml
vendored
2
.github/workflows/build-linux.yml
vendored
|
@ -22,7 +22,7 @@ jobs:
|
|||
matrix:
|
||||
configuration: [Release]
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>7.14.12</Version>
|
||||
<Version>7.15.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
|
|
@ -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,8 +20,9 @@
|
|||
<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="Splat.NLog" Version="16.2.1" />
|
||||
<PackageVersion Include="NLog" Version="6.0.4" />
|
||||
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
||||
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
||||
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
||||
|
|
|
@ -12,7 +12,6 @@ public enum EViewAction
|
|||
ProfilesFocus,
|
||||
ShareSub,
|
||||
ShareServer,
|
||||
ShowHideWindow,
|
||||
ScanScreenTask,
|
||||
ScanImageTask,
|
||||
BrowseServer,
|
||||
|
|
32
v2rayN/ServiceLib/Events/AppEvents.cs
Normal file
32
v2rayN/ServiceLib/Events/AppEvents.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using System.Reactive;
|
||||
|
||||
namespace ServiceLib.Events;
|
||||
|
||||
public static class AppEvents
|
||||
{
|
||||
public static readonly EventChannel<Unit> ReloadRequested = new();
|
||||
public static readonly EventChannel<bool?> ShowHideWindowRequested = new();
|
||||
public static readonly EventChannel<Unit> AddServerViaScanRequested = new();
|
||||
public static readonly EventChannel<Unit> AddServerViaClipboardRequested = new();
|
||||
public static readonly EventChannel<bool> SubscriptionsUpdateRequested = new();
|
||||
|
||||
public static readonly EventChannel<Unit> ProfilesRefreshRequested = new();
|
||||
public static readonly EventChannel<Unit> SubscriptionsRefreshRequested = new();
|
||||
public static readonly EventChannel<Unit> ProxiesReloadRequested = new();
|
||||
public static readonly EventChannel<ServerSpeedItem> DispatcherStatisticsRequested = new();
|
||||
|
||||
public static readonly EventChannel<string> SendSnackMsgRequested = new();
|
||||
public static readonly EventChannel<string> SendMsgViewRequested = new();
|
||||
|
||||
public static readonly EventChannel<Unit> AppExitRequested = new();
|
||||
public static readonly EventChannel<bool> ShutdownRequested = new();
|
||||
|
||||
public static readonly EventChannel<Unit> AdjustMainLvColWidthRequested = new();
|
||||
|
||||
public static readonly EventChannel<string> SetDefaultServerRequested = new();
|
||||
|
||||
public static readonly EventChannel<Unit> RoutingsMenuRefreshRequested = new();
|
||||
public static readonly EventChannel<Unit> TestServerRequested = new();
|
||||
public static readonly EventChannel<Unit> InboundDisplayRequested = new();
|
||||
public static readonly EventChannel<ESysProxyType> SysProxyChangeRequested = new();
|
||||
}
|
29
v2rayN/ServiceLib/Events/EventChannel.cs
Normal file
29
v2rayN/ServiceLib/Events/EventChannel.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace ServiceLib.Events;
|
||||
|
||||
public sealed class EventChannel<T>
|
||||
{
|
||||
private readonly ISubject<T> _subject = Subject.Synchronize(new Subject<T>());
|
||||
|
||||
public IObservable<T> AsObservable()
|
||||
{
|
||||
return _subject.AsObservable();
|
||||
}
|
||||
|
||||
public void Publish(T value)
|
||||
{
|
||||
_subject.OnNext(value);
|
||||
}
|
||||
|
||||
public void Publish()
|
||||
{
|
||||
if (typeof(T) != typeof(Unit))
|
||||
{
|
||||
throw new InvalidOperationException("Publish() without value is only valid for EventChannel<Unit>.");
|
||||
}
|
||||
_subject.OnNext((T)(object)Unit.Default);
|
||||
}
|
||||
}
|
|
@ -450,6 +450,14 @@ public class Global
|
|||
"none"
|
||||
];
|
||||
|
||||
public static readonly Dictionary<string, string> LogLevelColors = new()
|
||||
{
|
||||
{ "debug", "#6C757D" },
|
||||
{ "info", "#2ECC71" },
|
||||
{ "warning", "#FFA500" },
|
||||
{ "error", "#E74C3C" },
|
||||
};
|
||||
|
||||
public static readonly List<string> InboundTags =
|
||||
[
|
||||
"socks",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
global using ServiceLib.Base;
|
||||
global using ServiceLib.Common;
|
||||
global using ServiceLib.Enums;
|
||||
global using ServiceLib.Events;
|
||||
global using ServiceLib.Handler;
|
||||
global using ServiceLib.Helper;
|
||||
global using ServiceLib.Manager;
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
using System.Reactive;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
|
||||
public static class AppEvents
|
||||
{
|
||||
public static readonly Subject<Unit> ProfilesRefreshRequested = new();
|
||||
|
||||
public static readonly Subject<string> SendSnackMsgRequested = new();
|
||||
|
||||
public static readonly Subject<string> SendMsgViewRequested = new();
|
||||
|
||||
public static readonly Subject<Unit> AppExitRequested = new();
|
||||
|
||||
public static readonly Subject<bool> ShutdownRequested = new();
|
||||
|
||||
public static readonly Subject<Unit> AdjustMainLvColWidthRequested = new();
|
||||
|
||||
public static readonly Subject<ServerSpeedItem> DispatcherStatisticsRequested = new();
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
using System.Reactive;
|
||||
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public sealed class AppManager
|
||||
|
@ -97,7 +95,7 @@ public sealed class AppManager
|
|||
Logging.SaveLog("AppExitAsync Begin");
|
||||
|
||||
await SysProxyHandler.UpdateSysProxy(_config, true);
|
||||
AppEvents.AppExitRequested.OnNext(Unit.Default);
|
||||
AppEvents.AppExitRequested.Publish();
|
||||
await Task.Delay(50); //Wait for AppExitRequested to be processed
|
||||
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
|
@ -121,7 +119,13 @@ public sealed class AppManager
|
|||
|
||||
public void Shutdown(bool byUser)
|
||||
{
|
||||
AppEvents.ShutdownRequested.OnNext(byUser);
|
||||
AppEvents.ShutdownRequested.Publish(byUser);
|
||||
}
|
||||
|
||||
public async Task RebootAsAdmin()
|
||||
{
|
||||
ProcUtils.RebootAsAdmin();
|
||||
await AppManager.Instance.AppExitAsync(true);
|
||||
}
|
||||
|
||||
#endregion App
|
||||
|
|
|
@ -11,7 +11,7 @@ public class NoticeManager
|
|||
{
|
||||
return;
|
||||
}
|
||||
AppEvents.SendSnackMsgRequested.OnNext(content);
|
||||
AppEvents.SendSnackMsgRequested.Publish(content);
|
||||
}
|
||||
|
||||
public void SendMessage(string? content)
|
||||
|
@ -20,7 +20,7 @@ public class NoticeManager
|
|||
{
|
||||
return;
|
||||
}
|
||||
AppEvents.SendMsgViewRequested.OnNext(content);
|
||||
AppEvents.SendMsgViewRequested.Publish(content);
|
||||
}
|
||||
|
||||
public void SendMessageEx(string? content)
|
||||
|
|
|
@ -109,6 +109,42 @@ public class ProfileItem : ReactiveObject
|
|||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> HasCycle(HashSet<string> visited, HashSet<string> stack)
|
||||
{
|
||||
if (ConfigType < EConfigType.Group)
|
||||
return false;
|
||||
|
||||
if (stack.Contains(IndexId))
|
||||
return true;
|
||||
|
||||
if (visited.Contains(IndexId))
|
||||
return false;
|
||||
|
||||
visited.Add(IndexId);
|
||||
stack.Add(IndexId);
|
||||
|
||||
if (ProfileGroupItemManager.Instance.TryGet(IndexId, out var group)
|
||||
&& !group.ChildItems.IsNullOrEmpty())
|
||||
{
|
||||
var childProfiles = (await Task.WhenAll(
|
||||
Utils.String2List(group.ChildItems)
|
||||
.Where(p => !p.IsNullOrEmpty())
|
||||
.Select(AppManager.Instance.GetProfileItem)
|
||||
))
|
||||
.Where(p => p != null)
|
||||
.ToList();
|
||||
|
||||
foreach (var child in childProfiles)
|
||||
{
|
||||
if (await child.HasCycle(visited, stack))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
stack.Remove(IndexId);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion function
|
||||
|
||||
[PrimaryKey]
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -11,7 +11,7 @@
|
|||
</PackageReference>
|
||||
<PackageReference Include="ReactiveUI.Fody" />
|
||||
<PackageReference Include="sqlite-net-pcl" />
|
||||
<PackageReference Include="Splat.NLog" />
|
||||
<PackageReference Include="NLog" />
|
||||
<PackageReference Include="WebDav.Client" />
|
||||
<PackageReference Include="YamlDotNet" />
|
||||
<PackageReference Include="QRCoder" />
|
||||
|
|
|
@ -79,6 +79,7 @@ public class CoreConfigClashService
|
|||
|
||||
//external-controller
|
||||
fileContent["external-controller"] = $"{Global.Loopback}:{AppManager.Instance.StatePort2}";
|
||||
fileContent.Remove("secret");
|
||||
//allow-lan
|
||||
if (_config.Inbound.First().AllowLANConn)
|
||||
{
|
||||
|
|
|
@ -217,9 +217,15 @@ public partial class CoreConfigSingboxService
|
|||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var hasCycle = await node.HasCycle(new HashSet<string>(), new HashSet<string>());
|
||||
if (hasCycle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// remove custom nodes
|
||||
// remove group nodes for proxy chain
|
||||
// avoid self-reference
|
||||
var childProfiles = (await Task.WhenAll(
|
||||
Utils.String2List(profileGroupItem.ChildItems)
|
||||
.Where(p => !p.IsNullOrEmpty())
|
||||
|
@ -230,7 +236,6 @@ public partial class CoreConfigSingboxService
|
|||
&& p.IsValid()
|
||||
&& p.ConfigType != EConfigType.Custom
|
||||
&& (node.ConfigType == EConfigType.PolicyGroup || p.ConfigType < EConfigType.Group)
|
||||
&& p.IndexId != node.IndexId
|
||||
)
|
||||
.ToList();
|
||||
|
||||
|
|
|
@ -493,6 +493,13 @@ public partial class CoreConfigV2rayService
|
|||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var hasCycle = await node.HasCycle(new HashSet<string>(), new HashSet<string>());
|
||||
if (hasCycle)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// remove custom nodes
|
||||
// remove group nodes for proxy chain
|
||||
var childProfiles = (await Task.WhenAll(
|
||||
|
|
|
@ -2,11 +2,9 @@ using System.Reactive;
|
|||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using DynamicData;
|
||||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
|
@ -225,11 +223,11 @@ public class CheckUpdateViewModel : MyReactiveObject
|
|||
{
|
||||
if (blReload)
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
AppEvents.ReloadRequested.Publish();
|
||||
}
|
||||
else
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.CloseCore();
|
||||
await CoreManager.Instance.CoreStop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -340,6 +338,6 @@ public class CheckUpdateViewModel : MyReactiveObject
|
|||
{
|
||||
return;
|
||||
}
|
||||
found.Remarks = model.Remarks;
|
||||
found.Remarks = model.Remarks;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,8 @@ public class ClashProxiesViewModel : MyReactiveObject
|
|||
SortingSelected = _config.ClashUIItem.ProxiesSorting;
|
||||
RuleModeSelected = (int)_config.ClashUIItem.RuleMode;
|
||||
|
||||
#region WhenAnyValue && ReactiveCommand
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.SelectedGroup,
|
||||
y => y != null && y.Name.IsNotEmpty())
|
||||
|
@ -89,6 +91,17 @@ public class ClashProxiesViewModel : MyReactiveObject
|
|||
y => y == true)
|
||||
.Subscribe(c => { _config.ClashUIItem.ProxiesAutoRefresh = AutoRefresh; });
|
||||
|
||||
#endregion WhenAnyValue && ReactiveCommand
|
||||
|
||||
#region AppEvents
|
||||
|
||||
AppEvents.ProxiesReloadRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async _ => await ProxiesReload());
|
||||
|
||||
#endregion AppEvents
|
||||
|
||||
_ = Init();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
using System.Reactive;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
|
@ -194,7 +194,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
});
|
||||
RebootAsAdminCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await RebootAsAdmin();
|
||||
await AppManager.Instance.RebootAsAdmin();
|
||||
});
|
||||
ClearServerStatisticsCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
|
@ -227,6 +227,30 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
|
||||
#endregion WhenAnyValue && ReactiveCommand
|
||||
|
||||
#region AppEvents
|
||||
|
||||
AppEvents.ReloadRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async _ => await Reload());
|
||||
|
||||
AppEvents.AddServerViaScanRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async _ => await AddServerViaScanAsync());
|
||||
|
||||
AppEvents.AddServerViaClipboardRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async _ => await AddServerViaClipboardAsync(null));
|
||||
|
||||
AppEvents.SubscriptionsUpdateRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async blProxy => await UpdateSubscriptionProcess("", blProxy));
|
||||
|
||||
#endregion AppEvents
|
||||
|
||||
_ = Init();
|
||||
}
|
||||
|
||||
|
@ -234,7 +258,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
{
|
||||
_config.UiItem.ShowInTaskbar = true;
|
||||
|
||||
await ConfigHandler.InitBuiltinRouting(_config);
|
||||
//await ConfigHandler.InitBuiltinRouting(_config);
|
||||
await ConfigHandler.InitBuiltinDNS(_config);
|
||||
await ConfigHandler.InitBuiltinFullConfigTemplate(_config);
|
||||
await ProfileExManager.Instance.Init();
|
||||
|
@ -251,7 +275,6 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
BlReloadEnabled = true;
|
||||
await Reload();
|
||||
await AutoHideStartup();
|
||||
Locator.Current.GetService<StatusBarViewModel>()?.RefreshRoutingsMenu();
|
||||
}
|
||||
|
||||
#endregion Init
|
||||
|
@ -280,7 +303,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
}
|
||||
if (_config.UiItem.EnableAutoAdjustMainLvColWidth)
|
||||
{
|
||||
AppEvents.AdjustMainLvColWidthRequested.OnNext(Unit.Default);
|
||||
AppEvents.AdjustMainLvColWidthRequested.Publish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -291,12 +314,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
{
|
||||
return;
|
||||
}
|
||||
AppEvents.DispatcherStatisticsRequested.OnNext(update);
|
||||
}
|
||||
|
||||
public void ShowHideWindow(bool? blShow)
|
||||
{
|
||||
_updateView?.Invoke(EViewAction.ShowHideWindow, blShow);
|
||||
AppEvents.DispatcherStatisticsRequested.Publish(update);
|
||||
}
|
||||
|
||||
#endregion Actions
|
||||
|
@ -305,14 +323,14 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
|
||||
private async Task RefreshServers()
|
||||
{
|
||||
AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default);
|
||||
AppEvents.ProfilesRefreshRequested.Publish();
|
||||
|
||||
await Task.Delay(200);
|
||||
}
|
||||
|
||||
private void RefreshSubscriptions()
|
||||
{
|
||||
Locator.Current.GetService<ProfilesViewModel>()?.RefreshSubscriptions();
|
||||
AppEvents.SubscriptionsRefreshRequested.Publish();
|
||||
}
|
||||
|
||||
#endregion Servers && Groups
|
||||
|
@ -448,7 +466,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
var ret = await _updateView?.Invoke(EViewAction.OptionSettingWindow, null);
|
||||
if (ret == true)
|
||||
{
|
||||
Locator.Current.GetService<StatusBarViewModel>()?.InboundDisplayStatus();
|
||||
AppEvents.InboundDisplayRequested.Publish();
|
||||
await Reload();
|
||||
}
|
||||
}
|
||||
|
@ -459,7 +477,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
if (ret == true)
|
||||
{
|
||||
await ConfigHandler.InitBuiltinRouting(_config);
|
||||
Locator.Current.GetService<StatusBarViewModel>()?.RefreshRoutingsMenu();
|
||||
AppEvents.RoutingsMenuRefreshRequested.Publish();
|
||||
await Reload();
|
||||
}
|
||||
}
|
||||
|
@ -482,12 +500,6 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
}
|
||||
}
|
||||
|
||||
public async Task RebootAsAdmin()
|
||||
{
|
||||
ProcUtils.RebootAsAdmin();
|
||||
await AppManager.Instance.AppExitAsync(true);
|
||||
}
|
||||
|
||||
private async Task ClearServerStatistics()
|
||||
{
|
||||
await StatisticsManager.Instance.ClearAllServerStatistics();
|
||||
|
@ -533,9 +545,15 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
await SysProxyHandler.UpdateSysProxy(_config, false);
|
||||
await Task.Delay(1000);
|
||||
});
|
||||
Locator.Current.GetService<StatusBarViewModel>()?.TestServerAvailability();
|
||||
AppEvents.TestServerRequested.Publish();
|
||||
|
||||
RxApp.MainThreadScheduler.Schedule(() => _ = ReloadResult());
|
||||
var showClashUI = _config.IsRunningCore(ECoreType.sing_box);
|
||||
if (showClashUI)
|
||||
{
|
||||
AppEvents.ProxiesReloadRequested.Publish();
|
||||
}
|
||||
|
||||
RxApp.MainThreadScheduler.Schedule(() => ReloadResult(showClashUI));
|
||||
|
||||
BlReloadEnabled = true;
|
||||
if (_hasNextReloadJob)
|
||||
|
@ -545,19 +563,11 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
}
|
||||
}
|
||||
|
||||
public async Task ReloadResult()
|
||||
private void ReloadResult(bool showClashUI)
|
||||
{
|
||||
// BlReloadEnabled = true;
|
||||
//Locator.Current.GetService<StatusBarViewModel>()?.ChangeSystemProxyAsync(_config.systemProxyItem.sysProxyType, false);
|
||||
ShowClashUI = _config.IsRunningCore(ECoreType.sing_box);
|
||||
if (ShowClashUI)
|
||||
{
|
||||
Locator.Current.GetService<ClashProxiesViewModel>()?.ProxiesReload();
|
||||
}
|
||||
else
|
||||
{
|
||||
TabMainSelectedIndex = 0;
|
||||
}
|
||||
ShowClashUI = showClashUI;
|
||||
TabMainSelectedIndex = showClashUI ? TabMainSelectedIndex : 0;
|
||||
}
|
||||
|
||||
private async Task LoadCore()
|
||||
|
@ -566,17 +576,11 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
await CoreManager.Instance.LoadCore(node);
|
||||
}
|
||||
|
||||
public async Task CloseCore()
|
||||
{
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
await CoreManager.Instance.CoreStop();
|
||||
}
|
||||
|
||||
private async Task AutoHideStartup()
|
||||
{
|
||||
if (_config.UiItem.AutoHideStartup)
|
||||
{
|
||||
ShowHideWindow(false);
|
||||
AppEvents.ShowHideWindowRequested.Publish(false);
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
@ -589,7 +593,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||
{
|
||||
await ConfigHandler.ApplyRegionalPreset(_config, type);
|
||||
await ConfigHandler.InitRouting(_config);
|
||||
Locator.Current.GetService<StatusBarViewModel>()?.RefreshRoutingsMenu();
|
||||
AppEvents.RoutingsMenuRefreshRequested.Publish();
|
||||
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
await new UpdateService().UpdateGeoFileAll(_config, UpdateTaskHandler);
|
||||
|
|
|
@ -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; } = 500;
|
||||
|
||||
[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()
|
||||
{
|
||||
|
|
|
@ -6,7 +6,6 @@ using DynamicData;
|
|||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
|
@ -250,11 +249,21 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async _ => await RefreshServersBiz());
|
||||
|
||||
AppEvents.SubscriptionsRefreshRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async _ => await RefreshSubscriptions());
|
||||
|
||||
AppEvents.DispatcherStatisticsRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async result => await UpdateStatistics(result));
|
||||
|
||||
AppEvents.SetDefaultServerRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async indexId => await SetDefaultServer(indexId));
|
||||
|
||||
#endregion AppEvents
|
||||
|
||||
_ = Init();
|
||||
|
@ -276,7 +285,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
|
||||
private void Reload()
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
AppEvents.ReloadRequested.Publish();
|
||||
}
|
||||
|
||||
public async Task SetSpeedTestResult(SpeedTestResult result)
|
||||
|
@ -362,7 +371,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
|
||||
public async Task RefreshServers()
|
||||
{
|
||||
AppEvents.ProfilesRefreshRequested.OnNext(Unit.Default);
|
||||
AppEvents.ProfilesRefreshRequested.Publish();
|
||||
|
||||
await Task.Delay(200);
|
||||
}
|
||||
|
@ -390,7 +399,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
|
||||
}
|
||||
|
||||
public async Task RefreshSubscriptions()
|
||||
private async Task RefreshSubscriptions()
|
||||
{
|
||||
SubItems.Clear();
|
||||
|
||||
|
@ -579,7 +588,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||
await SetDefaultServer(SelectedProfile.IndexId);
|
||||
}
|
||||
|
||||
public async Task SetDefaultServer(string? indexId)
|
||||
private async Task SetDefaultServer(string? indexId)
|
||||
{
|
||||
if (indexId.IsNullOrEmpty())
|
||||
{
|
||||
|
|
|
@ -5,12 +5,14 @@ using System.Text;
|
|||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
using Splat;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class StatusBarViewModel : MyReactiveObject
|
||||
{
|
||||
private static readonly Lazy<StatusBarViewModel> _instance = new(() => new(null));
|
||||
public static StatusBarViewModel Instance => _instance.Value;
|
||||
|
||||
#region ObservableCollection
|
||||
|
||||
public IObservableCollection<RoutingItem> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItem>();
|
||||
|
@ -146,17 +148,17 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
|
||||
NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.ShowHideWindow(null);
|
||||
AppEvents.ShowHideWindowRequested.Publish(null);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
ShowWindowCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.ShowHideWindow(true);
|
||||
AppEvents.ShowHideWindowRequested.Publish(true);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
HideWindowCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.ShowHideWindow(false);
|
||||
AppEvents.ShowHideWindowRequested.Publish(false);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
|
||||
|
@ -209,6 +211,26 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async result => await UpdateStatistics(result));
|
||||
|
||||
AppEvents.RoutingsMenuRefreshRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async _ => await RefreshRoutingsMenu());
|
||||
|
||||
AppEvents.TestServerRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async _ => await TestServerAvailability());
|
||||
|
||||
AppEvents.InboundDisplayRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async _ => await InboundDisplayStatus());
|
||||
|
||||
AppEvents.SysProxyChangeRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(async result => await SetListenerType(result));
|
||||
|
||||
#endregion AppEvents
|
||||
|
||||
_ = Init();
|
||||
|
@ -216,6 +238,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
|
||||
private async Task Init()
|
||||
{
|
||||
await ConfigHandler.InitBuiltinRouting(_config);
|
||||
await RefreshRoutingsMenu();
|
||||
await InboundDisplayStatus();
|
||||
await ChangeSystemProxyAsync(_config.SystemProxyItem.SysProxyType, true);
|
||||
|
@ -252,23 +275,20 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
|
||||
private async Task AddServerViaClipboard()
|
||||
{
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
if (service != null)
|
||||
await service.AddServerViaClipboardAsync(null);
|
||||
AppEvents.AddServerViaClipboardRequested.Publish();
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
private async Task AddServerViaScan()
|
||||
{
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
if (service != null)
|
||||
await service.AddServerViaScanAsync();
|
||||
AppEvents.AddServerViaScanRequested.Publish();
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
private async Task UpdateSubscriptionProcess(bool blProxy)
|
||||
{
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
if (service != null)
|
||||
await service.UpdateSubscriptionProcess("", blProxy);
|
||||
AppEvents.SubscriptionsUpdateRequested.Publish(blProxy);
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
private async Task RefreshServersBiz()
|
||||
|
@ -329,7 +349,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
{
|
||||
return;
|
||||
}
|
||||
Locator.Current.GetService<ProfilesViewModel>()?.SetDefaultServer(SelectedServer.ID);
|
||||
AppEvents.SetDefaultServerRequested.Publish(SelectedServer.ID);
|
||||
}
|
||||
|
||||
public async Task TestServerAvailability()
|
||||
|
@ -364,7 +384,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
|
||||
#region System proxy and Routings
|
||||
|
||||
public async Task SetListenerType(ESysProxyType type)
|
||||
private async Task SetListenerType(ESysProxyType type)
|
||||
{
|
||||
if (_config.SystemProxyItem.SysProxyType == type)
|
||||
{
|
||||
|
@ -393,7 +413,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
}
|
||||
}
|
||||
|
||||
public async Task RefreshRoutingsMenu()
|
||||
private async Task RefreshRoutingsMenu()
|
||||
{
|
||||
RoutingItems.Clear();
|
||||
|
||||
|
@ -430,7 +450,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
if (await ConfigHandler.SetDefaultRouting(_config, item) == 0)
|
||||
{
|
||||
NoticeManager.Instance.SendMessageEx(ResUI.TipChangeRouting);
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
AppEvents.ReloadRequested.Publish();
|
||||
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
|
||||
}
|
||||
}
|
||||
|
@ -463,7 +483,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
if (Utils.IsWindows())
|
||||
{
|
||||
_config.TunModeItem.EnableTun = false;
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
|
||||
await AppManager.Instance.RebootAsAdmin();
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -477,7 +497,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
}
|
||||
}
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
AppEvents.ReloadRequested.Publish();
|
||||
}
|
||||
|
||||
private bool AllowEnableTun()
|
||||
|
@ -501,7 +521,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||
|
||||
#region UI
|
||||
|
||||
public async Task InboundDisplayStatus()
|
||||
private async Task InboundDisplayStatus()
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.Append($"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}");
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Splat;
|
||||
using v2rayN.Desktop.Common;
|
||||
using v2rayN.Desktop.Views;
|
||||
|
||||
namespace v2rayN.Desktop;
|
||||
|
@ -16,9 +14,7 @@ public partial class App : Application
|
|||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
|
||||
|
||||
var ViewModel = new StatusBarViewModel(null);
|
||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
|
||||
DataContext = ViewModel;
|
||||
DataContext = StatusBarViewModel.Instance;
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
|
@ -57,16 +53,8 @@ public partial class App : Application
|
|||
{
|
||||
if (desktop.MainWindow != null)
|
||||
{
|
||||
var clipboardData = await AvaUtils.GetClipboardData(desktop.MainWindow);
|
||||
if (clipboardData.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
if (service != null)
|
||||
{
|
||||
_ = service.AddServerViaClipboardAsync(clipboardData);
|
||||
}
|
||||
AppEvents.AddServerViaClipboardRequested.Publish();
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
129
v2rayN/v2rayN.Desktop/Common/TextEditorKeywordHighlighter.cs
Normal file
129
v2rayN/v2rayN.Desktop/Common/TextEditorKeywordHighlighter.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
using Avalonia.Media;
|
||||
using AvaloniaEdit;
|
||||
using AvaloniaEdit.Document;
|
||||
using AvaloniaEdit.Rendering;
|
||||
|
||||
namespace v2rayN.Desktop.Common;
|
||||
|
||||
public class KeywordColorizer : DocumentColorizingTransformer
|
||||
{
|
||||
private readonly string[] _keywords;
|
||||
private readonly Dictionary<string, IBrush> _brushMap;
|
||||
|
||||
public KeywordColorizer(IDictionary<string, IBrush> keywordBrushMap)
|
||||
{
|
||||
if (keywordBrushMap == null || keywordBrushMap.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("keywordBrushMap must not be null or empty", nameof(keywordBrushMap));
|
||||
}
|
||||
|
||||
_brushMap = new Dictionary<string, IBrush>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var kvp in keywordBrushMap)
|
||||
{
|
||||
if (string.IsNullOrEmpty(kvp.Key) || kvp.Value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_brushMap.ContainsKey(kvp.Key))
|
||||
{
|
||||
_brushMap[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (_brushMap.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("keywordBrushMap must contain at least one non-empty key with a non-null brush", nameof(keywordBrushMap));
|
||||
}
|
||||
|
||||
_keywords = _brushMap.Keys.ToArray();
|
||||
}
|
||||
|
||||
protected override void ColorizeLine(DocumentLine line)
|
||||
{
|
||||
var text = CurrentContext.Document.GetText(line);
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var kw in _keywords)
|
||||
{
|
||||
if (string.IsNullOrEmpty(kw))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var searchStart = 0;
|
||||
while (true)
|
||||
{
|
||||
var idx = text.IndexOf(kw, searchStart, StringComparison.OrdinalIgnoreCase);
|
||||
if (idx < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var kwEndIndex = idx + kw.Length;
|
||||
if (IsWordCharBefore(text, idx) || IsWordCharAfter(text, kwEndIndex))
|
||||
{
|
||||
searchStart = idx + Math.Max(1, kw.Length);
|
||||
continue;
|
||||
}
|
||||
|
||||
var start = line.Offset + idx;
|
||||
var end = start + kw.Length;
|
||||
|
||||
if (_brushMap.TryGetValue(kw, out var brush) && brush != null)
|
||||
{
|
||||
ChangeLinePart(start, end, element => element.TextRunProperties.SetForegroundBrush(brush));
|
||||
}
|
||||
|
||||
searchStart = idx + Math.Max(1, kw.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsWordCharBefore(string text, int idx)
|
||||
{
|
||||
if (idx <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var c = text[idx - 1];
|
||||
return char.IsLetterOrDigit(c) || c == '_';
|
||||
}
|
||||
|
||||
private static bool IsWordCharAfter(string text, int idx)
|
||||
{
|
||||
if (idx >= text.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var c = text[idx];
|
||||
return char.IsLetterOrDigit(c) || c == '_';
|
||||
}
|
||||
}
|
||||
|
||||
public static class TextEditorKeywordHighlighter
|
||||
{
|
||||
public static void Attach(TextEditor editor, IDictionary<string, IBrush> keywordBrushMap)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(editor);
|
||||
|
||||
if (keywordBrushMap == null || keywordBrushMap.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (editor.TextArea?.TextView?.LineTransformers?.OfType<KeywordColorizer>().Any() == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var colorizer = new KeywordColorizer(keywordBrushMap);
|
||||
editor.TextArea.TextView.LineTransformers.Add(colorizer);
|
||||
editor.TextArea.TextView.InvalidateVisual();
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ global using ServiceLib;
|
|||
global using ServiceLib.Base;
|
||||
global using ServiceLib.Common;
|
||||
global using ServiceLib.Enums;
|
||||
global using ServiceLib.Events;
|
||||
global using ServiceLib.Handler;
|
||||
global using ServiceLib.Manager;
|
||||
global using ServiceLib.Models;
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -3,7 +3,6 @@ using Avalonia.Input;
|
|||
using Avalonia.ReactiveUI;
|
||||
using DynamicData;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
|
@ -13,7 +12,6 @@ public partial class ClashProxiesView : ReactiveUserControl<ClashProxiesViewMode
|
|||
{
|
||||
InitializeComponent();
|
||||
ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
|
||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ClashProxiesViewModel));
|
||||
lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped;
|
||||
this.KeyDown += ClashProxiesView_KeyDown;
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ using Avalonia.Threading;
|
|||
using DialogHostAvalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
using v2rayN.Desktop.Manager;
|
||||
|
@ -20,7 +19,7 @@ namespace v2rayN.Desktop.Views;
|
|||
public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||
{
|
||||
private static Config _config;
|
||||
private WindowNotificationManager? _manager;
|
||||
private readonly WindowNotificationManager? _manager;
|
||||
private CheckUpdateView? _checkUpdateView;
|
||||
private BackupAndRestoreView? _backupAndRestoreView;
|
||||
private bool _blCloseByUser = false;
|
||||
|
@ -40,7 +39,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
menuClose.Click += MenuClose_Click;
|
||||
|
||||
ViewModel = new MainWindowViewModel(UpdateViewHandler);
|
||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
|
||||
|
||||
switch (_config.UiItem.MainGirdOrientation)
|
||||
{
|
||||
|
@ -155,6 +153,12 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(content => Shutdown(content))
|
||||
.DisposeWith(disposables);
|
||||
|
||||
AppEvents.ShowHideWindowRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(blShow => ShowHideWindow(blShow))
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
|
||||
if (Utils.IsWindows())
|
||||
|
@ -228,12 +232,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
case EViewAction.SubSettingWindow:
|
||||
return await new SubSettingWindow().ShowDialog<bool>(this);
|
||||
|
||||
case EViewAction.ShowHideWindow:
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
ShowHideWindow((bool?)obj),
|
||||
DispatcherPriority.Default);
|
||||
break;
|
||||
|
||||
case EViewAction.ScanScreenTask:
|
||||
await ScanScreenTaskAsync();
|
||||
break;
|
||||
|
@ -243,11 +241,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
break;
|
||||
|
||||
case EViewAction.AddServerViaClipboard:
|
||||
var clipboardData = await AvaUtils.GetClipboardData(this);
|
||||
if (clipboardData.IsNotEmpty() && ViewModel != null)
|
||||
{
|
||||
await ViewModel.AddServerViaClipboardAsync(clipboardData);
|
||||
}
|
||||
await AddServerViaClipboardAsync();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -266,7 +260,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
case EGlobalHotkey.SystemProxySet:
|
||||
case EGlobalHotkey.SystemProxyUnchanged:
|
||||
case EGlobalHotkey.SystemProxyPac:
|
||||
Locator.Current.GetService<StatusBarViewModel>()?.SetListenerType((ESysProxyType)((int)e - 1));
|
||||
AppEvents.SysProxyChangeRequested.Publish((ESysProxyType)((int)e - 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -302,11 +296,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
switch (e.Key)
|
||||
{
|
||||
case Key.V:
|
||||
var clipboardData = await AvaUtils.GetClipboardData(this);
|
||||
if (clipboardData.IsNotEmpty() && ViewModel != null)
|
||||
{
|
||||
await ViewModel.AddServerViaClipboardAsync(clipboardData);
|
||||
}
|
||||
await AddServerViaClipboardAsync();
|
||||
break;
|
||||
|
||||
case Key.S:
|
||||
|
@ -333,6 +323,15 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||
ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
|
||||
}
|
||||
|
||||
public async Task AddServerViaClipboardAsync()
|
||||
{
|
||||
var clipboardData = await AvaUtils.GetClipboardData(this);
|
||||
if (clipboardData.IsNotEmpty() && ViewModel != null)
|
||||
{
|
||||
await ViewModel.AddServerViaClipboardAsync(clipboardData);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ScanScreenTaskAsync()
|
||||
{
|
||||
//ShowHideWindow(false);
|
||||
|
|
|
@ -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,6 +1,6 @@
|
|||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Threading;
|
||||
using ReactiveUI;
|
||||
|
@ -10,13 +10,12 @@ namespace v2rayN.Desktop.Views;
|
|||
|
||||
public partial class MsgView : ReactiveUserControl<MsgViewModel>
|
||||
{
|
||||
private readonly ScrollViewer _scrollViewer;
|
||||
//private const int KeepLines = 30;
|
||||
|
||||
public MsgView()
|
||||
{
|
||||
InitializeComponent();
|
||||
_scrollViewer = this.FindControl<ScrollViewer>("msgScrollViewer");
|
||||
|
||||
txtMsg.TextArea.TextView.Options.EnableHyperlinks = false;
|
||||
ViewModel = new MsgViewModel(UpdateViewHandler);
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
|
@ -24,6 +23,11 @@ public partial class MsgView : ReactiveUserControl<MsgViewModel>
|
|||
this.Bind(ViewModel, vm => vm.MsgFilter, v => v.cmbMsgFilter.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
||||
});
|
||||
|
||||
TextEditorKeywordHighlighter.Attach(txtMsg, Global.LogLevelColors.ToDictionary(
|
||||
kv => kv.Key,
|
||||
kv => (IBrush)new SolidColorBrush(Color.Parse(kv.Value))
|
||||
));
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
|
@ -34,9 +38,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 +47,37 @@ public partial class MsgView : ReactiveUserControl<MsgViewModel>
|
|||
|
||||
private void ShowMsg(object msg)
|
||||
{
|
||||
txtMsg.Text = msg.ToString();
|
||||
//var lineCount = txtMsg.LineCount;
|
||||
//if (lineCount > ViewModel?.NumMaxMsg)
|
||||
//{
|
||||
// var cutLine = txtMsg.Document.GetLineByNumber(lineCount - KeepLines);
|
||||
// txtMsg.Document.Remove(0, cutLine.Offset);
|
||||
//}
|
||||
if (txtMsg.LineCount > ViewModel?.NumMaxMsg)
|
||||
{
|
||||
ClearMsg();
|
||||
}
|
||||
|
||||
txtMsg.AppendText(msg.ToString());
|
||||
if (togScrollToEnd.IsChecked ?? true)
|
||||
{
|
||||
_scrollViewer?.ScrollToEnd();
|
||||
txtMsg.ScrollToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearMsg()
|
||||
{
|
||||
ViewModel?.ClearMsg();
|
||||
txtMsg.Text = "";
|
||||
txtMsg.Clear();
|
||||
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)
|
||||
|
|
|
@ -3,13 +3,13 @@ using Avalonia;
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.VisualTree;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class ProfilesSelectWindow : ReactiveWindow<ProfilesSelectViewModel>
|
||||
public partial class ProfilesSelectWindow : WindowBase<ProfilesSelectViewModel>
|
||||
{
|
||||
private static Config _config;
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ using Avalonia.Threading;
|
|||
using DialogHostAvalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
@ -48,7 +47,6 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
|||
//}
|
||||
|
||||
ViewModel = new ProfilesViewModel(UpdateViewHandler);
|
||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel));
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.Resources>
|
||||
<sys:Double x:Key="QrcodeWidth">500</sys:Double>
|
||||
<sys:Double x:Key="QrcodeWidth">400</sys:Double>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid Margin="32" RowDefinitions="Auto,Auto">
|
||||
|
|
|
@ -6,7 +6,6 @@ using Avalonia.ReactiveUI;
|
|||
using Avalonia.Threading;
|
||||
using DialogHostAvalonia;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
@ -20,9 +19,8 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
|
|||
InitializeComponent();
|
||||
|
||||
_config = AppManager.Instance.Config;
|
||||
//ViewModel = new StatusBarViewModel(UpdateViewHandler);
|
||||
//Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
|
||||
ViewModel = Locator.Current.GetService<StatusBarViewModel>();
|
||||
|
||||
ViewModel = StatusBarViewModel.Instance;
|
||||
ViewModel?.InitUpdateView(UpdateViewHandler);
|
||||
|
||||
txtRunningServerDisplay.Tapped += TxtRunningServerDisplay_Tapped;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -2,6 +2,7 @@ global using ServiceLib;
|
|||
global using ServiceLib.Base;
|
||||
global using ServiceLib.Common;
|
||||
global using ServiceLib.Enums;
|
||||
global using ServiceLib.Events;
|
||||
global using ServiceLib.Handler;
|
||||
global using ServiceLib.Manager;
|
||||
global using ServiceLib.Models;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System.Reactive.Disposables;
|
||||
using System.Windows.Input;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
||||
|
@ -14,7 +13,6 @@ public partial class ClashProxiesView
|
|||
{
|
||||
InitializeComponent();
|
||||
ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
|
||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ClashProxiesViewModel));
|
||||
lstProxyDetails.PreviewMouseDoubleClick += lstProxyDetails_PreviewMouseDoubleClick;
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
|
|
|
@ -6,10 +6,8 @@ using System.Windows.Controls;
|
|||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
using v2rayN.Manager;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
@ -37,7 +35,6 @@ public partial class MainWindow
|
|||
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
|
||||
|
||||
ViewModel = new MainWindowViewModel(UpdateViewHandler);
|
||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
|
||||
|
||||
switch (_config.UiItem.MainGirdOrientation)
|
||||
{
|
||||
|
@ -148,10 +145,16 @@ public partial class MainWindow
|
|||
.DisposeWith(disposables);
|
||||
|
||||
AppEvents.ShutdownRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(content => Shutdown(content))
|
||||
.DisposeWith(disposables);
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(content => Shutdown(content))
|
||||
.DisposeWith(disposables);
|
||||
|
||||
AppEvents.ShowHideWindowRequested
|
||||
.AsObservable()
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.Subscribe(blShow => ShowHideWindow(blShow))
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
|
||||
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
|
||||
|
@ -217,13 +220,6 @@ public partial class MainWindow
|
|||
case EViewAction.SubSettingWindow:
|
||||
return (new SubSettingWindow().ShowDialog() ?? false);
|
||||
|
||||
case EViewAction.ShowHideWindow:
|
||||
Application.Current?.Dispatcher.Invoke((() =>
|
||||
{
|
||||
ShowHideWindow((bool?)obj);
|
||||
}), DispatcherPriority.Normal);
|
||||
break;
|
||||
|
||||
case EViewAction.ScanScreenTask:
|
||||
await ScanScreenTaskAsync();
|
||||
break;
|
||||
|
@ -233,11 +229,7 @@ public partial class MainWindow
|
|||
break;
|
||||
|
||||
case EViewAction.AddServerViaClipboard:
|
||||
var clipboardData = WindowsUtils.GetClipboardData();
|
||||
if (clipboardData.IsNotEmpty())
|
||||
{
|
||||
ViewModel?.AddServerViaClipboardAsync(clipboardData);
|
||||
}
|
||||
await AddServerViaClipboardAsync();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -256,7 +248,7 @@ public partial class MainWindow
|
|||
case EGlobalHotkey.SystemProxySet:
|
||||
case EGlobalHotkey.SystemProxyUnchanged:
|
||||
case EGlobalHotkey.SystemProxyPac:
|
||||
Locator.Current.GetService<StatusBarViewModel>()?.SetListenerType((ESysProxyType)((int)e - 1));
|
||||
AppEvents.SysProxyChangeRequested.Publish((ESysProxyType)((int)e - 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -290,16 +282,7 @@ public partial class MainWindow
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var clipboardData = WindowsUtils.GetClipboardData();
|
||||
if (clipboardData.IsNotEmpty())
|
||||
{
|
||||
var service = Locator.Current.GetService<MainWindowViewModel>();
|
||||
if (service != null)
|
||||
{
|
||||
_ = service.AddServerViaClipboardAsync(clipboardData);
|
||||
}
|
||||
}
|
||||
AddServerViaClipboardAsync().ContinueWith(_ => { });
|
||||
|
||||
break;
|
||||
|
||||
|
@ -333,6 +316,15 @@ public partial class MainWindow
|
|||
ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
|
||||
}
|
||||
|
||||
public async Task AddServerViaClipboardAsync()
|
||||
{
|
||||
var clipboardData = WindowsUtils.GetClipboardData();
|
||||
if (clipboardData.IsNotEmpty() && ViewModel != null)
|
||||
{
|
||||
await ViewModel.AddServerViaClipboardAsync(clipboardData);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ScanScreenTaskAsync()
|
||||
{
|
||||
ShowHideWindow(false);
|
||||
|
|
|
@ -47,19 +47,22 @@ 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)
|
||||
|
|
|
@ -8,7 +8,6 @@ using System.Windows.Media;
|
|||
using System.Windows.Threading;
|
||||
using MaterialDesignThemes.Wpf;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
using v2rayN.Base;
|
||||
using Point = System.Windows.Point;
|
||||
|
||||
|
@ -42,7 +41,6 @@ public partial class ProfilesView
|
|||
}
|
||||
|
||||
ViewModel = new ProfilesViewModel(UpdateViewHandler);
|
||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel));
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
Style="{StaticResource ViewGlobal}"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<sys:Double x:Key="QrcodeWidth">500</sys:Double>
|
||||
<sys:Double x:Key="QrcodeWidth">400</sys:Double>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid Margin="32">
|
||||
|
|
|
@ -3,7 +3,6 @@ using System.Windows;
|
|||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
using v2rayN.Manager;
|
||||
|
||||
namespace v2rayN.Views;
|
||||
|
@ -16,8 +15,8 @@ public partial class StatusBarView
|
|||
{
|
||||
InitializeComponent();
|
||||
_config = AppManager.Instance.Config;
|
||||
ViewModel = new StatusBarViewModel(UpdateViewHandler);
|
||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
|
||||
ViewModel = StatusBarViewModel.Instance;
|
||||
ViewModel?.InitUpdateView(UpdateViewHandler);
|
||||
|
||||
menuExit.Click += menuExit_Click;
|
||||
txtRunningServerDisplay.PreviewMouseDown += txtRunningInfoDisplay_MouseDoubleClick;
|
||||
|
|
Loading…
Reference in a new issue